Sat Mar 10 01:54:03 2012

Asterisk developer's documentation


app_voicemail_odbcstorage.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 #include "asterisk.h"
00047 
00048 #ifdef IMAP_STORAGE
00049 #include <ctype.h>
00050 #include <signal.h>
00051 #include <pwd.h>
00052 #ifdef USE_SYSTEM_IMAP
00053 #include <imap/c-client.h>
00054 #include <imap/imap4r1.h>
00055 #include <imap/linkage.h>
00056 #elif defined (USE_SYSTEM_CCLIENT)
00057 #include <c-client/c-client.h>
00058 #include <c-client/imap4r1.h>
00059 #include <c-client/linkage.h>
00060 #else
00061 #include "c-client.h"
00062 #include "imap4r1.h"
00063 #include "linkage.h"
00064 #endif
00065 #endif
00066 
00067 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 352643 $")
00068 
00069 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00070 #include <sys/time.h>
00071 #include <sys/stat.h>
00072 #include <sys/mman.h>
00073 #include <time.h>
00074 #include <dirent.h>
00075 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00076 #include <sys/wait.h>
00077 #endif
00078 
00079 #include "asterisk/logger.h"
00080 #include "asterisk/lock.h"
00081 #include "asterisk/file.h"
00082 #include "asterisk/channel.h"
00083 #include "asterisk/pbx.h"
00084 #include "asterisk/config.h"
00085 #include "asterisk/say.h"
00086 #include "asterisk/module.h"
00087 #include "asterisk/adsi.h"
00088 #include "asterisk/app.h"
00089 #include "asterisk/manager.h"
00090 #include "asterisk/dsp.h"
00091 #include "asterisk/localtime.h"
00092 #include "asterisk/cli.h"
00093 #include "asterisk/utils.h"
00094 #include "asterisk/stringfields.h"
00095 #include "asterisk/smdi.h"
00096 #include "asterisk/astobj2.h"
00097 #include "asterisk/event.h"
00098 #include "asterisk/taskprocessor.h"
00099 #include "asterisk/test.h"
00100 
00101 #ifdef ODBC_STORAGE
00102 #include "asterisk/res_odbc.h"
00103 #endif
00104 
00105 #ifdef IMAP_STORAGE
00106 #include "asterisk/threadstorage.h"
00107 #endif
00108 
00109 /*** DOCUMENTATION
00110    <application name="VoiceMail" language="en_US">
00111       <synopsis>
00112          Leave a Voicemail message.
00113       </synopsis>
00114       <syntax>
00115          <parameter name="mailboxs" argsep="&amp;" required="true">
00116             <argument name="mailbox1" argsep="@" required="true">
00117                <argument name="mailbox" required="true" />
00118                <argument name="context" />
00119             </argument>
00120             <argument name="mailbox2" argsep="@" multiple="true">
00121                <argument name="mailbox" required="true" />
00122                <argument name="context" />
00123             </argument>
00124          </parameter>
00125          <parameter name="options">
00126             <optionlist>
00127                <option name="b">
00128                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00129                </option>
00130                <option name="d">
00131                   <argument name="c" />
00132                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00133                   if played during the greeting. Context defaults to the current context.</para>
00134                </option>
00135                <option name="g">
00136                   <argument name="#" required="true" />
00137                   <para>Use the specified amount of gain when recording the voicemail
00138                   message. The units are whole-number decibels (dB). Only works on supported
00139                   technologies, which is DAHDI only.</para>
00140                </option>
00141                <option name="s">
00142                   <para>Skip the playback of instructions for leaving a message to the
00143                   calling party.</para>
00144                </option>
00145                <option name="u">
00146                   <para>Play the <literal>unavailable</literal> greeting.</para>
00147                </option>
00148                <option name="U">
00149                   <para>Mark message as <literal>URGENT</literal>.</para>
00150                </option>
00151                <option name="P">
00152                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00153                </option>
00154             </optionlist>
00155          </parameter>
00156       </syntax>
00157       <description>
00158          <para>This application allows the calling party to leave a message for the specified
00159          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00160          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00161          exist.</para>
00162          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00163          <enumlist>
00164             <enum name="0">
00165                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00166             </enum>
00167             <enum name="*">
00168                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00169             </enum>
00170          </enumlist>
00171          <para>This application will set the following channel variable upon completion:</para>
00172          <variablelist>
00173             <variable name="VMSTATUS">
00174                <para>This indicates the status of the execution of the VoiceMail application.</para>
00175                <value name="SUCCESS" />
00176                <value name="USEREXIT" />
00177                <value name="FAILED" />
00178             </variable>
00179          </variablelist>
00180       </description>
00181       <see-also>
00182          <ref type="application">VoiceMailMain</ref>
00183       </see-also>
00184    </application>
00185    <application name="VoiceMailMain" language="en_US">
00186       <synopsis>
00187          Check Voicemail messages.
00188       </synopsis>
00189       <syntax>
00190          <parameter name="mailbox" required="true" argsep="@">
00191             <argument name="mailbox" />
00192             <argument name="context" />
00193          </parameter>
00194          <parameter name="options">
00195             <optionlist>
00196                <option name="p">
00197                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00198                   the mailbox that is entered by the caller.</para>
00199                </option>
00200                <option name="g">
00201                   <argument name="#" required="true" />
00202                   <para>Use the specified amount of gain when recording a voicemail message.
00203                   The units are whole-number decibels (dB).</para>
00204                </option>
00205                <option name="s">
00206                   <para>Skip checking the passcode for the mailbox.</para>
00207                </option>
00208                <option name="a">
00209                   <argument name="folder" required="true" />
00210                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00211                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00212                   <enumlist>
00213                      <enum name="0"><para>INBOX</para></enum>
00214                      <enum name="1"><para>Old</para></enum>
00215                      <enum name="2"><para>Work</para></enum>
00216                      <enum name="3"><para>Family</para></enum>
00217                      <enum name="4"><para>Friends</para></enum>
00218                      <enum name="5"><para>Cust1</para></enum>
00219                      <enum name="6"><para>Cust2</para></enum>
00220                      <enum name="7"><para>Cust3</para></enum>
00221                      <enum name="8"><para>Cust4</para></enum>
00222                      <enum name="9"><para>Cust5</para></enum>
00223                   </enumlist>
00224                </option>
00225             </optionlist>
00226          </parameter>
00227       </syntax>
00228       <description>
00229          <para>This application allows the calling party to check voicemail messages. A specific
00230          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00231          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00232          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00233          <literal>default</literal> context will be used.</para>
00234          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00235          or Password, and the extension exists:</para>
00236          <enumlist>
00237             <enum name="*">
00238                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00239             </enum>
00240          </enumlist>
00241       </description>
00242       <see-also>
00243          <ref type="application">VoiceMail</ref>
00244       </see-also>
00245    </application>
00246    <application name="MailboxExists" language="en_US">
00247       <synopsis>
00248          Check to see if Voicemail mailbox exists.
00249       </synopsis>
00250       <syntax>
00251          <parameter name="mailbox" required="true" argsep="@">
00252             <argument name="mailbox" required="true" />
00253             <argument name="context" />
00254          </parameter>
00255          <parameter name="options">
00256             <para>None options.</para>
00257          </parameter>
00258       </syntax>
00259       <description>
00260          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00261          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00262          will be used.</para>
00263          <para>This application will set the following channel variable upon completion:</para>
00264          <variablelist>
00265             <variable name="VMBOXEXISTSSTATUS">
00266                <para>This will contain the status of the execution of the MailboxExists application.
00267                Possible values include:</para>
00268                <value name="SUCCESS" />
00269                <value name="FAILED" />
00270             </variable>
00271          </variablelist>
00272       </description>
00273    </application>
00274    <application name="VMAuthenticate" language="en_US">
00275       <synopsis>
00276          Authenticate with Voicemail passwords.
00277       </synopsis>
00278       <syntax>
00279          <parameter name="mailbox" required="true" argsep="@">
00280             <argument name="mailbox" />
00281             <argument name="context" />
00282          </parameter>
00283          <parameter name="options">
00284             <optionlist>
00285                <option name="s">
00286                   <para>Skip playing the initial prompts.</para>
00287                </option>
00288             </optionlist>
00289          </parameter>
00290       </syntax>
00291       <description>
00292          <para>This application behaves the same way as the Authenticate application, but the passwords
00293          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00294          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00295          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00296          mailbox.</para>
00297          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00298          or Password, and the extension exists:</para>
00299          <enumlist>
00300             <enum name="*">
00301                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00302             </enum>
00303          </enumlist>
00304       </description>
00305    </application>
00306    <application name="VMSayName" language="en_US">
00307       <synopsis>
00308          Play the name of a voicemail user
00309       </synopsis>
00310       <syntax>
00311          <parameter name="mailbox" required="true" argsep="@">
00312             <argument name="mailbox" />
00313             <argument name="context" />
00314          </parameter>
00315       </syntax>
00316       <description>
00317          <para>This application will say the recorded name of the voicemail user specified as the
00318          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00319       </description>
00320    </application>
00321    <function name="MAILBOX_EXISTS" language="en_US">
00322       <synopsis>
00323          Tell if a mailbox is configured.
00324       </synopsis>
00325       <syntax argsep="@">
00326          <parameter name="mailbox" required="true" />
00327          <parameter name="context" />
00328       </syntax>
00329       <description>
00330          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00331          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00332          context.</para>
00333       </description>
00334    </function>
00335    <manager name="VoicemailUsersList" language="en_US">
00336       <synopsis>
00337          List All Voicemail User Information.
00338       </synopsis>
00339       <syntax>
00340          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00341       </syntax>
00342       <description>
00343       </description>
00344    </manager>
00345  ***/
00346 
00347 #ifdef IMAP_STORAGE
00348 static char imapserver[48];
00349 static char imapport[8];
00350 static char imapflags[128];
00351 static char imapfolder[64];
00352 static char imapparentfolder[64] = "\0";
00353 static char greetingfolder[64];
00354 static char authuser[32];
00355 static char authpassword[42];
00356 static int imapversion = 1;
00357 
00358 static int expungeonhangup = 1;
00359 static int imapgreetings = 0;
00360 static char delimiter = '\0';
00361 
00362 struct vm_state;
00363 struct ast_vm_user;
00364 
00365 AST_THREADSTORAGE(ts_vmstate);
00366 
00367 /* Forward declarations for IMAP */
00368 static int init_mailstream(struct vm_state *vms, int box);
00369 static void write_file(char *filename, char *buffer, unsigned long len);
00370 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00371 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00372 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00373 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00374 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00375 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00376 static void vmstate_insert(struct vm_state *vms);
00377 static void vmstate_delete(struct vm_state *vms);
00378 static void set_update(MAILSTREAM * stream);
00379 static void init_vm_state(struct vm_state *vms);
00380 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00381 static void get_mailbox_delimiter(MAILSTREAM *stream);
00382 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00383 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00384 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);
00385 static void update_messages_by_imapuser(const char *user, unsigned long number);
00386 static int vm_delete(char *file);
00387 
00388 static int imap_remove_file (char *dir, int msgnum);
00389 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00390 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00391 static void check_quota(struct vm_state *vms, char *mailbox);
00392 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00393 struct vmstate {
00394    struct vm_state *vms;
00395    AST_LIST_ENTRY(vmstate) list;
00396 };
00397 
00398 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00399 
00400 #endif
00401 
00402 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00403 
00404 #define COMMAND_TIMEOUT 5000
00405 /* Don't modify these here; set your umask at runtime instead */
00406 #define  VOICEMAIL_DIR_MODE   0777
00407 #define  VOICEMAIL_FILE_MODE  0666
00408 #define  CHUNKSIZE   65536
00409 
00410 #define VOICEMAIL_CONFIG "voicemail.conf"
00411 #define ASTERISK_USERNAME "asterisk"
00412 
00413 /* Define fast-forward, pause, restart, and reverse keys
00414  * while listening to a voicemail message - these are
00415  * strings, not characters */
00416 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00417 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00418 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00419 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00420 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00421 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00422 
00423 /* Default mail command to mail voicemail. Change it with the
00424  * mailcmd= command in voicemail.conf */
00425 #define SENDMAIL "/usr/sbin/sendmail -t"
00426 
00427 #define INTRO "vm-intro"
00428 
00429 #define MAXMSG 100
00430 #define MAXMSGLIMIT 9999
00431 
00432 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00433 
00434 #define BASELINELEN 72
00435 #define BASEMAXINLINE 256
00436 #ifdef IMAP_STORAGE
00437 #define ENDL "\r\n"
00438 #else
00439 #define ENDL "\n"
00440 #endif
00441 
00442 #define MAX_DATETIME_FORMAT   512
00443 #define MAX_NUM_CID_CONTEXTS 10
00444 
00445 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00446 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00447 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00448 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00449 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00450 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00451 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00452 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00453 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00454 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00455 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00456 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00457 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00458 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00459 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00460 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00461 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00462 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00463 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00464 #define ERROR_LOCK_PATH  -100
00465 #define OPERATOR_EXIT     300
00466 
00467 
00468 enum vm_box {
00469    NEW_FOLDER,
00470    OLD_FOLDER,
00471    WORK_FOLDER,
00472    FAMILY_FOLDER,
00473    FRIENDS_FOLDER,
00474    GREETINGS_FOLDER
00475 };
00476 
00477 enum vm_option_flags {
00478    OPT_SILENT =           (1 << 0),
00479    OPT_BUSY_GREETING =    (1 << 1),
00480    OPT_UNAVAIL_GREETING = (1 << 2),
00481    OPT_RECORDGAIN =       (1 << 3),
00482    OPT_PREPEND_MAILBOX =  (1 << 4),
00483    OPT_AUTOPLAY =         (1 << 6),
00484    OPT_DTMFEXIT =         (1 << 7),
00485    OPT_MESSAGE_Urgent =   (1 << 8),
00486    OPT_MESSAGE_PRIORITY = (1 << 9)
00487 };
00488 
00489 enum vm_option_args {
00490    OPT_ARG_RECORDGAIN = 0,
00491    OPT_ARG_PLAYFOLDER = 1,
00492    OPT_ARG_DTMFEXIT   = 2,
00493    /* This *must* be the last value in this enum! */
00494    OPT_ARG_ARRAY_SIZE = 3,
00495 };
00496 
00497 enum vm_passwordlocation {
00498    OPT_PWLOC_VOICEMAILCONF = 0,
00499    OPT_PWLOC_SPOOLDIR      = 1,
00500    OPT_PWLOC_USERSCONF     = 2,
00501 };
00502 
00503 AST_APP_OPTIONS(vm_app_options, {
00504    AST_APP_OPTION('s', OPT_SILENT),
00505    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00506    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00507    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00508    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00509    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00510    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00511    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00512    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00513 });
00514 
00515 static int load_config(int reload);
00516 #ifdef TEST_FRAMEWORK
00517 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00518 #endif
00519 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00520 
00521 /*! \page vmlang Voicemail Language Syntaxes Supported
00522 
00523    \par Syntaxes supported, not really language codes.
00524    \arg \b en    - English
00525    \arg \b de    - German
00526    \arg \b es    - Spanish
00527    \arg \b fr    - French
00528    \arg \b it    - Italian
00529    \arg \b nl    - Dutch
00530    \arg \b pt    - Portuguese
00531    \arg \b pt_BR - Portuguese (Brazil)
00532    \arg \b gr    - Greek
00533    \arg \b no    - Norwegian
00534    \arg \b se    - Swedish
00535    \arg \b tw    - Chinese (Taiwan)
00536    \arg \b ua - Ukrainian
00537 
00538 German requires the following additional soundfile:
00539 \arg \b 1F  einE (feminine)
00540 
00541 Spanish requires the following additional soundfile:
00542 \arg \b 1M      un (masculine)
00543 
00544 Dutch, Portuguese & Spanish require the following additional soundfiles:
00545 \arg \b vm-INBOXs singular of 'new'
00546 \arg \b vm-Olds      singular of 'old/heard/read'
00547 
00548 NB these are plural:
00549 \arg \b vm-INBOX  nieuwe (nl)
00550 \arg \b vm-Old    oude (nl)
00551 
00552 Polish uses:
00553 \arg \b vm-new-a  'new', feminine singular accusative
00554 \arg \b vm-new-e  'new', feminine plural accusative
00555 \arg \b vm-new-ych   'new', feminine plural genitive
00556 \arg \b vm-old-a  'old', feminine singular accusative
00557 \arg \b vm-old-e  'old', feminine plural accusative
00558 \arg \b vm-old-ych   'old', feminine plural genitive
00559 \arg \b digits/1-a   'one', not always same as 'digits/1'
00560 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00561 
00562 Swedish uses:
00563 \arg \b vm-nytt      singular of 'new'
00564 \arg \b vm-nya    plural of 'new'
00565 \arg \b vm-gammalt   singular of 'old'
00566 \arg \b vm-gamla  plural of 'old'
00567 \arg \b digits/ett   'one', not always same as 'digits/1'
00568 
00569 Norwegian uses:
00570 \arg \b vm-ny     singular of 'new'
00571 \arg \b vm-nye    plural of 'new'
00572 \arg \b vm-gammel singular of 'old'
00573 \arg \b vm-gamle  plural of 'old'
00574 
00575 Dutch also uses:
00576 \arg \b nl-om     'at'?
00577 
00578 Spanish also uses:
00579 \arg \b vm-youhaveno
00580 
00581 Italian requires the following additional soundfile:
00582 
00583 For vm_intro_it:
00584 \arg \b vm-nuovo  new
00585 \arg \b vm-nuovi  new plural
00586 \arg \b vm-vecchio   old
00587 \arg \b vm-vecchi old plural
00588 
00589 Chinese (Taiwan) requires the following additional soundfile:
00590 \arg \b vm-tong      A class-word for call (tong1)
00591 \arg \b vm-ri     A class-word for day (ri4)
00592 \arg \b vm-you    You (ni3)
00593 \arg \b vm-haveno   Have no (mei2 you3)
00594 \arg \b vm-have     Have (you3)
00595 \arg \b vm-listen   To listen (yao4 ting1)
00596 
00597 
00598 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00599 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00600 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00601 
00602 */
00603 
00604 struct baseio {
00605    int iocp;
00606    int iolen;
00607    int linelength;
00608    int ateof;
00609    unsigned char iobuf[BASEMAXINLINE];
00610 };
00611 
00612 /*! Structure for linked list of users 
00613  * Use ast_vm_user_destroy() to free one of these structures. */
00614 struct ast_vm_user {
00615    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00616    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00617    char password[80];               /*!< Secret pin code, numbers only */
00618    char fullname[80];               /*!< Full name, for directory app */
00619    char email[80];                  /*!< E-mail address */
00620    char *emailsubject;              /*!< E-mail subject */
00621    char *emailbody;                 /*!< E-mail body */
00622    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00623    char serveremail[80];            /*!< From: Mail address */
00624    char mailcmd[160];               /*!< Configurable mail command */
00625    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00626    char zonetag[80];                /*!< Time zone */
00627    char locale[20];                 /*!< The locale (for presentation of date/time) */
00628    char callback[80];
00629    char dialout[80];
00630    char uniqueid[80];               /*!< Unique integer identifier */
00631    char exit[80];
00632    char attachfmt[20];              /*!< Attachment format */
00633    unsigned int flags;              /*!< VM_ flags */ 
00634    int saydurationm;
00635    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00636    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00637    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00638    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00639    int passwordlocation;            /*!< Storage location of the password */
00640 #ifdef IMAP_STORAGE
00641    char imapuser[80];               /*!< IMAP server login */
00642    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00643    char imapfolder[64];             /*!< IMAP voicemail folder */
00644    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00645    int imapversion;                 /*!< If configuration changes, use the new values */
00646 #endif
00647    double volgain;                  /*!< Volume gain for voicemails sent via email */
00648    AST_LIST_ENTRY(ast_vm_user) list;
00649 };
00650 
00651 /*! Voicemail time zones */
00652 struct vm_zone {
00653    AST_LIST_ENTRY(vm_zone) list;
00654    char name[80];
00655    char timezone[80];
00656    char msg_format[512];
00657 };
00658 
00659 #define VMSTATE_MAX_MSG_ARRAY 256
00660 
00661 /*! Voicemail mailbox state */
00662 struct vm_state {
00663    char curbox[80];
00664    char username[80];
00665    char context[80];
00666    char curdir[PATH_MAX];
00667    char vmbox[PATH_MAX];
00668    char fn[PATH_MAX];
00669    char intro[PATH_MAX];
00670    int *deleted;
00671    int *heard;
00672    int dh_arraysize; /* used for deleted / heard allocation */
00673    int curmsg;
00674    int lastmsg;
00675    int newmessages;
00676    int oldmessages;
00677    int urgentmessages;
00678    int starting;
00679    int repeats;
00680 #ifdef IMAP_STORAGE
00681    ast_mutex_t lock;
00682    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00683    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00684    MAILSTREAM *mailstream;
00685    int vmArrayIndex;
00686    char imapuser[80];                   /*!< IMAP server login */
00687    char imapfolder[64];                 /*!< IMAP voicemail folder */
00688    int imapversion;
00689    int interactive;
00690    char introfn[PATH_MAX];              /*!< Name of prepended file */
00691    unsigned int quota_limit;
00692    unsigned int quota_usage;
00693    struct vm_state *persist_vms;
00694 #endif
00695 };
00696 
00697 #ifdef ODBC_STORAGE
00698 static char odbc_database[80];
00699 static char odbc_table[80];
00700 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00701 #define DISPOSE(a,b) remove_file(a,b)
00702 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00703 #define EXISTS(a,b,c,d) (message_exists(a,b))
00704 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00705 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00706 #define DELETE(a,b,c,d) (delete_file(a,b))
00707 #else
00708 #ifdef IMAP_STORAGE
00709 #define DISPOSE(a,b) (imap_remove_file(a,b))
00710 #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))
00711 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00712 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00713 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00714 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00715 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00716 #else
00717 #define RETRIEVE(a,b,c,d)
00718 #define DISPOSE(a,b)
00719 #define STORE(a,b,c,d,e,f,g,h,i,j)
00720 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00721 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00722 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00723 #define DELETE(a,b,c,d) (vm_delete(c))
00724 #endif
00725 #endif
00726 
00727 static char VM_SPOOL_DIR[PATH_MAX];
00728 
00729 static char ext_pass_cmd[128];
00730 static char ext_pass_check_cmd[128];
00731 
00732 static int my_umask;
00733 
00734 #define PWDCHANGE_INTERNAL (1 << 1)
00735 #define PWDCHANGE_EXTERNAL (1 << 2)
00736 static int pwdchange = PWDCHANGE_INTERNAL;
00737 
00738 #ifdef ODBC_STORAGE
00739 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00740 #else
00741 # ifdef IMAP_STORAGE
00742 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00743 # else
00744 # define tdesc "Comedian Mail (Voicemail System)"
00745 # endif
00746 #endif
00747 
00748 static char userscontext[AST_MAX_EXTENSION] = "default";
00749 
00750 static char *addesc = "Comedian Mail";
00751 
00752 /* Leave a message */
00753 static char *app = "VoiceMail";
00754 
00755 /* Check mail, control, etc */
00756 static char *app2 = "VoiceMailMain";
00757 
00758 static char *app3 = "MailboxExists";
00759 static char *app4 = "VMAuthenticate";
00760 
00761 static char *sayname_app = "VMSayName";
00762 
00763 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00764 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00765 static char zonetag[80];
00766 static char locale[20];
00767 static int maxsilence;
00768 static int maxmsg;
00769 static int maxdeletedmsg;
00770 static int silencethreshold = 128;
00771 static char serveremail[80];
00772 static char mailcmd[160];  /* Configurable mail cmd */
00773 static char externnotify[160]; 
00774 static struct ast_smdi_interface *smdi_iface = NULL;
00775 static char vmfmts[80];
00776 static double volgain;
00777 static int vmminsecs;
00778 static int vmmaxsecs;
00779 static int maxgreet;
00780 static int skipms;
00781 static int maxlogins;
00782 static int minpassword;
00783 static int passwordlocation;
00784 
00785 /*! Poll mailboxes for changes since there is something external to
00786  *  app_voicemail that may change them. */
00787 static unsigned int poll_mailboxes;
00788 
00789 /*! Polling frequency */
00790 static unsigned int poll_freq;
00791 /*! By default, poll every 30 seconds */
00792 #define DEFAULT_POLL_FREQ 30
00793 
00794 AST_MUTEX_DEFINE_STATIC(poll_lock);
00795 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00796 static pthread_t poll_thread = AST_PTHREADT_NULL;
00797 static unsigned char poll_thread_run;
00798 
00799 /*! Subscription to ... MWI event subscriptions */
00800 static struct ast_event_sub *mwi_sub_sub;
00801 /*! Subscription to ... MWI event un-subscriptions */
00802 static struct ast_event_sub *mwi_unsub_sub;
00803 
00804 /*!
00805  * \brief An MWI subscription
00806  *
00807  * This is so we can keep track of which mailboxes are subscribed to.
00808  * This way, we know which mailboxes to poll when the pollmailboxes
00809  * option is being used.
00810  */
00811 struct mwi_sub {
00812    AST_RWLIST_ENTRY(mwi_sub) entry;
00813    int old_urgent;
00814    int old_new;
00815    int old_old;
00816    uint32_t uniqueid;
00817    char mailbox[1];
00818 };
00819 
00820 struct mwi_sub_task {
00821    const char *mailbox;
00822    const char *context;
00823    uint32_t uniqueid;
00824 };
00825 
00826 static struct ast_taskprocessor *mwi_subscription_tps;
00827 
00828 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00829 
00830 /* custom audio control prompts for voicemail playback */
00831 static char listen_control_forward_key[12];
00832 static char listen_control_reverse_key[12];
00833 static char listen_control_pause_key[12];
00834 static char listen_control_restart_key[12];
00835 static char listen_control_stop_key[12];
00836 
00837 /* custom password sounds */
00838 static char vm_password[80] = "vm-password";
00839 static char vm_newpassword[80] = "vm-newpassword";
00840 static char vm_passchanged[80] = "vm-passchanged";
00841 static char vm_reenterpassword[80] = "vm-reenterpassword";
00842 static char vm_mismatch[80] = "vm-mismatch";
00843 static char vm_invalid_password[80] = "vm-invalid-password";
00844 static char vm_pls_try_again[80] = "vm-pls-try-again";
00845 
00846 /*
00847  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00848  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00849  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00850  * app.c's __ast_play_and_record function
00851  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00852  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00853  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00854  * more effort than either of the other two.
00855  */
00856 static char vm_prepend_timeout[80] = "vm-then-pound";
00857 
00858 static struct ast_flags globalflags = {0};
00859 
00860 static int saydurationminfo;
00861 
00862 static char dialcontext[AST_MAX_CONTEXT] = "";
00863 static char callcontext[AST_MAX_CONTEXT] = "";
00864 static char exitcontext[AST_MAX_CONTEXT] = "";
00865 
00866 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00867 
00868 
00869 static char *emailbody = NULL;
00870 static char *emailsubject = NULL;
00871 static char *pagerbody = NULL;
00872 static char *pagersubject = NULL;
00873 static char fromstring[100];
00874 static char pagerfromstring[100];
00875 static char charset[32] = "ISO-8859-1";
00876 
00877 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00878 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00879 static int adsiver = 1;
00880 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00881 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00882 
00883 /* Forward declarations - generic */
00884 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00885 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);
00886 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00887 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00888          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00889          signed char record_gain, struct vm_state *vms, char *flag);
00890 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00891 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00892 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);
00893 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);
00894 static void apply_options(struct ast_vm_user *vmu, const char *options);
00895 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);
00896 static int is_valid_dtmf(const char *key);
00897 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00898 static int write_password_to_file(const char *secretfn, const char *password);
00899 static const char *substitute_escapes(const char *value);
00900 static void free_user(struct ast_vm_user *vmu);
00901 
00902 struct ao2_container *inprocess_container;
00903 
00904 struct inprocess {
00905    int count;
00906    char *context;
00907    char mailbox[0];
00908 };
00909 
00910 static int inprocess_hash_fn(const void *obj, const int flags)
00911 {
00912    const struct inprocess *i = obj;
00913    return atoi(i->mailbox);
00914 }
00915 
00916 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00917 {
00918    struct inprocess *i = obj, *j = arg;
00919    if (strcmp(i->mailbox, j->mailbox)) {
00920       return 0;
00921    }
00922    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00923 }
00924 
00925 static int inprocess_count(const char *context, const char *mailbox, int delta)
00926 {
00927    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00928    arg->context = arg->mailbox + strlen(mailbox) + 1;
00929    strcpy(arg->mailbox, mailbox); /* SAFE */
00930    strcpy(arg->context, context); /* SAFE */
00931    ao2_lock(inprocess_container);
00932    if ((i = ao2_find(inprocess_container, arg, 0))) {
00933       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00934       ao2_unlock(inprocess_container);
00935       ao2_ref(i, -1);
00936       return ret;
00937    }
00938    if (delta < 0) {
00939       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00940    }
00941    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00942       ao2_unlock(inprocess_container);
00943       return 0;
00944    }
00945    i->context = i->mailbox + strlen(mailbox) + 1;
00946    strcpy(i->mailbox, mailbox); /* SAFE */
00947    strcpy(i->context, context); /* SAFE */
00948    i->count = delta;
00949    ao2_link(inprocess_container, i);
00950    ao2_unlock(inprocess_container);
00951    ao2_ref(i, -1);
00952    return 0;
00953 }
00954 
00955 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00956 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00957 #endif
00958 
00959 /*!
00960  * \brief Strips control and non 7-bit clean characters from input string.
00961  *
00962  * \note To map control and none 7-bit characters to a 7-bit clean characters
00963  *  please use ast_str_encode_mine().
00964  */
00965 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00966 {
00967    char *bufptr = buf;
00968    for (; *input; input++) {
00969       if (*input < 32) {
00970          continue;
00971       }
00972       *bufptr++ = *input;
00973       if (bufptr == buf + buflen - 1) {
00974          break;
00975       }
00976    }
00977    *bufptr = '\0';
00978    return buf;
00979 }
00980 
00981 
00982 /*!
00983  * \brief Sets default voicemail system options to a voicemail user.
00984  *
00985  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00986  * - all the globalflags
00987  * - the saydurationminfo
00988  * - the callcontext
00989  * - the dialcontext
00990  * - the exitcontext
00991  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00992  * - volume gain
00993  * - emailsubject, emailbody set to NULL
00994  */
00995 static void populate_defaults(struct ast_vm_user *vmu)
00996 {
00997    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
00998    vmu->passwordlocation = passwordlocation;
00999    if (saydurationminfo) {
01000       vmu->saydurationm = saydurationminfo;
01001    }
01002    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01003    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01004    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01005    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01006    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01007    if (vmminsecs) {
01008       vmu->minsecs = vmminsecs;
01009    }
01010    if (vmmaxsecs) {
01011       vmu->maxsecs = vmmaxsecs;
01012    }
01013    if (maxmsg) {
01014       vmu->maxmsg = maxmsg;
01015    }
01016    if (maxdeletedmsg) {
01017       vmu->maxdeletedmsg = maxdeletedmsg;
01018    }
01019    vmu->volgain = volgain;
01020    ast_free(vmu->emailsubject);
01021    vmu->emailsubject = NULL;
01022    ast_free(vmu->emailbody);
01023    vmu->emailbody = NULL;
01024 #ifdef IMAP_STORAGE
01025    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01026 #endif
01027 }
01028 
01029 /*!
01030  * \brief Sets a a specific property value.
01031  * \param vmu The voicemail user object to work with.
01032  * \param var The name of the property to be set.
01033  * \param value The value to be set to the property.
01034  * 
01035  * The property name must be one of the understood properties. See the source for details.
01036  */
01037 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01038 {
01039    int x;
01040    if (!strcasecmp(var, "attach")) {
01041       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01042    } else if (!strcasecmp(var, "attachfmt")) {
01043       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01044    } else if (!strcasecmp(var, "serveremail")) {
01045       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01046    } else if (!strcasecmp(var, "emailbody")) {
01047       vmu->emailbody = ast_strdup(substitute_escapes(value));
01048    } else if (!strcasecmp(var, "emailsubject")) {
01049       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01050    } else if (!strcasecmp(var, "language")) {
01051       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01052    } else if (!strcasecmp(var, "tz")) {
01053       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01054    } else if (!strcasecmp(var, "locale")) {
01055       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01056 #ifdef IMAP_STORAGE
01057    } else if (!strcasecmp(var, "imapuser")) {
01058       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01059       vmu->imapversion = imapversion;
01060    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01061       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01062       vmu->imapversion = imapversion;
01063    } else if (!strcasecmp(var, "imapfolder")) {
01064       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01065    } else if (!strcasecmp(var, "imapvmshareid")) {
01066       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01067       vmu->imapversion = imapversion;
01068 #endif
01069    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01070       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01071    } else if (!strcasecmp(var, "saycid")){
01072       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01073    } else if (!strcasecmp(var, "sendvoicemail")){
01074       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01075    } else if (!strcasecmp(var, "review")){
01076       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01077    } else if (!strcasecmp(var, "tempgreetwarn")){
01078       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01079    } else if (!strcasecmp(var, "messagewrap")){
01080       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01081    } else if (!strcasecmp(var, "operator")) {
01082       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01083    } else if (!strcasecmp(var, "envelope")){
01084       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01085    } else if (!strcasecmp(var, "moveheard")){
01086       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01087    } else if (!strcasecmp(var, "sayduration")){
01088       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01089    } else if (!strcasecmp(var, "saydurationm")){
01090       if (sscanf(value, "%30d", &x) == 1) {
01091          vmu->saydurationm = x;
01092       } else {
01093          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01094       }
01095    } else if (!strcasecmp(var, "forcename")){
01096       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01097    } else if (!strcasecmp(var, "forcegreetings")){
01098       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01099    } else if (!strcasecmp(var, "callback")) {
01100       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01101    } else if (!strcasecmp(var, "dialout")) {
01102       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01103    } else if (!strcasecmp(var, "exitcontext")) {
01104       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01105    } else if (!strcasecmp(var, "minsecs")) {
01106       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01107          vmu->minsecs = x;
01108       } else {
01109          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01110          vmu->minsecs = vmminsecs;
01111       }
01112    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01113       vmu->maxsecs = atoi(value);
01114       if (vmu->maxsecs <= 0) {
01115          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01116          vmu->maxsecs = vmmaxsecs;
01117       } else {
01118          vmu->maxsecs = atoi(value);
01119       }
01120       if (!strcasecmp(var, "maxmessage"))
01121          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01122    } else if (!strcasecmp(var, "maxmsg")) {
01123       vmu->maxmsg = atoi(value);
01124       /* Accept maxmsg=0 (Greetings only voicemail) */
01125       if (vmu->maxmsg < 0) {
01126          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01127          vmu->maxmsg = MAXMSG;
01128       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01129          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01130          vmu->maxmsg = MAXMSGLIMIT;
01131       }
01132    } else if (!strcasecmp(var, "nextaftercmd")) {
01133       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01134    } else if (!strcasecmp(var, "backupdeleted")) {
01135       if (sscanf(value, "%30d", &x) == 1)
01136          vmu->maxdeletedmsg = x;
01137       else if (ast_true(value))
01138          vmu->maxdeletedmsg = MAXMSG;
01139       else
01140          vmu->maxdeletedmsg = 0;
01141 
01142       if (vmu->maxdeletedmsg < 0) {
01143          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01144          vmu->maxdeletedmsg = MAXMSG;
01145       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01146          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01147          vmu->maxdeletedmsg = MAXMSGLIMIT;
01148       }
01149    } else if (!strcasecmp(var, "volgain")) {
01150       sscanf(value, "%30lf", &vmu->volgain);
01151    } else if (!strcasecmp(var, "passwordlocation")) {
01152       if (!strcasecmp(value, "spooldir")) {
01153          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01154       } else {
01155          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01156       }
01157    } else if (!strcasecmp(var, "options")) {
01158       apply_options(vmu, value);
01159    }
01160 }
01161 
01162 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01163 {
01164    int fds[2], pid = 0;
01165 
01166    memset(buf, 0, len);
01167 
01168    if (pipe(fds)) {
01169       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01170    } else {
01171       /* good to go*/
01172       pid = ast_safe_fork(0);
01173 
01174       if (pid < 0) {
01175          /* ok maybe not */
01176          close(fds[0]);
01177          close(fds[1]);
01178          snprintf(buf, len, "FAILURE: Fork failed");
01179       } else if (pid) {
01180          /* parent */
01181          close(fds[1]);
01182          if (read(fds[0], buf, len) < 0) {
01183             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01184          }
01185          close(fds[0]);
01186       } else {
01187          /*  child */
01188          AST_DECLARE_APP_ARGS(arg,
01189             AST_APP_ARG(v)[20];
01190          );
01191          char *mycmd = ast_strdupa(command);
01192 
01193          close(fds[0]);
01194          dup2(fds[1], STDOUT_FILENO);
01195          close(fds[1]);
01196          ast_close_fds_above_n(STDOUT_FILENO);
01197 
01198          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01199 
01200          execv(arg.v[0], arg.v); 
01201          printf("FAILURE: %s", strerror(errno));
01202          _exit(0);
01203       }
01204    }
01205    return buf;
01206 }
01207 
01208 /*!
01209  * \brief Check that password meets minimum required length
01210  * \param vmu The voicemail user to change the password for.
01211  * \param password The password string to check
01212  *
01213  * \return zero on ok, 1 on not ok.
01214  */
01215 static int check_password(struct ast_vm_user *vmu, char *password)
01216 {
01217    /* check minimum length */
01218    if (strlen(password) < minpassword)
01219       return 1;
01220    /* check that password does not contain '*' character */
01221    if (!ast_strlen_zero(password) && password[0] == '*')
01222       return 1;
01223    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01224       char cmd[255], buf[255];
01225 
01226       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01227 
01228       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01229       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01230          ast_debug(5, "Result: %s\n", buf);
01231          if (!strncasecmp(buf, "VALID", 5)) {
01232             ast_debug(3, "Passed password check: '%s'\n", buf);
01233             return 0;
01234          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01235             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01236             return 0;
01237          } else {
01238             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01239             return 1;
01240          }
01241       }
01242    }
01243    return 0;
01244 }
01245 
01246 /*! 
01247  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01248  * \param vmu The voicemail user to change the password for.
01249  * \param password The new value to be set to the password for this user.
01250  * 
01251  * This only works if there is a realtime engine configured.
01252  * This is called from the (top level) vm_change_password.
01253  *
01254  * \return zero on success, -1 on error.
01255  */
01256 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01257 {
01258    int res = -1;
01259    if (!strcmp(vmu->password, password)) {
01260       /* No change (but an update would return 0 rows updated, so we opt out here) */
01261       return 0;
01262    }
01263 
01264    if (strlen(password) > 10) {
01265       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01266    }
01267    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01268       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01269       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01270       res = 0;
01271    }
01272    return res;
01273 }
01274 
01275 /*!
01276  * \brief Destructively Parse options and apply.
01277  */
01278 static void apply_options(struct ast_vm_user *vmu, const char *options)
01279 {  
01280    char *stringp;
01281    char *s;
01282    char *var, *value;
01283    stringp = ast_strdupa(options);
01284    while ((s = strsep(&stringp, "|"))) {
01285       value = s;
01286       if ((var = strsep(&value, "=")) && value) {
01287          apply_option(vmu, var, value);
01288       }
01289    }  
01290 }
01291 
01292 /*!
01293  * \brief Loads the options specific to a voicemail user.
01294  * 
01295  * This is called when a vm_user structure is being set up, such as from load_options.
01296  */
01297 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01298 {
01299    for (; var; var = var->next) {
01300       if (!strcasecmp(var->name, "vmsecret")) {
01301          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01302       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01303          if (ast_strlen_zero(retval->password)) {
01304             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01305                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01306                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01307             } else {
01308                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01309             }
01310          }
01311       } else if (!strcasecmp(var->name, "uniqueid")) {
01312          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01313       } else if (!strcasecmp(var->name, "pager")) {
01314          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01315       } else if (!strcasecmp(var->name, "email")) {
01316          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01317       } else if (!strcasecmp(var->name, "fullname")) {
01318          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01319       } else if (!strcasecmp(var->name, "context")) {
01320          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01321       } else if (!strcasecmp(var->name, "emailsubject")) {
01322          ast_free(retval->emailsubject);
01323          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01324       } else if (!strcasecmp(var->name, "emailbody")) {
01325          ast_free(retval->emailbody);
01326          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01327 #ifdef IMAP_STORAGE
01328       } else if (!strcasecmp(var->name, "imapuser")) {
01329          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01330          retval->imapversion = imapversion;
01331       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01332          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01333          retval->imapversion = imapversion;
01334       } else if (!strcasecmp(var->name, "imapfolder")) {
01335          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01336       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01337          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01338          retval->imapversion = imapversion;
01339 #endif
01340       } else
01341          apply_option(retval, var->name, var->value);
01342    }
01343 }
01344 
01345 /*!
01346  * \brief Determines if a DTMF key entered is valid.
01347  * \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.
01348  *
01349  * Tests the character entered against the set of valid DTMF characters. 
01350  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01351  */
01352 static int is_valid_dtmf(const char *key)
01353 {
01354    int i;
01355    char *local_key = ast_strdupa(key);
01356 
01357    for (i = 0; i < strlen(key); ++i) {
01358       if (!strchr(VALID_DTMF, *local_key)) {
01359          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01360          return 0;
01361       }
01362       local_key++;
01363    }
01364    return 1;
01365 }
01366 
01367 /*!
01368  * \brief Finds a voicemail user from the realtime engine.
01369  * \param ivm
01370  * \param context
01371  * \param mailbox
01372  *
01373  * 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.
01374  *
01375  * \return The ast_vm_user structure for the user that was found.
01376  */
01377 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01378 {
01379    struct ast_variable *var;
01380    struct ast_vm_user *retval;
01381 
01382    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01383       if (!ivm)
01384          ast_set_flag(retval, VM_ALLOCED);   
01385       else
01386          memset(retval, 0, sizeof(*retval));
01387       if (mailbox) 
01388          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01389       populate_defaults(retval);
01390       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01391          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01392       else
01393          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01394       if (var) {
01395          apply_options_full(retval, var);
01396          ast_variables_destroy(var);
01397       } else { 
01398          if (!ivm) 
01399             free_user(retval);
01400          retval = NULL;
01401       }  
01402    } 
01403    return retval;
01404 }
01405 
01406 /*!
01407  * \brief Finds a voicemail user from the users file or the realtime engine.
01408  * \param ivm
01409  * \param context
01410  * \param mailbox
01411  * 
01412  * \return The ast_vm_user structure for the user that was found.
01413  */
01414 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01415 {
01416    /* This function could be made to generate one from a database, too */
01417    struct ast_vm_user *vmu = NULL, *cur;
01418    AST_LIST_LOCK(&users);
01419 
01420    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01421       context = "default";
01422 
01423    AST_LIST_TRAVERSE(&users, cur, list) {
01424 #ifdef IMAP_STORAGE
01425       if (cur->imapversion != imapversion) {
01426          continue;
01427       }
01428 #endif
01429       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01430          break;
01431       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01432          break;
01433    }
01434    if (cur) {
01435       /* Make a copy, so that on a reload, we have no race */
01436       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01437          *vmu = *cur;
01438          if (!ivm) {
01439             vmu->emailbody = ast_strdup(cur->emailbody);
01440             vmu->emailsubject = ast_strdup(cur->emailsubject);
01441          }
01442          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01443          AST_LIST_NEXT(vmu, list) = NULL;
01444       }
01445    } else
01446       vmu = find_user_realtime(ivm, context, mailbox);
01447    AST_LIST_UNLOCK(&users);
01448    return vmu;
01449 }
01450 
01451 /*!
01452  * \brief Resets a user password to a specified password.
01453  * \param context
01454  * \param mailbox
01455  * \param newpass
01456  *
01457  * This does the actual change password work, called by the vm_change_password() function.
01458  *
01459  * \return zero on success, -1 on error.
01460  */
01461 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01462 {
01463    /* This function could be made to generate one from a database, too */
01464    struct ast_vm_user *cur;
01465    int res = -1;
01466    AST_LIST_LOCK(&users);
01467    AST_LIST_TRAVERSE(&users, cur, list) {
01468       if ((!context || !strcasecmp(context, cur->context)) &&
01469          (!strcasecmp(mailbox, cur->mailbox)))
01470             break;
01471    }
01472    if (cur) {
01473       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01474       res = 0;
01475    }
01476    AST_LIST_UNLOCK(&users);
01477    return res;
01478 }
01479 
01480 /*! 
01481  * \brief The handler for the change password option.
01482  * \param vmu The voicemail user to work with.
01483  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01484  * 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.
01485  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01486  */
01487 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01488 {
01489    struct ast_config   *cfg = NULL;
01490    struct ast_variable *var = NULL;
01491    struct ast_category *cat = NULL;
01492    char *category = NULL, *value = NULL, *new = NULL;
01493    const char *tmp = NULL;
01494    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01495    char secretfn[PATH_MAX] = "";
01496    int found = 0;
01497 
01498    if (!change_password_realtime(vmu, newpassword))
01499       return;
01500 
01501    /* check if we should store the secret in the spool directory next to the messages */
01502    switch (vmu->passwordlocation) {
01503    case OPT_PWLOC_SPOOLDIR:
01504       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01505       if (write_password_to_file(secretfn, newpassword) == 0) {
01506          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01507          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01508          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01509          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01510          break;
01511       } else {
01512          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01513       }
01514       /* Fall-through */
01515    case OPT_PWLOC_VOICEMAILCONF:
01516       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01517          while ((category = ast_category_browse(cfg, category))) {
01518             if (!strcasecmp(category, vmu->context)) {
01519                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01520                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01521                   break;
01522                }
01523                value = strstr(tmp, ",");
01524                if (!value) {
01525                   new = alloca(strlen(newpassword)+1);
01526                   sprintf(new, "%s", newpassword);
01527                } else {
01528                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01529                   sprintf(new, "%s%s", newpassword, value);
01530                }
01531                if (!(cat = ast_category_get(cfg, category))) {
01532                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01533                   break;
01534                }
01535                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01536                found = 1;
01537             }
01538          }
01539          /* save the results */
01540          if (found) {
01541             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01542             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01543             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01544             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01545             break;
01546          }
01547       }
01548       /* Fall-through */
01549    case OPT_PWLOC_USERSCONF:
01550       /* check users.conf and update the password stored for the mailbox */
01551       /* if no vmsecret entry exists create one. */
01552       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01553          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01554          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01555             ast_debug(4, "users.conf: %s\n", category);
01556             if (!strcasecmp(category, vmu->mailbox)) {
01557                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01558                   ast_debug(3, "looks like we need to make vmsecret!\n");
01559                   var = ast_variable_new("vmsecret", newpassword, "");
01560                } else {
01561                   var = NULL;
01562                }
01563                new = alloca(strlen(newpassword) + 1);
01564                sprintf(new, "%s", newpassword);
01565                if (!(cat = ast_category_get(cfg, category))) {
01566                   ast_debug(4, "failed to get category!\n");
01567                   ast_free(var);
01568                   break;
01569                }
01570                if (!var) {
01571                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01572                } else {
01573                   ast_variable_append(cat, var);
01574                }
01575                found = 1;
01576                break;
01577             }
01578          }
01579          /* save the results and clean things up */
01580          if (found) {
01581             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01582             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01583             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01584             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01585          }
01586       }
01587    }
01588 }
01589 
01590 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01591 {
01592    char buf[255];
01593    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01594    ast_debug(1, "External password: %s\n",buf);
01595    if (!ast_safe_system(buf)) {
01596       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01597       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01598       /* Reset the password in memory, too */
01599       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01600    }
01601 }
01602 
01603 /*! 
01604  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01605  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01606  * \param len The length of the path string that was written out.
01607  * \param context
01608  * \param ext 
01609  * \param folder 
01610  * 
01611  * The path is constructed as 
01612  *    VM_SPOOL_DIRcontext/ext/folder
01613  *
01614  * \return zero on success, -1 on error.
01615  */
01616 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01617 {
01618    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01619 }
01620 
01621 /*! 
01622  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01623  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01624  * \param len The length of the path string that was written out.
01625  * \param dir 
01626  * \param num 
01627  * 
01628  * The path is constructed as 
01629  *    VM_SPOOL_DIRcontext/ext/folder
01630  *
01631  * \return zero on success, -1 on error.
01632  */
01633 static int make_file(char *dest, const int len, const char *dir, const int num)
01634 {
01635    return snprintf(dest, len, "%s/msg%04d", dir, num);
01636 }
01637 
01638 /* same as mkstemp, but return a FILE * */
01639 static FILE *vm_mkftemp(char *template)
01640 {
01641    FILE *p = NULL;
01642    int pfd = mkstemp(template);
01643    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01644    if (pfd > -1) {
01645       p = fdopen(pfd, "w+");
01646       if (!p) {
01647          close(pfd);
01648          pfd = -1;
01649       }
01650    }
01651    return p;
01652 }
01653 
01654 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01655  * \param dest    String. base directory.
01656  * \param len     Length of dest.
01657  * \param context String. Ignored if is null or empty string.
01658  * \param ext     String. Ignored if is null or empty string.
01659  * \param folder  String. Ignored if is null or empty string. 
01660  * \return -1 on failure, 0 on success.
01661  */
01662 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01663 {
01664    mode_t   mode = VOICEMAIL_DIR_MODE;
01665    int res;
01666 
01667    make_dir(dest, len, context, ext, folder);
01668    if ((res = ast_mkdir(dest, mode))) {
01669       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01670       return -1;
01671    }
01672    return 0;
01673 }
01674 
01675 static const char * const mailbox_folders[] = {
01676 #ifdef IMAP_STORAGE
01677    imapfolder,
01678 #else
01679    "INBOX",
01680 #endif
01681    "Old",
01682    "Work",
01683    "Family",
01684    "Friends",
01685    "Cust1",
01686    "Cust2",
01687    "Cust3",
01688    "Cust4",
01689    "Cust5",
01690    "Deleted",
01691    "Urgent",
01692 };
01693 
01694 static const char *mbox(struct ast_vm_user *vmu, int id)
01695 {
01696 #ifdef IMAP_STORAGE
01697    if (vmu && id == 0) {
01698       return vmu->imapfolder;
01699    }
01700 #endif
01701    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01702 }
01703 
01704 static int get_folder_by_name(const char *name)
01705 {
01706    size_t i;
01707 
01708    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01709       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01710          return i;
01711       }
01712    }
01713 
01714    return -1;
01715 }
01716 
01717 static void free_user(struct ast_vm_user *vmu)
01718 {
01719    if (ast_test_flag(vmu, VM_ALLOCED)) {
01720 
01721       ast_free(vmu->emailbody);
01722       vmu->emailbody = NULL;
01723 
01724       ast_free(vmu->emailsubject);
01725       vmu->emailsubject = NULL;
01726 
01727       ast_free(vmu);
01728    }
01729 }
01730 
01731 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01732 
01733    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01734    if (!vms->dh_arraysize) {
01735       /* initial allocation */
01736       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01737          return -1;
01738       }
01739       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01740          return -1;
01741       }
01742       vms->dh_arraysize = arraysize;
01743    } else if (vms->dh_arraysize < arraysize) {
01744       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01745          return -1;
01746       }
01747       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01748          return -1;
01749       }
01750       memset(vms->deleted, 0, arraysize * sizeof(int));
01751       memset(vms->heard, 0, arraysize * sizeof(int));
01752       vms->dh_arraysize = arraysize;
01753    }
01754 
01755    return 0;
01756 }
01757 
01758 /* All IMAP-specific functions should go in this block. This
01759  * keeps them from being spread out all over the code */
01760 #ifdef IMAP_STORAGE
01761 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01762 {
01763    char arg[10];
01764    struct vm_state *vms;
01765    unsigned long messageNum;
01766 
01767    /* If greetings aren't stored in IMAP, just delete the file */
01768    if (msgnum < 0 && !imapgreetings) {
01769       ast_filedelete(file, NULL);
01770       return;
01771    }
01772 
01773    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01774       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);
01775       return;
01776    }
01777 
01778    /* find real message number based on msgnum */
01779    /* this may be an index into vms->msgArray based on the msgnum. */
01780    messageNum = vms->msgArray[msgnum];
01781    if (messageNum == 0) {
01782       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01783       return;
01784    }
01785    if (option_debug > 2)
01786       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01787    /* delete message */
01788    snprintf (arg, sizeof(arg), "%lu", messageNum);
01789    ast_mutex_lock(&vms->lock);
01790    mail_setflag (vms->mailstream, arg, "\\DELETED");
01791    mail_expunge(vms->mailstream);
01792    ast_mutex_unlock(&vms->lock);
01793 }
01794 
01795 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01796 {
01797    struct vm_state *vms_p;
01798    char *file, *filename;
01799    char *attachment;
01800    int i;
01801    BODY *body;
01802 
01803    /* This function is only used for retrieval of IMAP greetings
01804     * regular messages are not retrieved this way, nor are greetings
01805     * if they are stored locally*/
01806    if (msgnum > -1 || !imapgreetings) {
01807       return 0;
01808    } else {
01809       file = strrchr(ast_strdupa(dir), '/');
01810       if (file)
01811          *file++ = '\0';
01812       else {
01813          ast_debug (1, "Failed to procure file name from directory passed.\n");
01814          return -1;
01815       }
01816    }
01817 
01818    /* check if someone is accessing this box right now... */
01819    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01820       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01821       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01822       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01823       * that's all we need to do.
01824       */
01825       if (!(vms_p = create_vm_state_from_user(vmu))) {
01826          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01827          return -1;
01828       }
01829    }
01830 
01831    /* Greetings will never have a prepended message */
01832    *vms_p->introfn = '\0';
01833 
01834    ast_mutex_lock(&vms_p->lock);
01835    init_mailstream(vms_p, GREETINGS_FOLDER);
01836    if (!vms_p->mailstream) {
01837       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01838       ast_mutex_unlock(&vms_p->lock);
01839       return -1;
01840    }
01841 
01842    /*XXX Yuck, this could probably be done a lot better */
01843    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01844       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01845       /* We have the body, now we extract the file name of the first attachment. */
01846       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01847          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01848       } else {
01849          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01850          ast_mutex_unlock(&vms_p->lock);
01851          return -1;
01852       }
01853       filename = strsep(&attachment, ".");
01854       if (!strcmp(filename, file)) {
01855          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01856          vms_p->msgArray[vms_p->curmsg] = i + 1;
01857          save_body(body, vms_p, "2", attachment, 0);
01858          ast_mutex_unlock(&vms_p->lock);
01859          return 0;
01860       }
01861    }
01862    ast_mutex_unlock(&vms_p->lock);
01863 
01864    return -1;
01865 }
01866 
01867 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01868 {
01869    BODY *body;
01870    char *header_content;
01871    char *attachedfilefmt;
01872    char buf[80];
01873    struct vm_state *vms;
01874    char text_file[PATH_MAX];
01875    FILE *text_file_ptr;
01876    int res = 0;
01877    struct ast_vm_user *vmu;
01878 
01879    if (!(vmu = find_user(NULL, context, mailbox))) {
01880       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01881       return -1;
01882    }
01883    
01884    if (msgnum < 0) {
01885       if (imapgreetings) {
01886          res = imap_retrieve_greeting(dir, msgnum, vmu);
01887          goto exit;
01888       } else {
01889          res = 0;
01890          goto exit;
01891       }
01892    }
01893 
01894    /* Before anything can happen, we need a vm_state so that we can
01895     * actually access the imap server through the vms->mailstream
01896     */
01897    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01898       /* This should not happen. If it does, then I guess we'd
01899        * need to create the vm_state, extract which mailbox to
01900        * open, and then set up the msgArray so that the correct
01901        * IMAP message could be accessed. If I have seen correctly
01902        * though, the vms should be obtainable from the vmstates list
01903        * and should have its msgArray properly set up.
01904        */
01905       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01906       res = -1;
01907       goto exit;
01908    }
01909    
01910    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01911    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01912 
01913    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01914    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01915       res = 0;
01916       goto exit;
01917    }
01918 
01919    if (option_debug > 2)
01920       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01921    if (vms->msgArray[msgnum] == 0) {
01922       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01923       res = -1;
01924       goto exit;
01925    }
01926 
01927    /* This will only work for new messages... */
01928    ast_mutex_lock(&vms->lock);
01929    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01930    ast_mutex_unlock(&vms->lock);
01931    /* empty string means no valid header */
01932    if (ast_strlen_zero(header_content)) {
01933       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01934       res = -1;
01935       goto exit;
01936    }
01937 
01938    ast_mutex_lock(&vms->lock);
01939    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01940    ast_mutex_unlock(&vms->lock);
01941 
01942    /* We have the body, now we extract the file name of the first attachment. */
01943    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01944       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01945    } else {
01946       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01947       res = -1;
01948       goto exit;
01949    }
01950    
01951    /* Find the format of the attached file */
01952 
01953    strsep(&attachedfilefmt, ".");
01954    if (!attachedfilefmt) {
01955       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01956       res = -1;
01957       goto exit;
01958    }
01959    
01960    save_body(body, vms, "2", attachedfilefmt, 0);
01961    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01962       *vms->introfn = '\0';
01963    }
01964 
01965    /* Get info from headers!! */
01966    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01967 
01968    if (!(text_file_ptr = fopen(text_file, "w"))) {
01969       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01970    }
01971 
01972    fprintf(text_file_ptr, "%s\n", "[message]");
01973 
01974    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01975    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01976    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01977    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01978    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01979    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01980    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01981    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01982    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01983    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01984    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01985    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01986    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01987    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01988    fclose(text_file_ptr);
01989 
01990 exit:
01991    free_user(vmu);
01992    return res;
01993 }
01994 
01995 static int folder_int(const char *folder)
01996 {
01997    /*assume a NULL folder means INBOX*/
01998    if (!folder) {
01999       return 0;
02000    }
02001    if (!strcasecmp(folder, imapfolder)) {
02002       return 0;
02003    } else if (!strcasecmp(folder, "Old")) {
02004       return 1;
02005    } else if (!strcasecmp(folder, "Work")) {
02006       return 2;
02007    } else if (!strcasecmp(folder, "Family")) {
02008       return 3;
02009    } else if (!strcasecmp(folder, "Friends")) {
02010       return 4;
02011    } else if (!strcasecmp(folder, "Cust1")) {
02012       return 5;
02013    } else if (!strcasecmp(folder, "Cust2")) {
02014       return 6;
02015    } else if (!strcasecmp(folder, "Cust3")) {
02016       return 7;
02017    } else if (!strcasecmp(folder, "Cust4")) {
02018       return 8;
02019    } else if (!strcasecmp(folder, "Cust5")) {
02020       return 9;
02021    } else if (!strcasecmp(folder, "Urgent")) {
02022       return 11;
02023    } else { /*assume they meant INBOX if folder is not found otherwise*/
02024       return 0;
02025    }
02026 }
02027 
02028 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02029 {
02030    SEARCHPGM *pgm;
02031    SEARCHHEADER *hdr;
02032 
02033    struct ast_vm_user *vmu, vmus;
02034    struct vm_state *vms_p;
02035    int ret = 0;
02036    int fold = folder_int(folder);
02037    int urgent = 0;
02038    
02039    /* If URGENT, then look at INBOX */
02040    if (fold == 11) {
02041       fold = NEW_FOLDER;
02042       urgent = 1;
02043    }
02044 
02045    if (ast_strlen_zero(mailbox))
02046       return 0;
02047 
02048    /* We have to get the user before we can open the stream! */
02049    vmu = find_user(&vmus, context, mailbox);
02050    if (!vmu) {
02051       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02052       return -1;
02053    } else {
02054       /* No IMAP account available */
02055       if (vmu->imapuser[0] == '\0') {
02056          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02057          return -1;
02058       }
02059    }
02060    
02061    /* No IMAP account available */
02062    if (vmu->imapuser[0] == '\0') {
02063       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02064       free_user(vmu);
02065       return -1;
02066    }
02067 
02068    /* check if someone is accessing this box right now... */
02069    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02070    if (!vms_p) {
02071       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02072    }
02073    if (vms_p) {
02074       ast_debug(3, "Returning before search - user is logged in\n");
02075       if (fold == 0) { /* INBOX */
02076          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02077       }
02078       if (fold == 1) { /* Old messages */
02079          return vms_p->oldmessages;
02080       }
02081    }
02082 
02083    /* add one if not there... */
02084    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02085    if (!vms_p) {
02086       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02087    }
02088 
02089    if (!vms_p) {
02090       vms_p = create_vm_state_from_user(vmu);
02091    }
02092    ret = init_mailstream(vms_p, fold);
02093    if (!vms_p->mailstream) {
02094       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02095       return -1;
02096    }
02097    if (ret == 0) {
02098       ast_mutex_lock(&vms_p->lock);
02099       pgm = mail_newsearchpgm ();
02100       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02101       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02102       pgm->header = hdr;
02103       if (fold != OLD_FOLDER) {
02104          pgm->unseen = 1;
02105          pgm->seen = 0;
02106       }
02107       /* In the special case where fold is 1 (old messages) we have to do things a bit
02108        * differently. Old messages are stored in the INBOX but are marked as "seen"
02109        */
02110       else {
02111          pgm->unseen = 0;
02112          pgm->seen = 1;
02113       }
02114       /* look for urgent messages */
02115       if (fold == NEW_FOLDER) {
02116          if (urgent) {
02117             pgm->flagged = 1;
02118             pgm->unflagged = 0;
02119          } else {
02120             pgm->flagged = 0;
02121             pgm->unflagged = 1;
02122          }
02123       }
02124       pgm->undeleted = 1;
02125       pgm->deleted = 0;
02126 
02127       vms_p->vmArrayIndex = 0;
02128       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02129       if (fold == 0 && urgent == 0)
02130          vms_p->newmessages = vms_p->vmArrayIndex;
02131       if (fold == 1)
02132          vms_p->oldmessages = vms_p->vmArrayIndex;
02133       if (fold == 0 && urgent == 1)
02134          vms_p->urgentmessages = vms_p->vmArrayIndex;
02135       /*Freeing the searchpgm also frees the searchhdr*/
02136       mail_free_searchpgm(&pgm);
02137       ast_mutex_unlock(&vms_p->lock);
02138       vms_p->updated = 0;
02139       return vms_p->vmArrayIndex;
02140    } else {
02141       ast_mutex_lock(&vms_p->lock);
02142       mail_ping(vms_p->mailstream);
02143       ast_mutex_unlock(&vms_p->lock);
02144    }
02145    return 0;
02146 }
02147 
02148 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02149 {
02150    /* Check if mailbox is full */
02151    check_quota(vms, vmu->imapfolder);
02152    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02153       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02154       ast_play_and_wait(chan, "vm-mailboxfull");
02155       return -1;
02156    }
02157    
02158    /* Check if we have exceeded maxmsg */
02159    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));
02160    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02161       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02162       ast_play_and_wait(chan, "vm-mailboxfull");
02163       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02164       return -1;
02165    }
02166 
02167    return 0;
02168 }
02169 
02170 /*!
02171  * \brief Gets the number of messages that exist in a mailbox folder.
02172  * \param context
02173  * \param mailbox
02174  * \param folder
02175  * 
02176  * This method is used when IMAP backend is used.
02177  * \return The number of messages in this mailbox folder (zero or more).
02178  */
02179 static int messagecount(const char *context, const char *mailbox, const char *folder)
02180 {
02181    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02182       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02183    } else {
02184       return __messagecount(context, mailbox, folder);
02185    }
02186 }
02187 
02188 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)
02189 {
02190    char *myserveremail = serveremail;
02191    char fn[PATH_MAX];
02192    char introfn[PATH_MAX];
02193    char mailbox[256];
02194    char *stringp;
02195    FILE *p = NULL;
02196    char tmp[80] = "/tmp/astmail-XXXXXX";
02197    long len;
02198    void *buf;
02199    int tempcopy = 0;
02200    STRING str;
02201    int ret; /* for better error checking */
02202    char *imap_flags = NIL;
02203    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02204    int box = NEW_FOLDER;
02205 
02206    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02207    if (msgnum < 0) {
02208       if(!imapgreetings) {
02209          return 0;
02210       } else {
02211          box = GREETINGS_FOLDER;
02212       }
02213    }
02214    
02215    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02216       return -1;
02217    }
02218 
02219    /* Set urgent flag for IMAP message */
02220    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02221       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02222       imap_flags = "\\FLAGGED";
02223    }
02224    
02225    /* Attach only the first format */
02226    fmt = ast_strdupa(fmt);
02227    stringp = fmt;
02228    strsep(&stringp, "|");
02229 
02230    if (!ast_strlen_zero(vmu->serveremail))
02231       myserveremail = vmu->serveremail;
02232 
02233    if (msgnum > -1)
02234       make_file(fn, sizeof(fn), dir, msgnum);
02235    else
02236       ast_copy_string (fn, dir, sizeof(fn));
02237 
02238    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02239    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02240       *introfn = '\0';
02241    }
02242    
02243    if (ast_strlen_zero(vmu->email)) {
02244       /* We need the vmu->email to be set when we call make_email_file, but
02245        * if we keep it set, a duplicate e-mail will be created. So at the end
02246        * of this function, we will revert back to an empty string if tempcopy
02247        * is 1.
02248        */
02249       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02250       tempcopy = 1;
02251    }
02252 
02253    if (!strcmp(fmt, "wav49"))
02254       fmt = "WAV";
02255    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02256 
02257    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02258       command hangs. */
02259    if (!(p = vm_mkftemp(tmp))) {
02260       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02261       if (tempcopy)
02262          *(vmu->email) = '\0';
02263       return -1;
02264    }
02265 
02266    if (msgnum < 0 && imapgreetings) {
02267       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02268          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02269          return -1;
02270       }
02271       imap_delete_old_greeting(fn, vms);
02272    }
02273 
02274    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02275       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02276       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02277       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02278    /* read mail file to memory */
02279    len = ftell(p);
02280    rewind(p);
02281    if (!(buf = ast_malloc(len + 1))) {
02282       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02283       fclose(p);
02284       if (tempcopy)
02285          *(vmu->email) = '\0';
02286       return -1;
02287    }
02288    if (fread(buf, len, 1, p) < len) {
02289       if (ferror(p)) {
02290          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02291          return -1;
02292       }
02293    }
02294    ((char *) buf)[len] = '\0';
02295    INIT(&str, mail_string, buf, len);
02296    ret = init_mailstream(vms, box);
02297    if (ret == 0) {
02298       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02299       ast_mutex_lock(&vms->lock);
02300       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02301          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02302       ast_mutex_unlock(&vms->lock);
02303       fclose(p);
02304       unlink(tmp);
02305       ast_free(buf);
02306    } else {
02307       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02308       fclose(p);
02309       unlink(tmp);
02310       ast_free(buf);
02311       return -1;
02312    }
02313    ast_debug(3, "%s stored\n", fn);
02314    
02315    if (tempcopy)
02316       *(vmu->email) = '\0';
02317    inprocess_count(vmu->mailbox, vmu->context, -1);
02318    return 0;
02319 
02320 }
02321 
02322 /*!
02323  * \brief Gets the number of messages that exist in the inbox folder.
02324  * \param mailbox_context
02325  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02326  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02327  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02328  * 
02329  * This method is used when IMAP backend is used.
02330  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02331  *
02332  * \return zero on success, -1 on error.
02333  */
02334 
02335 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02336 {
02337    char tmp[PATH_MAX] = "";
02338    char *mailboxnc;
02339    char *context;
02340    char *mb;
02341    char *cur;
02342    if (newmsgs)
02343       *newmsgs = 0;
02344    if (oldmsgs)
02345       *oldmsgs = 0;
02346    if (urgentmsgs)
02347       *urgentmsgs = 0;
02348 
02349    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02350    /* If no mailbox, return immediately */
02351    if (ast_strlen_zero(mailbox_context))
02352       return 0;
02353    
02354    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02355    context = strchr(tmp, '@');
02356    if (strchr(mailbox_context, ',')) {
02357       int tmpnew, tmpold, tmpurgent;
02358       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02359       mb = tmp;
02360       while ((cur = strsep(&mb, ", "))) {
02361          if (!ast_strlen_zero(cur)) {
02362             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02363                return -1;
02364             else {
02365                if (newmsgs)
02366                   *newmsgs += tmpnew; 
02367                if (oldmsgs)
02368                   *oldmsgs += tmpold;
02369                if (urgentmsgs)
02370                   *urgentmsgs += tmpurgent;
02371             }
02372          }
02373       }
02374       return 0;
02375    }
02376    if (context) {
02377       *context = '\0';
02378       mailboxnc = tmp;
02379       context++;
02380    } else {
02381       context = "default";
02382       mailboxnc = (char *) mailbox_context;
02383    }
02384 
02385    if (newmsgs) {
02386       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02387       if (!vmu) {
02388          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02389          return -1;
02390       }
02391       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02392          return -1;
02393       }
02394    }
02395    if (oldmsgs) {
02396       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02397          return -1;
02398       }
02399    }
02400    if (urgentmsgs) {
02401       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02402          return -1;
02403       }
02404    }
02405    return 0;
02406 }
02407 
02408 /** 
02409  * \brief Determines if the given folder has messages.
02410  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02411  * \param folder the folder to look in
02412  *
02413  * This function is used when the mailbox is stored in an IMAP back end.
02414  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02415  * \return 1 if the folder has one or more messages. zero otherwise.
02416  */
02417 
02418 static int has_voicemail(const char *mailbox, const char *folder)
02419 {
02420    char tmp[256], *tmp2, *box, *context;
02421    ast_copy_string(tmp, mailbox, sizeof(tmp));
02422    tmp2 = tmp;
02423    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02424       while ((box = strsep(&tmp2, ",&"))) {
02425          if (!ast_strlen_zero(box)) {
02426             if (has_voicemail(box, folder)) {
02427                return 1;
02428             }
02429          }
02430       }
02431    }
02432    if ((context = strchr(tmp, '@'))) {
02433       *context++ = '\0';
02434    } else {
02435       context = "default";
02436    }
02437    return __messagecount(context, tmp, folder) ? 1 : 0;
02438 }
02439 
02440 /*!
02441  * \brief Copies a message from one mailbox to another.
02442  * \param chan
02443  * \param vmu
02444  * \param imbox
02445  * \param msgnum
02446  * \param duration
02447  * \param recip
02448  * \param fmt
02449  * \param dir
02450  *
02451  * This works with IMAP storage based mailboxes.
02452  *
02453  * \return zero on success, -1 on error.
02454  */
02455 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)
02456 {
02457    struct vm_state *sendvms = NULL, *destvms = NULL;
02458    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02459    if (msgnum >= recip->maxmsg) {
02460       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02461       return -1;
02462    }
02463    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02464       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02465       return -1;
02466    }
02467    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02468       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02469       return -1;
02470    }
02471    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02472    ast_mutex_lock(&sendvms->lock);
02473    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02474       ast_mutex_unlock(&sendvms->lock);
02475       return 0;
02476    }
02477    ast_mutex_unlock(&sendvms->lock);
02478    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02479    return -1;
02480 }
02481 
02482 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02483 {
02484    char tmp[256], *t = tmp;
02485    size_t left = sizeof(tmp);
02486    
02487    if (box == OLD_FOLDER) {
02488       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02489    } else {
02490       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02491    }
02492 
02493    if (box == NEW_FOLDER) {
02494       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02495    } else {
02496       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02497    }
02498 
02499    /* Build up server information */
02500    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02501 
02502    /* Add authentication user if present */
02503    if (!ast_strlen_zero(authuser))
02504       ast_build_string(&t, &left, "/authuser=%s", authuser);
02505 
02506    /* Add flags if present */
02507    if (!ast_strlen_zero(imapflags))
02508       ast_build_string(&t, &left, "/%s", imapflags);
02509 
02510    /* End with username */
02511 #if 1
02512    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02513 #else
02514    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02515 #endif
02516    if (box == NEW_FOLDER || box == OLD_FOLDER)
02517       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02518    else if (box == GREETINGS_FOLDER)
02519       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02520    else {   /* Other folders such as Friends, Family, etc... */
02521       if (!ast_strlen_zero(imapparentfolder)) {
02522          /* imapparentfolder would typically be set to INBOX */
02523          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02524       } else {
02525          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02526       }
02527    }
02528 }
02529 
02530 static int init_mailstream(struct vm_state *vms, int box)
02531 {
02532    MAILSTREAM *stream = NIL;
02533    long debug;
02534    char tmp[256];
02535    
02536    if (!vms) {
02537       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02538       return -1;
02539    }
02540    if (option_debug > 2)
02541       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02542    if (vms->mailstream == NIL || !vms->mailstream) {
02543       if (option_debug)
02544          ast_log(LOG_DEBUG, "mailstream not set.\n");
02545    } else {
02546       stream = vms->mailstream;
02547    }
02548    /* debug = T;  user wants protocol telemetry? */
02549    debug = NIL;  /* NO protocol telemetry? */
02550 
02551    if (delimiter == '\0') {      /* did not probe the server yet */
02552       char *cp;
02553 #ifdef USE_SYSTEM_IMAP
02554 #include <imap/linkage.c>
02555 #elif defined(USE_SYSTEM_CCLIENT)
02556 #include <c-client/linkage.c>
02557 #else
02558 #include "linkage.c"
02559 #endif
02560       /* Connect to INBOX first to get folders delimiter */
02561       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02562       ast_mutex_lock(&vms->lock);
02563       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02564       ast_mutex_unlock(&vms->lock);
02565       if (stream == NIL) {
02566          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02567          return -1;
02568       }
02569       get_mailbox_delimiter(stream);
02570       /* update delimiter in imapfolder */
02571       for (cp = vms->imapfolder; *cp; cp++)
02572          if (*cp == '/')
02573             *cp = delimiter;
02574    }
02575    /* Now connect to the target folder */
02576    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02577    if (option_debug > 2)
02578       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02579    ast_mutex_lock(&vms->lock);
02580    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02581    ast_mutex_unlock(&vms->lock);
02582    if (vms->mailstream == NIL) {
02583       return -1;
02584    } else {
02585       return 0;
02586    }
02587 }
02588 
02589 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02590 {
02591    SEARCHPGM *pgm;
02592    SEARCHHEADER *hdr;
02593    int ret, urgent = 0;
02594 
02595    /* If Urgent, then look at INBOX */
02596    if (box == 11) {
02597       box = NEW_FOLDER;
02598       urgent = 1;
02599    }
02600 
02601    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02602    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02603    vms->imapversion = vmu->imapversion;
02604    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02605 
02606    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02607       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02608       return -1;
02609    }
02610    
02611    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02612    
02613    /* Check Quota */
02614    if  (box == 0)  {
02615       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02616       check_quota(vms, (char *) mbox(vmu, box));
02617    }
02618 
02619    ast_mutex_lock(&vms->lock);
02620    pgm = mail_newsearchpgm();
02621 
02622    /* Check IMAP folder for Asterisk messages only... */
02623    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02624    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02625    pgm->header = hdr;
02626    pgm->deleted = 0;
02627    pgm->undeleted = 1;
02628 
02629    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02630    if (box == NEW_FOLDER && urgent == 1) {
02631       pgm->unseen = 1;
02632       pgm->seen = 0;
02633       pgm->flagged = 1;
02634       pgm->unflagged = 0;
02635    } else if (box == NEW_FOLDER && urgent == 0) {
02636       pgm->unseen = 1;
02637       pgm->seen = 0;
02638       pgm->flagged = 0;
02639       pgm->unflagged = 1;
02640    } else if (box == OLD_FOLDER) {
02641       pgm->seen = 1;
02642       pgm->unseen = 0;
02643    }
02644 
02645    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02646 
02647    vms->vmArrayIndex = 0;
02648    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02649    vms->lastmsg = vms->vmArrayIndex - 1;
02650    mail_free_searchpgm(&pgm);
02651    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02652     * ensure to allocate enough space to account for all of them. Warn if old messages
02653     * have not been checked first as that is required.
02654     */
02655    if (box == 0 && !vms->dh_arraysize) {
02656       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02657    }
02658    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02659       ast_mutex_unlock(&vms->lock);
02660       return -1;
02661    }
02662 
02663    ast_mutex_unlock(&vms->lock);
02664    return 0;
02665 }
02666 
02667 static void write_file(char *filename, char *buffer, unsigned long len)
02668 {
02669    FILE *output;
02670 
02671    output = fopen (filename, "w");
02672    if (fwrite(buffer, len, 1, output) != 1) {
02673       if (ferror(output)) {
02674          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02675       }
02676    }
02677    fclose (output);
02678 }
02679 
02680 static void update_messages_by_imapuser(const char *user, unsigned long number)
02681 {
02682    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02683 
02684    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02685       return;
02686    }
02687 
02688    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02689    vms->msgArray[vms->vmArrayIndex++] = number;
02690 }
02691 
02692 void mm_searched(MAILSTREAM *stream, unsigned long number)
02693 {
02694    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02695 
02696    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02697       return;
02698 
02699    update_messages_by_imapuser(user, number);
02700 }
02701 
02702 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02703 {
02704    struct ast_variable *var;
02705    struct ast_vm_user *vmu;
02706 
02707    vmu = ast_calloc(1, sizeof *vmu);
02708    if (!vmu)
02709       return NULL;
02710    ast_set_flag(vmu, VM_ALLOCED);
02711    populate_defaults(vmu);
02712 
02713    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02714    if (var) {
02715       apply_options_full(vmu, var);
02716       ast_variables_destroy(var);
02717       return vmu;
02718    } else {
02719       ast_free(vmu);
02720       return NULL;
02721    }
02722 }
02723 
02724 /* Interfaces to C-client */
02725 
02726 void mm_exists(MAILSTREAM * stream, unsigned long number)
02727 {
02728    /* mail_ping will callback here if new mail! */
02729    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02730    if (number == 0) return;
02731    set_update(stream);
02732 }
02733 
02734 
02735 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02736 {
02737    /* mail_ping will callback here if expunged mail! */
02738    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02739    if (number == 0) return;
02740    set_update(stream);
02741 }
02742 
02743 
02744 void mm_flags(MAILSTREAM * stream, unsigned long number)
02745 {
02746    /* mail_ping will callback here if read mail! */
02747    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02748    if (number == 0) return;
02749    set_update(stream);
02750 }
02751 
02752 
02753 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02754 {
02755    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02756    mm_log (string, errflg);
02757 }
02758 
02759 
02760 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02761 {
02762    if (delimiter == '\0') {
02763       delimiter = delim;
02764    }
02765 
02766    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02767    if (attributes & LATT_NOINFERIORS)
02768       ast_debug(5, "no inferiors\n");
02769    if (attributes & LATT_NOSELECT)
02770       ast_debug(5, "no select\n");
02771    if (attributes & LATT_MARKED)
02772       ast_debug(5, "marked\n");
02773    if (attributes & LATT_UNMARKED)
02774       ast_debug(5, "unmarked\n");
02775 }
02776 
02777 
02778 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02779 {
02780    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02781    if (attributes & LATT_NOINFERIORS)
02782       ast_debug(5, "no inferiors\n");
02783    if (attributes & LATT_NOSELECT)
02784       ast_debug(5, "no select\n");
02785    if (attributes & LATT_MARKED)
02786       ast_debug(5, "marked\n");
02787    if (attributes & LATT_UNMARKED)
02788       ast_debug(5, "unmarked\n");
02789 }
02790 
02791 
02792 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02793 {
02794    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02795    if (status->flags & SA_MESSAGES)
02796       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02797    if (status->flags & SA_RECENT)
02798       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02799    if (status->flags & SA_UNSEEN)
02800       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02801    if (status->flags & SA_UIDVALIDITY)
02802       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02803    if (status->flags & SA_UIDNEXT)
02804       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02805    ast_log(AST_LOG_NOTICE, "\n");
02806 }
02807 
02808 
02809 void mm_log(char *string, long errflg)
02810 {
02811    switch ((short) errflg) {
02812       case NIL:
02813          ast_debug(1, "IMAP Info: %s\n", string);
02814          break;
02815       case PARSE:
02816       case WARN:
02817          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02818          break;
02819       case ERROR:
02820          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02821          break;
02822    }
02823 }
02824 
02825 
02826 void mm_dlog(char *string)
02827 {
02828    ast_log(AST_LOG_NOTICE, "%s\n", string);
02829 }
02830 
02831 
02832 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02833 {
02834    struct ast_vm_user *vmu;
02835 
02836    ast_debug(4, "Entering callback mm_login\n");
02837 
02838    ast_copy_string(user, mb->user, MAILTMPLEN);
02839 
02840    /* We should only do this when necessary */
02841    if (!ast_strlen_zero(authpassword)) {
02842       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02843    } else {
02844       AST_LIST_TRAVERSE(&users, vmu, list) {
02845          if (!strcasecmp(mb->user, vmu->imapuser)) {
02846             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02847             break;
02848          }
02849       }
02850       if (!vmu) {
02851          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02852             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02853             free_user(vmu);
02854          }
02855       }
02856    }
02857 }
02858 
02859 
02860 void mm_critical(MAILSTREAM * stream)
02861 {
02862 }
02863 
02864 
02865 void mm_nocritical(MAILSTREAM * stream)
02866 {
02867 }
02868 
02869 
02870 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02871 {
02872    kill (getpid (), SIGSTOP);
02873    return NIL;
02874 }
02875 
02876 
02877 void mm_fatal(char *string)
02878 {
02879    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02880 }
02881 
02882 /* C-client callback to handle quota */
02883 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02884 {
02885    struct vm_state *vms;
02886    char *mailbox = stream->mailbox, *user;
02887    char buf[1024] = "";
02888    unsigned long usage = 0, limit = 0;
02889    
02890    while (pquota) {
02891       usage = pquota->usage;
02892       limit = pquota->limit;
02893       pquota = pquota->next;
02894    }
02895    
02896    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)))) {
02897       ast_log(AST_LOG_ERROR, "No state found.\n");
02898       return;
02899    }
02900 
02901    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02902 
02903    vms->quota_usage = usage;
02904    vms->quota_limit = limit;
02905 }
02906 
02907 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02908 {
02909    char *start, *eol_pnt;
02910    int taglen;
02911 
02912    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02913       return NULL;
02914 
02915    taglen = strlen(tag) + 1;
02916    if (taglen < 1)
02917       return NULL;
02918 
02919    if (!(start = strstr(header, tag)))
02920       return NULL;
02921 
02922    /* Since we can be called multiple times we should clear our buffer */
02923    memset(buf, 0, len);
02924 
02925    ast_copy_string(buf, start+taglen, len);
02926    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02927       *eol_pnt = '\0';
02928    return buf;
02929 }
02930 
02931 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02932 {
02933    char *start, *quote, *eol_pnt;
02934 
02935    if (ast_strlen_zero(mailbox))
02936       return NULL;
02937 
02938    if (!(start = strstr(mailbox, "/user=")))
02939       return NULL;
02940 
02941    ast_copy_string(buf, start+6, len);
02942 
02943    if (!(quote = strchr(buf, '\"'))) {
02944       if (!(eol_pnt = strchr(buf, '/')))
02945          eol_pnt = strchr(buf,'}');
02946       *eol_pnt = '\0';
02947       return buf;
02948    } else {
02949       eol_pnt = strchr(buf+1,'\"');
02950       *eol_pnt = '\0';
02951       return buf+1;
02952    }
02953 }
02954 
02955 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02956 {
02957    struct vm_state *vms_p;
02958 
02959    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02960    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02961       return vms_p;
02962    }
02963    if (option_debug > 4)
02964       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02965    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02966       return NULL;
02967    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02968    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02969    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02970    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02971    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02972    vms_p->imapversion = vmu->imapversion;
02973    if (option_debug > 4)
02974       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02975    vms_p->updated = 1;
02976    /* set mailbox to INBOX! */
02977    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02978    init_vm_state(vms_p);
02979    vmstate_insert(vms_p);
02980    return vms_p;
02981 }
02982 
02983 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02984 {
02985    struct vmstate *vlist = NULL;
02986 
02987    if (interactive) {
02988       struct vm_state *vms;
02989       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02990       vms = pthread_getspecific(ts_vmstate.key);
02991       return vms;
02992    }
02993 
02994    AST_LIST_LOCK(&vmstates);
02995    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02996       if (!vlist->vms) {
02997          ast_debug(3, "error: vms is NULL for %s\n", user);
02998          continue;
02999       }
03000       if (vlist->vms->imapversion != imapversion) {
03001          continue;
03002       }
03003       if (!vlist->vms->imapuser) {
03004          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03005          continue;
03006       }
03007 
03008       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03009          AST_LIST_UNLOCK(&vmstates);
03010          return vlist->vms;
03011       }
03012    }
03013    AST_LIST_UNLOCK(&vmstates);
03014 
03015    ast_debug(3, "%s not found in vmstates\n", user);
03016 
03017    return NULL;
03018 }
03019 
03020 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03021 {
03022 
03023    struct vmstate *vlist = NULL;
03024    const char *local_context = S_OR(context, "default");
03025 
03026    if (interactive) {
03027       struct vm_state *vms;
03028       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03029       vms = pthread_getspecific(ts_vmstate.key);
03030       return vms;
03031    }
03032 
03033    AST_LIST_LOCK(&vmstates);
03034    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03035       if (!vlist->vms) {
03036          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03037          continue;
03038       }
03039       if (vlist->vms->imapversion != imapversion) {
03040          continue;
03041       }
03042       if (!vlist->vms->username || !vlist->vms->context) {
03043          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03044          continue;
03045       }
03046 
03047       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);
03048       
03049       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03050          ast_debug(3, "Found it!\n");
03051          AST_LIST_UNLOCK(&vmstates);
03052          return vlist->vms;
03053       }
03054    }
03055    AST_LIST_UNLOCK(&vmstates);
03056 
03057    ast_debug(3, "%s not found in vmstates\n", mailbox);
03058 
03059    return NULL;
03060 }
03061 
03062 static void vmstate_insert(struct vm_state *vms) 
03063 {
03064    struct vmstate *v;
03065    struct vm_state *altvms;
03066 
03067    /* If interactive, it probably already exists, and we should
03068       use the one we already have since it is more up to date.
03069       We can compare the username to find the duplicate */
03070    if (vms->interactive == 1) {
03071       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03072       if (altvms) {  
03073          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03074          vms->newmessages = altvms->newmessages;
03075          vms->oldmessages = altvms->oldmessages;
03076          vms->vmArrayIndex = altvms->vmArrayIndex;
03077          vms->lastmsg = altvms->lastmsg;
03078          vms->curmsg = altvms->curmsg;
03079          /* get a pointer to the persistent store */
03080          vms->persist_vms = altvms;
03081          /* Reuse the mailstream? */
03082 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03083          vms->mailstream = altvms->mailstream;
03084 #else
03085          vms->mailstream = NIL;
03086 #endif
03087       }
03088       return;
03089    }
03090 
03091    if (!(v = ast_calloc(1, sizeof(*v))))
03092       return;
03093    
03094    v->vms = vms;
03095 
03096    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03097 
03098    AST_LIST_LOCK(&vmstates);
03099    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03100    AST_LIST_UNLOCK(&vmstates);
03101 }
03102 
03103 static void vmstate_delete(struct vm_state *vms) 
03104 {
03105    struct vmstate *vc = NULL;
03106    struct vm_state *altvms = NULL;
03107 
03108    /* If interactive, we should copy pertinent info
03109       back to the persistent state (to make update immediate) */
03110    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03111       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03112       altvms->newmessages = vms->newmessages;
03113       altvms->oldmessages = vms->oldmessages;
03114       altvms->updated = 1;
03115       vms->mailstream = mail_close(vms->mailstream);
03116 
03117       /* Interactive states are not stored within the persistent list */
03118       return;
03119    }
03120    
03121    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03122    
03123    AST_LIST_LOCK(&vmstates);
03124    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03125       if (vc->vms == vms) {
03126          AST_LIST_REMOVE_CURRENT(list);
03127          break;
03128       }
03129    }
03130    AST_LIST_TRAVERSE_SAFE_END
03131    AST_LIST_UNLOCK(&vmstates);
03132    
03133    if (vc) {
03134       ast_mutex_destroy(&vc->vms->lock);
03135       ast_free(vc);
03136    }
03137    else
03138       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03139 }
03140 
03141 static void set_update(MAILSTREAM * stream) 
03142 {
03143    struct vm_state *vms;
03144    char *mailbox = stream->mailbox, *user;
03145    char buf[1024] = "";
03146 
03147    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03148       if (user && option_debug > 2)
03149          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03150       return;
03151    }
03152 
03153    ast_debug(3, "User %s mailbox set for update.\n", user);
03154 
03155    vms->updated = 1; /* Set updated flag since mailbox changed */
03156 }
03157 
03158 static void init_vm_state(struct vm_state *vms) 
03159 {
03160    int x;
03161    vms->vmArrayIndex = 0;
03162    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03163       vms->msgArray[x] = 0;
03164    }
03165    ast_mutex_init(&vms->lock);
03166 }
03167 
03168 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03169 {
03170    char *body_content;
03171    char *body_decoded;
03172    char *fn = is_intro ? vms->introfn : vms->fn;
03173    unsigned long len;
03174    unsigned long newlen;
03175    char filename[256];
03176    
03177    if (!body || body == NIL)
03178       return -1;
03179 
03180    ast_mutex_lock(&vms->lock);
03181    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03182    ast_mutex_unlock(&vms->lock);
03183    if (body_content != NIL) {
03184       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03185       /* ast_debug(1,body_content); */
03186       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03187       /* If the body of the file is empty, return an error */
03188       if (!newlen) {
03189          return -1;
03190       }
03191       write_file(filename, (char *) body_decoded, newlen);
03192    } else {
03193       ast_debug(5, "Body of message is NULL.\n");
03194       return -1;
03195    }
03196    return 0;
03197 }
03198 
03199 /*! 
03200  * \brief Get delimiter via mm_list callback 
03201  * \param stream
03202  *
03203  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03204  */
03205 /* MUTEX should already be held */
03206 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03207    char tmp[50];
03208    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03209    mail_list(stream, tmp, "*");
03210 }
03211 
03212 /*! 
03213  * \brief Check Quota for user 
03214  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03215  * \param mailbox the mailbox to check the quota for.
03216  *
03217  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03218  */
03219 static void check_quota(struct vm_state *vms, char *mailbox) {
03220    ast_mutex_lock(&vms->lock);
03221    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03222    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03223    if (vms && vms->mailstream != NULL) {
03224       imap_getquotaroot(vms->mailstream, mailbox);
03225    } else {
03226       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03227    }
03228    ast_mutex_unlock(&vms->lock);
03229 }
03230 
03231 #endif /* IMAP_STORAGE */
03232 
03233 /*! \brief Lock file path
03234  * only return failure if ast_lock_path returns 'timeout',
03235  * not if the path does not exist or any other reason
03236  */
03237 static int vm_lock_path(const char *path)
03238 {
03239    switch (ast_lock_path(path)) {
03240    case AST_LOCK_TIMEOUT:
03241       return -1;
03242    default:
03243       return 0;
03244    }
03245 }
03246 
03247 
03248 #ifdef ODBC_STORAGE
03249 struct generic_prepare_struct {
03250    char *sql;
03251    int argc;
03252    char **argv;
03253 };
03254 
03255 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03256 {
03257    struct generic_prepare_struct *gps = data;
03258    int res, i;
03259    SQLHSTMT stmt;
03260 
03261    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03262    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03263       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03264       return NULL;
03265    }
03266    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03267    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03268       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03269       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03270       return NULL;
03271    }
03272    for (i = 0; i < gps->argc; i++)
03273       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03274 
03275    return stmt;
03276 }
03277 
03278 /*!
03279  * \brief Retrieves a file from an ODBC data store.
03280  * \param dir the path to the file to be retreived.
03281  * \param msgnum the message number, such as within a mailbox folder.
03282  * 
03283  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03284  * 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.
03285  *
03286  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03287  * The output is the message information file with the name msgnum and the extension .txt
03288  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03289  * 
03290  * \return 0 on success, -1 on error.
03291  */
03292 static int retrieve_file(char *dir, int msgnum)
03293 {
03294    int x = 0;
03295    int res;
03296    int fd = -1;
03297    size_t fdlen = 0;
03298    void *fdm = MAP_FAILED;
03299    SQLSMALLINT colcount = 0;
03300    SQLHSTMT stmt;
03301    char sql[PATH_MAX];
03302    char fmt[80]="";
03303    char *c;
03304    char coltitle[256];
03305    SQLSMALLINT collen;
03306    SQLSMALLINT datatype;
03307    SQLSMALLINT decimaldigits;
03308    SQLSMALLINT nullable;
03309    SQLULEN colsize;
03310    SQLLEN colsize2;
03311    FILE *f = NULL;
03312    char rowdata[80];
03313    char fn[PATH_MAX];
03314    char full_fn[PATH_MAX];
03315    char msgnums[80];
03316    char *argv[] = { dir, msgnums };
03317    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03318 
03319    struct odbc_obj *obj;
03320    obj = ast_odbc_request_obj(odbc_database, 0);
03321    if (obj) {
03322       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03323       c = strchr(fmt, '|');
03324       if (c)
03325          *c = '\0';
03326       if (!strcasecmp(fmt, "wav49"))
03327          strcpy(fmt, "WAV");
03328       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03329       if (msgnum > -1)
03330          make_file(fn, sizeof(fn), dir, msgnum);
03331       else
03332          ast_copy_string(fn, dir, sizeof(fn));
03333 
03334       /* Create the information file */
03335       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03336       
03337       if (!(f = fopen(full_fn, "w+"))) {
03338          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03339          goto yuck;
03340       }
03341       
03342       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03343       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03344       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03345       if (!stmt) {
03346          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03347          ast_odbc_release_obj(obj);
03348          goto yuck;
03349       }
03350       res = SQLFetch(stmt);
03351       if (res == SQL_NO_DATA) {
03352          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03353          ast_odbc_release_obj(obj);
03354          goto yuck;
03355       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03356          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03357          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03358          ast_odbc_release_obj(obj);
03359          goto yuck;
03360       }
03361       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03362       if (fd < 0) {
03363          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03364          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03365          ast_odbc_release_obj(obj);
03366          goto yuck;
03367       }
03368       res = SQLNumResultCols(stmt, &colcount);
03369       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03370          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03371          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03372          ast_odbc_release_obj(obj);
03373          goto yuck;
03374       }
03375       if (f) 
03376          fprintf(f, "[message]\n");
03377       for (x = 0; x < colcount; x++) {
03378          rowdata[0] = '\0';
03379          colsize = 0;
03380          collen = sizeof(coltitle);
03381          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03382                   &datatype, &colsize, &decimaldigits, &nullable);
03383          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03384             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03385             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03386             ast_odbc_release_obj(obj);
03387             goto yuck;
03388          }
03389          if (!strcasecmp(coltitle, "recording")) {
03390             off_t offset;
03391             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03392             fdlen = colsize2;
03393             if (fd > -1) {
03394                char tmp[1]="";
03395                lseek(fd, fdlen - 1, SEEK_SET);
03396                if (write(fd, tmp, 1) != 1) {
03397                   close(fd);
03398                   fd = -1;
03399                   continue;
03400                }
03401                /* Read out in small chunks */
03402                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03403                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03404                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03405                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03406                      ast_odbc_release_obj(obj);
03407                      goto yuck;
03408                   } else {
03409                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03410                      munmap(fdm, CHUNKSIZE);
03411                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03412                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03413                         unlink(full_fn);
03414                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03415                         ast_odbc_release_obj(obj);
03416                         goto yuck;
03417                      }
03418                   }
03419                }
03420                if (truncate(full_fn, fdlen) < 0) {
03421                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03422                }
03423             }
03424          } else {
03425             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03426             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03427                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03428                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03429                ast_odbc_release_obj(obj);
03430                goto yuck;
03431             }
03432             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03433                fprintf(f, "%s=%s\n", coltitle, rowdata);
03434          }
03435       }
03436       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03437       ast_odbc_release_obj(obj);
03438    } else
03439       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03440 yuck:
03441    if (f)
03442       fclose(f);
03443    if (fd > -1)
03444       close(fd);
03445    return x - 1;
03446 }
03447 
03448 /*!
03449  * \brief Determines the highest message number in use for a given user and mailbox folder.
03450  * \param vmu 
03451  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03452  *
03453  * This method is used when mailboxes are stored in an ODBC back end.
03454  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03455  *
03456  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03457 
03458  */
03459 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03460 {
03461    int x = 0;
03462    int res;
03463    SQLHSTMT stmt;
03464    char sql[PATH_MAX];
03465    char rowdata[20];
03466    char *argv[] = { dir };
03467    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03468 
03469    struct odbc_obj *obj;
03470    obj = ast_odbc_request_obj(odbc_database, 0);
03471    if (obj) {
03472       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03473 
03474       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03475       if (!stmt) {
03476          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03477          ast_odbc_release_obj(obj);
03478          goto yuck;
03479       }
03480       res = SQLFetch(stmt);
03481       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03482          if (res == SQL_NO_DATA) {
03483             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03484          } else {
03485             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03486          }
03487 
03488          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03489          ast_odbc_release_obj(obj);
03490          goto yuck;
03491       }
03492       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03493       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03494          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03495          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03496          ast_odbc_release_obj(obj);
03497          goto yuck;
03498       }
03499       if (sscanf(rowdata, "%30d", &x) != 1)
03500          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03501       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03502       ast_odbc_release_obj(obj);
03503       return x;
03504    } else
03505       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03506 yuck:
03507    return x - 1;
03508 }
03509 
03510 /*!
03511  * \brief Determines if the specified message exists.
03512  * \param dir the folder the mailbox folder to look for messages. 
03513  * \param msgnum the message index to query for.
03514  *
03515  * This method is used when mailboxes are stored in an ODBC back end.
03516  *
03517  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03518  */
03519 static int message_exists(char *dir, int msgnum)
03520 {
03521    int x = 0;
03522    int res;
03523    SQLHSTMT stmt;
03524    char sql[PATH_MAX];
03525    char rowdata[20];
03526    char msgnums[20];
03527    char *argv[] = { dir, msgnums };
03528    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03529 
03530    struct odbc_obj *obj;
03531    obj = ast_odbc_request_obj(odbc_database, 0);
03532    if (obj) {
03533       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03534       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03535       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03536       if (!stmt) {
03537          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03538          ast_odbc_release_obj(obj);
03539          goto yuck;
03540       }
03541       res = SQLFetch(stmt);
03542       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03543          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03544          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03545          ast_odbc_release_obj(obj);
03546          goto yuck;
03547       }
03548       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03549       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03550          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03551          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03552          ast_odbc_release_obj(obj);
03553          goto yuck;
03554       }
03555       if (sscanf(rowdata, "%30d", &x) != 1)
03556          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03557       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03558       ast_odbc_release_obj(obj);
03559    } else
03560       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03561 yuck:
03562    return x;
03563 }
03564 
03565 /*!
03566  * \brief returns the number of messages found.
03567  * \param vmu
03568  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03569  *
03570  * This method is used when mailboxes are stored in an ODBC back end.
03571  *
03572  * \return The count of messages being zero or more, less than zero on error.
03573  */
03574 static int count_messages(struct ast_vm_user *vmu, char *dir)
03575 {
03576    int x = 0;
03577    int res;
03578    SQLHSTMT stmt;
03579    char sql[PATH_MAX];
03580    char rowdata[20];
03581    char *argv[] = { dir };
03582    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03583 
03584    struct odbc_obj *obj;
03585    obj = ast_odbc_request_obj(odbc_database, 0);
03586    if (obj) {
03587       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03588       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03589       if (!stmt) {
03590          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03591          ast_odbc_release_obj(obj);
03592          goto yuck;
03593       }
03594       res = SQLFetch(stmt);
03595       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03596          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03597          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03598          ast_odbc_release_obj(obj);
03599          goto yuck;
03600       }
03601       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03602       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03603          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03604          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03605          ast_odbc_release_obj(obj);
03606          goto yuck;
03607       }
03608       if (sscanf(rowdata, "%30d", &x) != 1)
03609          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03610       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03611       ast_odbc_release_obj(obj);
03612       return x;
03613    } else
03614       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03615 yuck:
03616    return x - 1;
03617 
03618 }
03619 
03620 /*!
03621  * \brief Deletes a message from the mailbox folder.
03622  * \param sdir The mailbox folder to work in.
03623  * \param smsg The message index to be deleted.
03624  *
03625  * This method is used when mailboxes are stored in an ODBC back end.
03626  * The specified message is directly deleted from the database 'voicemessages' table.
03627  * 
03628  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03629  */
03630 static void delete_file(const char *sdir, int smsg)
03631 {
03632    SQLHSTMT stmt;
03633    char sql[PATH_MAX];
03634    char msgnums[20];
03635    char *argv[] = { NULL, msgnums };
03636    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03637    struct odbc_obj *obj;
03638 
03639    argv[0] = ast_strdupa(sdir);
03640 
03641    obj = ast_odbc_request_obj(odbc_database, 0);
03642    if (obj) {
03643       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03644       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03645       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03646       if (!stmt)
03647          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03648       else
03649          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03650       ast_odbc_release_obj(obj);
03651    } else
03652       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03653    return;  
03654 }
03655 
03656 /*!
03657  * \brief Copies a voicemail from one mailbox to another.
03658  * \param sdir the folder for which to look for the message to be copied.
03659  * \param smsg the index of the message to be copied.
03660  * \param ddir the destination folder to copy the message into.
03661  * \param dmsg the index to be used for the copied message.
03662  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03663  * \param dmailboxcontext The context for the destination user.
03664  *
03665  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03666  */
03667 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03668 {
03669    SQLHSTMT stmt;
03670    char sql[512];
03671    char msgnums[20];
03672    char msgnumd[20];
03673    struct odbc_obj *obj;
03674    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03675    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03676 
03677    delete_file(ddir, dmsg);
03678    obj = ast_odbc_request_obj(odbc_database, 0);
03679    if (obj) {
03680       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03681       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03682       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);
03683       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03684       if (!stmt)
03685          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03686       else
03687          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03688       ast_odbc_release_obj(obj);
03689    } else
03690       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03691    return;  
03692 }
03693 
03694 struct insert_data {
03695    char *sql;
03696    const char *dir;
03697    const char *msgnums;
03698    void *data;
03699    SQLLEN datalen;
03700    SQLLEN indlen;
03701    const char *context;
03702    const char *macrocontext;
03703    const char *callerid;
03704    const char *origtime;
03705    const char *duration;
03706    const char *mailboxuser;
03707    const char *mailboxcontext;
03708    const char *category;
03709    const char *flag;
03710 };
03711 
03712 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03713 {
03714    struct insert_data *data = vdata;
03715    int res;
03716    SQLHSTMT stmt;
03717 
03718    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03719    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03720       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03721       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03722       return NULL;
03723    }
03724 
03725    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03726    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03727    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03728    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03729    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03730    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03731    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03732    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03733    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03734    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03735    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03736    if (!ast_strlen_zero(data->category)) {
03737       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03738    }
03739    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03740    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03741       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03742       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03743       return NULL;
03744    }
03745 
03746    return stmt;
03747 }
03748 
03749 /*!
03750  * \brief Stores a voicemail into the database.
03751  * \param dir the folder the mailbox folder to store the message.
03752  * \param mailboxuser the user owning the mailbox folder.
03753  * \param mailboxcontext
03754  * \param msgnum the message index for the message to be stored.
03755  *
03756  * This method is used when mailboxes are stored in an ODBC back end.
03757  * The message sound file and information file is looked up on the file system. 
03758  * A SQL query is invoked to store the message into the (MySQL) database.
03759  *
03760  * \return the zero on success -1 on error.
03761  */
03762 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03763 {
03764    int res = 0;
03765    int fd = -1;
03766    void *fdm = MAP_FAILED;
03767    size_t fdlen = -1;
03768    SQLHSTMT stmt;
03769    char sql[PATH_MAX];
03770    char msgnums[20];
03771    char fn[PATH_MAX];
03772    char full_fn[PATH_MAX];
03773    char fmt[80]="";
03774    char *c;
03775    struct ast_config *cfg = NULL;
03776    struct odbc_obj *obj;
03777    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03778       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03779    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03780 
03781    delete_file(dir, msgnum);
03782    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03783       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03784       return -1;
03785    }
03786 
03787    do {
03788       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03789       c = strchr(fmt, '|');
03790       if (c)
03791          *c = '\0';
03792       if (!strcasecmp(fmt, "wav49"))
03793          strcpy(fmt, "WAV");
03794       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03795       if (msgnum > -1)
03796          make_file(fn, sizeof(fn), dir, msgnum);
03797       else
03798          ast_copy_string(fn, dir, sizeof(fn));
03799       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03800       cfg = ast_config_load(full_fn, config_flags);
03801       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03802       fd = open(full_fn, O_RDWR);
03803       if (fd < 0) {
03804          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03805          res = -1;
03806          break;
03807       }
03808       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03809          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03810             idata.context = "";
03811          }
03812          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03813             idata.macrocontext = "";
03814          }
03815          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03816             idata.callerid = "";
03817          }
03818          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03819             idata.origtime = "";
03820          }
03821          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03822             idata.duration = "";
03823          }
03824          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03825             idata.category = "";
03826          }
03827          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03828             idata.flag = "";
03829          }
03830       }
03831       fdlen = lseek(fd, 0, SEEK_END);
03832       lseek(fd, 0, SEEK_SET);
03833       printf("Length is %zd\n", fdlen);
03834       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03835       if (fdm == MAP_FAILED) {
03836          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03837          res = -1;
03838          break;
03839       } 
03840       idata.data = fdm;
03841       idata.datalen = idata.indlen = fdlen;
03842 
03843       if (!ast_strlen_zero(idata.category)) 
03844          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03845       else
03846          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03847 
03848       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03849          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03850       } else {
03851          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03852          res = -1;
03853       }
03854    } while (0);
03855    if (obj) {
03856       ast_odbc_release_obj(obj);
03857    }
03858    if (cfg)
03859       ast_config_destroy(cfg);
03860    if (fdm != MAP_FAILED)
03861       munmap(fdm, fdlen);
03862    if (fd > -1)
03863       close(fd);
03864    return res;
03865 }
03866 
03867 /*!
03868  * \brief Renames a message in a mailbox folder.
03869  * \param sdir The folder of the message to be renamed.
03870  * \param smsg The index of the message to be renamed.
03871  * \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.
03872  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03873  * \param ddir The destination folder for the message to be renamed into
03874  * \param dmsg The destination message for the message to be renamed.
03875  *
03876  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03877  * 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.
03878  * 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.
03879  */
03880 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03881 {
03882    SQLHSTMT stmt;
03883    char sql[PATH_MAX];
03884    char msgnums[20];
03885    char msgnumd[20];
03886    struct odbc_obj *obj;
03887    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03888    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03889 
03890    delete_file(ddir, dmsg);
03891    obj = ast_odbc_request_obj(odbc_database, 0);
03892    if (obj) {
03893       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03894       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03895       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03896       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03897       if (!stmt)
03898          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03899       else
03900          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03901       ast_odbc_release_obj(obj);
03902    } else
03903       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03904    return;  
03905 }
03906 
03907 /*!
03908  * \brief Removes a voicemail message file.
03909  * \param dir the path to the message file.
03910  * \param msgnum the unique number for the message within the mailbox.
03911  *
03912  * Removes the message content file and the information file.
03913  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03914  * Typical use is to clean up after a RETRIEVE operation. 
03915  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03916  * \return zero on success, -1 on error.
03917  */
03918 static int remove_file(char *dir, int msgnum)
03919 {
03920    char fn[PATH_MAX];
03921    char full_fn[PATH_MAX];
03922    char msgnums[80];
03923    
03924    if (msgnum > -1) {
03925       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03926       make_file(fn, sizeof(fn), dir, msgnum);
03927    } else
03928       ast_copy_string(fn, dir, sizeof(fn));
03929    ast_filedelete(fn, NULL);  
03930    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03931    unlink(full_fn);
03932    return 0;
03933 }
03934 #else
03935 #ifndef IMAP_STORAGE
03936 /*!
03937  * \brief Find all .txt files - even if they are not in sequence from 0000.
03938  * \param vmu
03939  * \param dir
03940  *
03941  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03942  *
03943  * \return the count of messages, zero or more.
03944  */
03945 static int count_messages(struct ast_vm_user *vmu, char *dir)
03946 {
03947 
03948    int vmcount = 0;
03949    DIR *vmdir = NULL;
03950    struct dirent *vment = NULL;
03951 
03952    if (vm_lock_path(dir))
03953       return ERROR_LOCK_PATH;
03954 
03955    if ((vmdir = opendir(dir))) {
03956       while ((vment = readdir(vmdir))) {
03957          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03958             vmcount++;
03959          }
03960       }
03961       closedir(vmdir);
03962    }
03963    ast_unlock_path(dir);
03964    
03965    return vmcount;
03966 }
03967 
03968 /*!
03969  * \brief Renames a message in a mailbox folder.
03970  * \param sfn The path to the mailbox information and data file to be renamed.
03971  * \param dfn The path for where the message data and information files will be renamed to.
03972  *
03973  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03974  */
03975 static void rename_file(char *sfn, char *dfn)
03976 {
03977    char stxt[PATH_MAX];
03978    char dtxt[PATH_MAX];
03979    ast_filerename(sfn, dfn, NULL);
03980    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03981    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03982    if (ast_check_realtime("voicemail_data")) {
03983       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03984    }
03985    rename(stxt, dtxt);
03986 }
03987 
03988 /*! 
03989  * \brief Determines the highest message number in use for a given user and mailbox folder.
03990  * \param vmu 
03991  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03992  *
03993  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03994  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03995  *
03996  * \note Should always be called with a lock already set on dir.
03997  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03998  */
03999 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04000 {
04001    int x;
04002    unsigned char map[MAXMSGLIMIT] = "";
04003    DIR *msgdir;
04004    struct dirent *msgdirent;
04005    int msgdirint;
04006    char extension[4];
04007    int stopcount = 0;
04008 
04009    /* Reading the entire directory into a file map scales better than
04010     * doing a stat repeatedly on a predicted sequence.  I suspect this
04011     * is partially due to stat(2) internally doing a readdir(2) itself to
04012     * find each file. */
04013    if (!(msgdir = opendir(dir))) {
04014       return -1;
04015    }
04016 
04017    while ((msgdirent = readdir(msgdir))) {
04018       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04019          map[msgdirint] = 1;
04020          stopcount++;
04021          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04022       }
04023    }
04024    closedir(msgdir);
04025 
04026    for (x = 0; x < vmu->maxmsg; x++) {
04027       if (map[x] == 1) {
04028          stopcount--;
04029       } else if (map[x] == 0 && !stopcount) {
04030          break;
04031       }
04032    }
04033 
04034    return x - 1;
04035 }
04036 
04037 #endif /* #ifndef IMAP_STORAGE */
04038 #endif /* #else of #ifdef ODBC_STORAGE */
04039 #ifndef IMAP_STORAGE
04040 /*!
04041  * \brief Utility function to copy a file.
04042  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04043  * \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.
04044  *
04045  * 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.
04046  * The copy operation copies up to 4096 bytes at once.
04047  *
04048  * \return zero on success, -1 on error.
04049  */
04050 static int copy(char *infile, char *outfile)
04051 {
04052    int ifd;
04053    int ofd;
04054    int res;
04055    int len;
04056    char buf[4096];
04057 
04058 #ifdef HARDLINK_WHEN_POSSIBLE
04059    /* Hard link if possible; saves disk space & is faster */
04060    if (link(infile, outfile)) {
04061 #endif
04062       if ((ifd = open(infile, O_RDONLY)) < 0) {
04063          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04064          return -1;
04065       }
04066       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04067          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04068          close(ifd);
04069          return -1;
04070       }
04071       do {
04072          len = read(ifd, buf, sizeof(buf));
04073          if (len < 0) {
04074             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04075             close(ifd);
04076             close(ofd);
04077             unlink(outfile);
04078          }
04079          if (len) {
04080             res = write(ofd, buf, len);
04081             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04082                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04083                close(ifd);
04084                close(ofd);
04085                unlink(outfile);
04086             }
04087          }
04088       } while (len);
04089       close(ifd);
04090       close(ofd);
04091       return 0;
04092 #ifdef HARDLINK_WHEN_POSSIBLE
04093    } else {
04094       /* Hard link succeeded */
04095       return 0;
04096    }
04097 #endif
04098 }
04099 
04100 /*!
04101  * \brief Copies a voicemail information (envelope) file.
04102  * \param frompath
04103  * \param topath 
04104  *
04105  * Every voicemail has the data (.wav) file, and the information file.
04106  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04107  * This is used by the COPY macro when not using IMAP storage.
04108  */
04109 static void copy_plain_file(char *frompath, char *topath)
04110 {
04111    char frompath2[PATH_MAX], topath2[PATH_MAX];
04112    struct ast_variable *tmp,*var = NULL;
04113    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04114    ast_filecopy(frompath, topath, NULL);
04115    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04116    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04117    if (ast_check_realtime("voicemail_data")) {
04118       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04119       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04120       for (tmp = var; tmp; tmp = tmp->next) {
04121          if (!strcasecmp(tmp->name, "origmailbox")) {
04122             origmailbox = tmp->value;
04123          } else if (!strcasecmp(tmp->name, "context")) {
04124             context = tmp->value;
04125          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04126             macrocontext = tmp->value;
04127          } else if (!strcasecmp(tmp->name, "exten")) {
04128             exten = tmp->value;
04129          } else if (!strcasecmp(tmp->name, "priority")) {
04130             priority = tmp->value;
04131          } else if (!strcasecmp(tmp->name, "callerchan")) {
04132             callerchan = tmp->value;
04133          } else if (!strcasecmp(tmp->name, "callerid")) {
04134             callerid = tmp->value;
04135          } else if (!strcasecmp(tmp->name, "origdate")) {
04136             origdate = tmp->value;
04137          } else if (!strcasecmp(tmp->name, "origtime")) {
04138             origtime = tmp->value;
04139          } else if (!strcasecmp(tmp->name, "category")) {
04140             category = tmp->value;
04141          } else if (!strcasecmp(tmp->name, "duration")) {
04142             duration = tmp->value;
04143          }
04144       }
04145       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);
04146    }
04147    copy(frompath2, topath2);
04148    ast_variables_destroy(var);
04149 }
04150 #endif
04151 
04152 /*! 
04153  * \brief Removes the voicemail sound and information file.
04154  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04155  *
04156  * This is used by the DELETE macro when voicemails are stored on the file system.
04157  *
04158  * \return zero on success, -1 on error.
04159  */
04160 static int vm_delete(char *file)
04161 {
04162    char *txt;
04163    int txtsize = 0;
04164 
04165    txtsize = (strlen(file) + 5)*sizeof(char);
04166    txt = alloca(txtsize);
04167    /* Sprintf here would safe because we alloca'd exactly the right length,
04168     * but trying to eliminate all sprintf's anyhow
04169     */
04170    if (ast_check_realtime("voicemail_data")) {
04171       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04172    }
04173    snprintf(txt, txtsize, "%s.txt", file);
04174    unlink(txt);
04175    return ast_filedelete(file, NULL);
04176 }
04177 
04178 /*!
04179  * \brief utility used by inchar(), for base_encode()
04180  */
04181 static int inbuf(struct baseio *bio, FILE *fi)
04182 {
04183    int l;
04184 
04185    if (bio->ateof)
04186       return 0;
04187 
04188    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04189       if (ferror(fi))
04190          return -1;
04191 
04192       bio->ateof = 1;
04193       return 0;
04194    }
04195 
04196    bio->iolen = l;
04197    bio->iocp = 0;
04198 
04199    return 1;
04200 }
04201 
04202 /*!
04203  * \brief utility used by base_encode()
04204  */
04205 static int inchar(struct baseio *bio, FILE *fi)
04206 {
04207    if (bio->iocp>=bio->iolen) {
04208       if (!inbuf(bio, fi))
04209          return EOF;
04210    }
04211 
04212    return bio->iobuf[bio->iocp++];
04213 }
04214 
04215 /*!
04216  * \brief utility used by base_encode()
04217  */
04218 static int ochar(struct baseio *bio, int c, FILE *so)
04219 {
04220    if (bio->linelength >= BASELINELEN) {
04221       if (fputs(ENDL, so) == EOF) {
04222          return -1;
04223       }
04224 
04225       bio->linelength = 0;
04226    }
04227 
04228    if (putc(((unsigned char) c), so) == EOF) {
04229       return -1;
04230    }
04231 
04232    bio->linelength++;
04233 
04234    return 1;
04235 }
04236 
04237 /*!
04238  * \brief Performs a base 64 encode algorithm on the contents of a File
04239  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04240  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04241  *
04242  * 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 ?
04243  *
04244  * \return zero on success, -1 on error.
04245  */
04246 static int base_encode(char *filename, FILE *so)
04247 {
04248    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04249       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04250       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04251       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04252    int i, hiteof = 0;
04253    FILE *fi;
04254    struct baseio bio;
04255 
04256    memset(&bio, 0, sizeof(bio));
04257    bio.iocp = BASEMAXINLINE;
04258 
04259    if (!(fi = fopen(filename, "rb"))) {
04260       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04261       return -1;
04262    }
04263 
04264    while (!hiteof){
04265       unsigned char igroup[3], ogroup[4];
04266       int c, n;
04267 
04268       memset(igroup, 0, sizeof(igroup));
04269 
04270       for (n = 0; n < 3; n++) {
04271          if ((c = inchar(&bio, fi)) == EOF) {
04272             hiteof = 1;
04273             break;
04274          }
04275 
04276          igroup[n] = (unsigned char) c;
04277       }
04278 
04279       if (n > 0) {
04280          ogroup[0]= dtable[igroup[0] >> 2];
04281          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04282          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04283          ogroup[3]= dtable[igroup[2] & 0x3F];
04284 
04285          if (n < 3) {
04286             ogroup[3] = '=';
04287 
04288             if (n < 2)
04289                ogroup[2] = '=';
04290          }
04291 
04292          for (i = 0; i < 4; i++)
04293             ochar(&bio, ogroup[i], so);
04294       }
04295    }
04296 
04297    fclose(fi);
04298    
04299    if (fputs(ENDL, so) == EOF) {
04300       return 0;
04301    }
04302 
04303    return 1;
04304 }
04305 
04306 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)
04307 {
04308    char callerid[256];
04309    char num[12];
04310    char fromdir[256], fromfile[256];
04311    struct ast_config *msg_cfg;
04312    const char *origcallerid, *origtime;
04313    char origcidname[80], origcidnum[80], origdate[80];
04314    int inttime;
04315    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04316 
04317    /* Prepare variables for substitution in email body and subject */
04318    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04319    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04320    snprintf(num, sizeof(num), "%d", msgnum);
04321    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04322    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04323    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04324    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04325       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04326    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04327    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04328    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04329    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04330    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04331 
04332    /* Retrieve info from VM attribute file */
04333    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04334    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04335    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04336       strcat(fromfile, ".txt");
04337    }
04338    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04339       if (option_debug > 0) {
04340          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04341       }
04342       return;
04343    }
04344 
04345    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04346       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04347       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04348       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04349       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04350    }
04351 
04352    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04353       struct timeval tv = { inttime, };
04354       struct ast_tm tm;
04355       ast_localtime(&tv, &tm, NULL);
04356       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04357       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04358    }
04359    ast_config_destroy(msg_cfg);
04360 }
04361 
04362 /*!
04363  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04364  * \param from The string to work with.
04365  * \param buf The buffer into which to write the modified quoted string.
04366  * \param maxlen Always zero, but see \see ast_str
04367  * 
04368  * \return The destination string with quotes wrapped on it (the to field).
04369  */
04370 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04371 {
04372    const char *ptr;
04373 
04374    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04375    ast_str_set(buf, maxlen, "\"");
04376    for (ptr = from; *ptr; ptr++) {
04377       if (*ptr == '"' || *ptr == '\\') {
04378          ast_str_append(buf, maxlen, "\\%c", *ptr);
04379       } else {
04380          ast_str_append(buf, maxlen, "%c", *ptr);
04381       }
04382    }
04383    ast_str_append(buf, maxlen, "\"");
04384 
04385    return ast_str_buffer(*buf);
04386 }
04387 
04388 /*! \brief
04389  * fill in *tm for current time according to the proper timezone, if any.
04390  * \return tm so it can be used as a function argument.
04391  */
04392 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04393 {
04394    const struct vm_zone *z = NULL;
04395    struct timeval t = ast_tvnow();
04396 
04397    /* Does this user have a timezone specified? */
04398    if (!ast_strlen_zero(vmu->zonetag)) {
04399       /* Find the zone in the list */
04400       AST_LIST_LOCK(&zones);
04401       AST_LIST_TRAVERSE(&zones, z, list) {
04402          if (!strcmp(z->name, vmu->zonetag))
04403             break;
04404       }
04405       AST_LIST_UNLOCK(&zones);
04406    }
04407    ast_localtime(&t, tm, z ? z->timezone : NULL);
04408    return tm;
04409 }
04410 
04411 /*!\brief Check if the string would need encoding within the MIME standard, to
04412  * avoid confusing certain mail software that expects messages to be 7-bit
04413  * clean.
04414  */
04415 static int check_mime(const char *str)
04416 {
04417    for (; *str; str++) {
04418       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04419          return 1;
04420       }
04421    }
04422    return 0;
04423 }
04424 
04425 /*!\brief Encode a string according to the MIME rules for encoding strings
04426  * that are not 7-bit clean or contain control characters.
04427  *
04428  * Additionally, if the encoded string would exceed the MIME limit of 76
04429  * characters per line, then the encoding will be broken up into multiple
04430  * sections, separated by a space character, in order to facilitate
04431  * breaking up the associated header across multiple lines.
04432  *
04433  * \param end An expandable buffer for holding the result
04434  * \param maxlen Always zero, but see \see ast_str
04435  * \param start A string to be encoded
04436  * \param preamble The length of the first line already used for this string,
04437  * to ensure that each line maintains a maximum length of 76 chars.
04438  * \param postamble the length of any additional characters appended to the
04439  * line, used to ensure proper field wrapping.
04440  * \retval The encoded string.
04441  */
04442 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04443 {
04444    struct ast_str *tmp = ast_str_alloca(80);
04445    int first_section = 1;
04446 
04447    ast_str_reset(*end);
04448    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04449    for (; *start; start++) {
04450       int need_encoding = 0;
04451       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04452          need_encoding = 1;
04453       }
04454       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04455          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04456          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04457          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04458          /* Start new line */
04459          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04460          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04461          first_section = 0;
04462       }
04463       if (need_encoding && *start == ' ') {
04464          ast_str_append(&tmp, -1, "_");
04465       } else if (need_encoding) {
04466          ast_str_append(&tmp, -1, "=%hhX", *start);
04467       } else {
04468          ast_str_append(&tmp, -1, "%c", *start);
04469       }
04470    }
04471    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04472    return ast_str_buffer(*end);
04473 }
04474 
04475 /*!
04476  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04477  * \param p The output file to generate the email contents into.
04478  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04479  * \param vmu The voicemail user who is sending the voicemail.
04480  * \param msgnum The message index in the mailbox folder.
04481  * \param context 
04482  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04483  * \param fromfolder
04484  * \param cidnum The caller ID number.
04485  * \param cidname The caller ID name.
04486  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04487  * \param attach2 
04488  * \param format The message sound file format. i.e. .wav
04489  * \param duration The time of the message content, in seconds.
04490  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04491  * \param chan
04492  * \param category
04493  * \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.
04494  * \param flag
04495  *
04496  * 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.
04497  */
04498 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)
04499 {
04500    char date[256];
04501    char host[MAXHOSTNAMELEN] = "";
04502    char who[256];
04503    char bound[256];
04504    char dur[256];
04505    struct ast_tm tm;
04506    char enc_cidnum[256] = "", enc_cidname[256] = "";
04507    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04508    char *greeting_attachment; 
04509    char filename[256];
04510 
04511    if (!str1 || !str2) {
04512       ast_free(str1);
04513       ast_free(str2);
04514       return;
04515    }
04516 
04517    if (cidnum) {
04518       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04519    }
04520    if (cidname) {
04521       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04522    }
04523    gethostname(host, sizeof(host) - 1);
04524 
04525    if (strchr(srcemail, '@')) {
04526       ast_copy_string(who, srcemail, sizeof(who));
04527    } else {
04528       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04529    }
04530 
04531    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04532    if (greeting_attachment) {
04533       *greeting_attachment++ = '\0';
04534    }
04535 
04536    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04537    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04538    fprintf(p, "Date: %s" ENDL, date);
04539 
04540    /* Set date format for voicemail mail */
04541    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04542 
04543    if (!ast_strlen_zero(fromstring)) {
04544       struct ast_channel *ast;
04545       if ((ast = ast_dummy_channel_alloc())) {
04546          char *ptr;
04547          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04548          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04549 
04550          if (check_mime(ast_str_buffer(str1))) {
04551             int first_line = 1;
04552             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04553             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04554                *ptr = '\0';
04555                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04556                first_line = 0;
04557                /* Substring is smaller, so this will never grow */
04558                ast_str_set(&str2, 0, "%s", ptr + 1);
04559             }
04560             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04561          } else {
04562             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04563          }
04564          ast = ast_channel_unref(ast);
04565       } else {
04566          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04567       }
04568    } else {
04569       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04570    }
04571 
04572    if (check_mime(vmu->fullname)) {
04573       int first_line = 1;
04574       char *ptr;
04575       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04576       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04577          *ptr = '\0';
04578          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04579          first_line = 0;
04580          /* Substring is smaller, so this will never grow */
04581          ast_str_set(&str2, 0, "%s", ptr + 1);
04582       }
04583       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04584    } else {
04585       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04586    }
04587 
04588    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04589       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04590       struct ast_channel *ast;
04591       if ((ast = ast_dummy_channel_alloc())) {
04592          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04593          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04594          if (check_mime(ast_str_buffer(str1))) {
04595             int first_line = 1;
04596             char *ptr;
04597             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04598             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04599                *ptr = '\0';
04600                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04601                first_line = 0;
04602                /* Substring is smaller, so this will never grow */
04603                ast_str_set(&str2, 0, "%s", ptr + 1);
04604             }
04605             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04606          } else {
04607             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04608          }
04609          ast = ast_channel_unref(ast);
04610       } else {
04611          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04612       }
04613    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04614       if (ast_strlen_zero(flag)) {
04615          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04616       } else {
04617          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04618       }
04619    } else {
04620       if (ast_strlen_zero(flag)) {
04621          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04622       } else {
04623          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04624       }
04625    }
04626 
04627    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04628       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04629    if (imap) {
04630       /* additional information needed for IMAP searching */
04631       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04632       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04633       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04634       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04635 #ifdef IMAP_STORAGE
04636       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04637 #else
04638       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04639 #endif
04640       /* flag added for Urgent */
04641       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04642       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04643       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04644       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04645       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04646       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04647       if (!ast_strlen_zero(category)) {
04648          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04649       } else {
04650          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04651       }
04652       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04653       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04654       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04655    }
04656    if (!ast_strlen_zero(cidnum)) {
04657       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04658    }
04659    if (!ast_strlen_zero(cidname)) {
04660       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04661    }
04662    fprintf(p, "MIME-Version: 1.0" ENDL);
04663    if (attach_user_voicemail) {
04664       /* Something unique. */
04665       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04666          (int) getpid(), (unsigned int) ast_random());
04667 
04668       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04669       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04670       fprintf(p, "--%s" ENDL, bound);
04671    }
04672    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04673    if (emailbody || vmu->emailbody) {
04674       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04675       struct ast_channel *ast;
04676       if ((ast = ast_dummy_channel_alloc())) {
04677          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04678          ast_str_substitute_variables(&str1, 0, ast, e_body);
04679 #ifdef IMAP_STORAGE
04680             {
04681                /* Convert body to native line terminators for IMAP backend */
04682                char *line = ast_str_buffer(str1), *next;
04683                do {
04684                   /* Terminate line before outputting it to the file */
04685                   if ((next = strchr(line, '\n'))) {
04686                      *next++ = '\0';
04687                   }
04688                   fprintf(p, "%s" ENDL, line);
04689                   line = next;
04690                } while (!ast_strlen_zero(line));
04691             }
04692 #else
04693          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04694 #endif
04695          ast = ast_channel_unref(ast);
04696       } else {
04697          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04698       }
04699    } else if (msgnum > -1) {
04700       if (strcmp(vmu->mailbox, mailbox)) {
04701          /* Forwarded type */
04702          struct ast_config *msg_cfg;
04703          const char *v;
04704          int inttime;
04705          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04706          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04707          /* Retrieve info from VM attribute file */
04708          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04709          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04710          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04711             strcat(fromfile, ".txt");
04712          }
04713          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04714             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04715                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04716             }
04717 
04718             /* You might be tempted to do origdate, except that a) it's in the wrong
04719              * format, and b) it's missing for IMAP recordings. */
04720             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04721                struct timeval tv = { inttime, };
04722                struct ast_tm tm;
04723                ast_localtime(&tv, &tm, NULL);
04724                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04725             }
04726             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04727                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04728                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04729                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04730                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04731                date, origcallerid, origdate);
04732             ast_config_destroy(msg_cfg);
04733          } else {
04734             goto plain_message;
04735          }
04736       } else {
04737 plain_message:
04738          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04739             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04740             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04741             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04742             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04743       }
04744    } else {
04745       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04746             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04747    }
04748 
04749    if (imap || attach_user_voicemail) {
04750       if (!ast_strlen_zero(attach2)) {
04751          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04752          ast_debug(5, "creating second attachment filename %s\n", filename);
04753          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04754          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04755          ast_debug(5, "creating attachment filename %s\n", filename);
04756          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04757       } else {
04758          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04759          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04760          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04761       }
04762    }
04763    ast_free(str1);
04764    ast_free(str2);
04765 }
04766 
04767 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)
04768 {
04769    char tmpdir[256], newtmp[256];
04770    char fname[256];
04771    char tmpcmd[256];
04772    int tmpfd = -1;
04773    int soxstatus = 0;
04774 
04775    /* Eww. We want formats to tell us their own MIME type */
04776    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04777 
04778    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04779       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04780       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04781       tmpfd = mkstemp(newtmp);
04782       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04783       ast_debug(3, "newtmp: %s\n", newtmp);
04784       if (tmpfd > -1) {
04785          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04786          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04787             attach = newtmp;
04788             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04789          } else {
04790             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04791                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04792             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04793          }
04794       }
04795    }
04796    fprintf(p, "--%s" ENDL, bound);
04797    if (msgnum > -1)
04798       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04799    else
04800       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04801    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04802    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04803    if (msgnum > -1)
04804       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04805    else
04806       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04807    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04808    base_encode(fname, p);
04809    if (last)
04810       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04811    if (tmpfd > -1) {
04812       if (soxstatus == 0) {
04813          unlink(fname);
04814       }
04815       close(tmpfd);
04816       unlink(newtmp);
04817    }
04818    return 0;
04819 }
04820 
04821 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)
04822 {
04823    FILE *p = NULL;
04824    char tmp[80] = "/tmp/astmail-XXXXXX";
04825    char tmp2[256];
04826    char *stringp;
04827 
04828    if (vmu && ast_strlen_zero(vmu->email)) {
04829       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04830       return(0);
04831    }
04832 
04833    /* Mail only the first format */
04834    format = ast_strdupa(format);
04835    stringp = format;
04836    strsep(&stringp, "|");
04837 
04838    if (!strcmp(format, "wav49"))
04839       format = "WAV";
04840    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));
04841    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04842       command hangs */
04843    if ((p = vm_mkftemp(tmp)) == NULL) {
04844       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04845       return -1;
04846    } else {
04847       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04848       fclose(p);
04849       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04850       ast_safe_system(tmp2);
04851       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04852    }
04853    return 0;
04854 }
04855 
04856 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)
04857 {
04858    char enc_cidnum[256], enc_cidname[256];
04859    char date[256];
04860    char host[MAXHOSTNAMELEN] = "";
04861    char who[256];
04862    char dur[PATH_MAX];
04863    char tmp[80] = "/tmp/astmail-XXXXXX";
04864    char tmp2[PATH_MAX];
04865    struct ast_tm tm;
04866    FILE *p;
04867    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04868 
04869    if (!str1 || !str2) {
04870       ast_free(str1);
04871       ast_free(str2);
04872       return -1;
04873    }
04874 
04875    if (cidnum) {
04876       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04877    }
04878    if (cidname) {
04879       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04880    }
04881 
04882    if ((p = vm_mkftemp(tmp)) == NULL) {
04883       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04884       ast_free(str1);
04885       ast_free(str2);
04886       return -1;
04887    }
04888    gethostname(host, sizeof(host)-1);
04889    if (strchr(srcemail, '@')) {
04890       ast_copy_string(who, srcemail, sizeof(who));
04891    } else {
04892       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04893    }
04894    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04895    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04896    fprintf(p, "Date: %s\n", date);
04897 
04898    /* Reformat for custom pager format */
04899    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04900 
04901    if (!ast_strlen_zero(pagerfromstring)) {
04902       struct ast_channel *ast;
04903       if ((ast = ast_dummy_channel_alloc())) {
04904          char *ptr;
04905          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04906          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04907 
04908          if (check_mime(ast_str_buffer(str1))) {
04909             int first_line = 1;
04910             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04911             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04912                *ptr = '\0';
04913                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04914                first_line = 0;
04915                /* Substring is smaller, so this will never grow */
04916                ast_str_set(&str2, 0, "%s", ptr + 1);
04917             }
04918             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04919          } else {
04920             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04921          }
04922          ast = ast_channel_unref(ast);
04923       } else {
04924          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04925       }
04926    } else {
04927       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04928    }
04929 
04930    if (check_mime(vmu->fullname)) {
04931       int first_line = 1;
04932       char *ptr;
04933       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04934       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04935          *ptr = '\0';
04936          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04937          first_line = 0;
04938          /* Substring is smaller, so this will never grow */
04939          ast_str_set(&str2, 0, "%s", ptr + 1);
04940       }
04941       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04942    } else {
04943       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04944    }
04945 
04946    if (!ast_strlen_zero(pagersubject)) {
04947       struct ast_channel *ast;
04948       if ((ast = ast_dummy_channel_alloc())) {
04949          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04950          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04951          if (check_mime(ast_str_buffer(str1))) {
04952             int first_line = 1;
04953             char *ptr;
04954             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04955             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04956                *ptr = '\0';
04957                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04958                first_line = 0;
04959                /* Substring is smaller, so this will never grow */
04960                ast_str_set(&str2, 0, "%s", ptr + 1);
04961             }
04962             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04963          } else {
04964             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04965          }
04966          ast = ast_channel_unref(ast);
04967       } else {
04968          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04969       }
04970    } else {
04971       if (ast_strlen_zero(flag)) {
04972          fprintf(p, "Subject: New VM\n\n");
04973       } else {
04974          fprintf(p, "Subject: New %s VM\n\n", flag);
04975       }
04976    }
04977 
04978    if (pagerbody) {
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, pagerbody);
04983          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04984          ast = ast_channel_unref(ast);
04985       } else {
04986          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04987       }
04988    } else {
04989       fprintf(p, "New %s long %s msg in box %s\n"
04990             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04991    }
04992 
04993    fclose(p);
04994    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04995    ast_safe_system(tmp2);
04996    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04997    ast_free(str1);
04998    ast_free(str2);
04999    return 0;
05000 }
05001 
05002 /*!
05003  * \brief Gets the current date and time, as formatted string.
05004  * \param s The buffer to hold the output formatted date.
05005  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05006  * 
05007  * The date format string used is "%a %b %e %r UTC %Y".
05008  * 
05009  * \return zero on success, -1 on error.
05010  */
05011 static int get_date(char *s, int len)
05012 {
05013    struct ast_tm tm;
05014    struct timeval t = ast_tvnow();
05015    
05016    ast_localtime(&t, &tm, "UTC");
05017 
05018    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05019 }
05020 
05021 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05022 {
05023    int res;
05024    char fn[PATH_MAX];
05025    char dest[PATH_MAX];
05026 
05027    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05028 
05029    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05030       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05031       return -1;
05032    }
05033 
05034    RETRIEVE(fn, -1, ext, context);
05035    if (ast_fileexists(fn, NULL, NULL) > 0) {
05036       res = ast_stream_and_wait(chan, fn, ecodes);
05037       if (res) {
05038          DISPOSE(fn, -1);
05039          return res;
05040       }
05041    } else {
05042       /* Dispose just in case */
05043       DISPOSE(fn, -1);
05044       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05045       if (res)
05046          return res;
05047       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05048       if (res)
05049          return res;
05050    }
05051    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05052    return res;
05053 }
05054 
05055 static void free_zone(struct vm_zone *z)
05056 {
05057    ast_free(z);
05058 }
05059 
05060 #ifdef ODBC_STORAGE
05061 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05062 {
05063    int x = -1;
05064    int res;
05065    SQLHSTMT stmt = NULL;
05066    char sql[PATH_MAX];
05067    char rowdata[20];
05068    char tmp[PATH_MAX] = "";
05069    struct odbc_obj *obj = NULL;
05070    char *context;
05071    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05072 
05073    if (newmsgs)
05074       *newmsgs = 0;
05075    if (oldmsgs)
05076       *oldmsgs = 0;
05077    if (urgentmsgs)
05078       *urgentmsgs = 0;
05079 
05080    /* If no mailbox, return immediately */
05081    if (ast_strlen_zero(mailbox))
05082       return 0;
05083 
05084    ast_copy_string(tmp, mailbox, sizeof(tmp));
05085 
05086    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05087       int u, n, o;
05088       char *next, *remaining = tmp;
05089       while ((next = strsep(&remaining, " ,"))) {
05090          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05091             return -1;
05092          }
05093          if (urgentmsgs) {
05094             *urgentmsgs += u;
05095          }
05096          if (newmsgs) {
05097             *newmsgs += n;
05098          }
05099          if (oldmsgs) {
05100             *oldmsgs += o;
05101          }
05102       }
05103       return 0;
05104    }
05105 
05106    context = strchr(tmp, '@');
05107    if (context) {
05108       *context = '\0';
05109       context++;
05110    } else
05111       context = "default";
05112 
05113    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05114       do {
05115          if (newmsgs) {
05116             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05117             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05118                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05119                break;
05120             }
05121             res = SQLFetch(stmt);
05122             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05123                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05124                break;
05125             }
05126             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05127             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05128                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05129                break;
05130             }
05131             *newmsgs = atoi(rowdata);
05132             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05133          }
05134 
05135          if (oldmsgs) {
05136             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05137             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05138                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05139                break;
05140             }
05141             res = SQLFetch(stmt);
05142             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05143                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05144                break;
05145             }
05146             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05147             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05148                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05149                break;
05150             }
05151             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05152             *oldmsgs = atoi(rowdata);
05153          }
05154 
05155          if (urgentmsgs) {
05156             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05157             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05158                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05159                break;
05160             }
05161             res = SQLFetch(stmt);
05162             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05163                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05164                break;
05165             }
05166             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05167             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05168                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05169                break;
05170             }
05171             *urgentmsgs = atoi(rowdata);
05172          }
05173 
05174          x = 0;
05175       } while (0);
05176    } else {
05177       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05178    }
05179 
05180    if (stmt) {
05181       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05182    }
05183    if (obj) {
05184       ast_odbc_release_obj(obj);
05185    }
05186    return x;
05187 }
05188 
05189 /*!
05190  * \brief Gets the number of messages that exist in a mailbox folder.
05191  * \param context
05192  * \param mailbox
05193  * \param folder
05194  * 
05195  * This method is used when ODBC backend is used.
05196  * \return The number of messages in this mailbox folder (zero or more).
05197  */
05198 static int messagecount(const char *context, const char *mailbox, const char *folder)
05199 {
05200    struct odbc_obj *obj = NULL;
05201    int nummsgs = 0;
05202    int res;
05203    SQLHSTMT stmt = NULL;
05204    char sql[PATH_MAX];
05205    char rowdata[20];
05206    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05207    if (!folder)
05208       folder = "INBOX";
05209    /* If no mailbox, return immediately */
05210    if (ast_strlen_zero(mailbox))
05211       return 0;
05212 
05213    obj = ast_odbc_request_obj(odbc_database, 0);
05214    if (obj) {
05215       if (!strcmp(folder, "INBOX")) {
05216          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);
05217       } else {
05218          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05219       }
05220       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05221       if (!stmt) {
05222          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05223          goto yuck;
05224       }
05225       res = SQLFetch(stmt);
05226       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05227          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05228          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05229          goto yuck;
05230       }
05231       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05232       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05233          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05234          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05235          goto yuck;
05236       }
05237       nummsgs = atoi(rowdata);
05238       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05239    } else
05240       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05241 
05242 yuck:
05243    if (obj)
05244       ast_odbc_release_obj(obj);
05245    return nummsgs;
05246 }
05247 
05248 /** 
05249  * \brief Determines if the given folder has messages.
05250  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05251  * 
05252  * This function is used when the mailbox is stored in an ODBC back end.
05253  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05254  * \return 1 if the folder has one or more messages. zero otherwise.
05255  */
05256 static int has_voicemail(const char *mailbox, const char *folder)
05257 {
05258    char tmp[256], *tmp2 = tmp, *box, *context;
05259    ast_copy_string(tmp, mailbox, sizeof(tmp));
05260    while ((context = box = strsep(&tmp2, ",&"))) {
05261       strsep(&context, "@");
05262       if (ast_strlen_zero(context))
05263          context = "default";
05264       if (messagecount(context, box, folder))
05265          return 1;
05266    }
05267    return 0;
05268 }
05269 #endif
05270 #ifndef IMAP_STORAGE
05271 /*! 
05272  * \brief Copies a message from one mailbox to another.
05273  * \param chan
05274  * \param vmu
05275  * \param imbox
05276  * \param msgnum
05277  * \param duration
05278  * \param recip
05279  * \param fmt
05280  * \param dir
05281  * \param flag
05282  *
05283  * This is only used by file storage based mailboxes.
05284  *
05285  * \return zero on success, -1 on error.
05286  */
05287 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)
05288 {
05289    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05290    const char *frombox = mbox(vmu, imbox);
05291    const char *userfolder;
05292    int recipmsgnum;
05293    int res = 0;
05294 
05295    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05296 
05297    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05298       userfolder = "Urgent";
05299    } else {
05300       userfolder = "INBOX";
05301    }
05302 
05303    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05304 
05305    if (!dir)
05306       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05307    else
05308       ast_copy_string(fromdir, dir, sizeof(fromdir));
05309 
05310    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05311    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05312 
05313    if (vm_lock_path(todir))
05314       return ERROR_LOCK_PATH;
05315 
05316    recipmsgnum = last_message_index(recip, todir) + 1;
05317    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05318       make_file(topath, sizeof(topath), todir, recipmsgnum);
05319 #ifndef ODBC_STORAGE
05320       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05321          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05322       } else {
05323 #endif
05324          /* If we are prepending a message for ODBC, then the message already
05325           * exists in the database, but we want to force copying from the
05326           * filesystem (since only the FS contains the prepend). */
05327          copy_plain_file(frompath, topath);
05328          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05329          vm_delete(topath);
05330 #ifndef ODBC_STORAGE
05331       }
05332 #endif
05333    } else {
05334       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05335       res = -1;
05336    }
05337    ast_unlock_path(todir);
05338    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05339       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05340       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05341       flag);
05342    
05343    return res;
05344 }
05345 #endif
05346 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05347 
05348 static int messagecount(const char *context, const char *mailbox, const char *folder)
05349 {
05350    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05351 }
05352 
05353 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05354 {
05355    DIR *dir;
05356    struct dirent *de;
05357    char fn[256];
05358    int ret = 0;
05359 
05360    /* If no mailbox, return immediately */
05361    if (ast_strlen_zero(mailbox))
05362       return 0;
05363 
05364    if (ast_strlen_zero(folder))
05365       folder = "INBOX";
05366    if (ast_strlen_zero(context))
05367       context = "default";
05368 
05369    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05370 
05371    if (!(dir = opendir(fn)))
05372       return 0;
05373 
05374    while ((de = readdir(dir))) {
05375       if (!strncasecmp(de->d_name, "msg", 3)) {
05376          if (shortcircuit) {
05377             ret = 1;
05378             break;
05379          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05380             ret++;
05381          }
05382       }
05383    }
05384 
05385    closedir(dir);
05386 
05387    return ret;
05388 }
05389 
05390 /** 
05391  * \brief Determines if the given folder has messages.
05392  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05393  * \param folder the folder to look in
05394  *
05395  * This function is used when the mailbox is stored in a filesystem back end.
05396  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05397  * \return 1 if the folder has one or more messages. zero otherwise.
05398  */
05399 static int has_voicemail(const char *mailbox, const char *folder)
05400 {
05401    char tmp[256], *tmp2 = tmp, *box, *context;
05402    ast_copy_string(tmp, mailbox, sizeof(tmp));
05403    if (ast_strlen_zero(folder)) {
05404       folder = "INBOX";
05405    }
05406    while ((box = strsep(&tmp2, ",&"))) {
05407       if ((context = strchr(box, '@')))
05408          *context++ = '\0';
05409       else
05410          context = "default";
05411       if (__has_voicemail(context, box, folder, 1))
05412          return 1;
05413       /* If we are checking INBOX, we should check Urgent as well */
05414       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05415          return 1;
05416       }
05417    }
05418    return 0;
05419 }
05420 
05421 
05422 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05423 {
05424    char tmp[256];
05425    char *context;
05426 
05427    /* If no mailbox, return immediately */
05428    if (ast_strlen_zero(mailbox))
05429       return 0;
05430 
05431    if (newmsgs)
05432       *newmsgs = 0;
05433    if (oldmsgs)
05434       *oldmsgs = 0;
05435    if (urgentmsgs)
05436       *urgentmsgs = 0;
05437 
05438    if (strchr(mailbox, ',')) {
05439       int tmpnew, tmpold, tmpurgent;
05440       char *mb, *cur;
05441 
05442       ast_copy_string(tmp, mailbox, sizeof(tmp));
05443       mb = tmp;
05444       while ((cur = strsep(&mb, ", "))) {
05445          if (!ast_strlen_zero(cur)) {
05446             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05447                return -1;
05448             else {
05449                if (newmsgs)
05450                   *newmsgs += tmpnew; 
05451                if (oldmsgs)
05452                   *oldmsgs += tmpold;
05453                if (urgentmsgs)
05454                   *urgentmsgs += tmpurgent;
05455             }
05456          }
05457       }
05458       return 0;
05459    }
05460 
05461    ast_copy_string(tmp, mailbox, sizeof(tmp));
05462    
05463    if ((context = strchr(tmp, '@')))
05464       *context++ = '\0';
05465    else
05466       context = "default";
05467 
05468    if (newmsgs)
05469       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05470    if (oldmsgs)
05471       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05472    if (urgentmsgs)
05473       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05474 
05475    return 0;
05476 }
05477 
05478 #endif
05479 
05480 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05481 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05482 {
05483    int urgentmsgs = 0;
05484    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05485    if (newmsgs) {
05486       *newmsgs += urgentmsgs;
05487    }
05488    return res;
05489 }
05490 
05491 static void run_externnotify(char *context, char *extension, const char *flag)
05492 {
05493    char arguments[255];
05494    char ext_context[256] = "";
05495    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05496    struct ast_smdi_mwi_message *mwi_msg;
05497 
05498    if (!ast_strlen_zero(context))
05499       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05500    else
05501       ast_copy_string(ext_context, extension, sizeof(ext_context));
05502 
05503    if (smdi_iface) {
05504       if (ast_app_has_voicemail(ext_context, NULL)) 
05505          ast_smdi_mwi_set(smdi_iface, extension);
05506       else
05507          ast_smdi_mwi_unset(smdi_iface, extension);
05508 
05509       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05510          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05511          if (!strncmp(mwi_msg->cause, "INV", 3))
05512             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05513          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05514             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05515          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05516          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05517       } else {
05518          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05519       }
05520    }
05521 
05522    if (!ast_strlen_zero(externnotify)) {
05523       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05524          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05525       } else {
05526          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05527          ast_debug(1, "Executing %s\n", arguments);
05528          ast_safe_system(arguments);
05529       }
05530    }
05531 }
05532 
05533 /*!
05534  * \brief Variables used for saving a voicemail.
05535  *
05536  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05537  */
05538 struct leave_vm_options {
05539    unsigned int flags;
05540    signed char record_gain;
05541    char *exitcontext;
05542 };
05543 
05544 /*!
05545  * \brief Prompts the user and records a voicemail to a mailbox.
05546  * \param chan
05547  * \param ext
05548  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05549  * 
05550  * 
05551  * 
05552  * \return zero on success, -1 on error.
05553  */
05554 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05555 {
05556 #ifdef IMAP_STORAGE
05557    int newmsgs, oldmsgs;
05558 #else
05559    char urgdir[PATH_MAX];
05560 #endif
05561    char txtfile[PATH_MAX];
05562    char tmptxtfile[PATH_MAX];
05563    struct vm_state *vms = NULL;
05564    char callerid[256];
05565    FILE *txt;
05566    char date[256];
05567    int txtdes;
05568    int res = 0;
05569    int msgnum;
05570    int duration = 0;
05571    int sound_duration = 0;
05572    int ausemacro = 0;
05573    int ousemacro = 0;
05574    int ouseexten = 0;
05575    char tmpdur[16];
05576    char priority[16];
05577    char origtime[16];
05578    char dir[PATH_MAX];
05579    char tmpdir[PATH_MAX];
05580    char fn[PATH_MAX];
05581    char prefile[PATH_MAX] = "";
05582    char tempfile[PATH_MAX] = "";
05583    char ext_context[256] = "";
05584    char fmt[80];
05585    char *context;
05586    char ecodes[17] = "#";
05587    struct ast_str *tmp = ast_str_create(16);
05588    char *tmpptr;
05589    struct ast_vm_user *vmu;
05590    struct ast_vm_user svm;
05591    const char *category = NULL;
05592    const char *code;
05593    const char *alldtmf = "0123456789ABCD*#";
05594    char flag[80];
05595 
05596    if (!tmp) {
05597       return -1;
05598    }
05599 
05600    ast_str_set(&tmp, 0, "%s", ext);
05601    ext = ast_str_buffer(tmp);
05602    if ((context = strchr(ext, '@'))) {
05603       *context++ = '\0';
05604       tmpptr = strchr(context, '&');
05605    } else {
05606       tmpptr = strchr(ext, '&');
05607    }
05608 
05609    if (tmpptr)
05610       *tmpptr++ = '\0';
05611 
05612    ast_channel_lock(chan);
05613    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05614       category = ast_strdupa(category);
05615    }
05616    ast_channel_unlock(chan);
05617 
05618    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05619       ast_copy_string(flag, "Urgent", sizeof(flag));
05620    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05621       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05622    } else {
05623       flag[0] = '\0';
05624    }
05625 
05626    ast_debug(3, "Before find_user\n");
05627    if (!(vmu = find_user(&svm, context, ext))) {
05628       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05629       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05630       ast_free(tmp);
05631       return res;
05632    }
05633    /* Setup pre-file if appropriate */
05634    if (strcmp(vmu->context, "default"))
05635       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05636    else
05637       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05638 
05639    /* Set the path to the prefile. Will be one of 
05640       VM_SPOOL_DIRcontext/ext/busy
05641       VM_SPOOL_DIRcontext/ext/unavail
05642       Depending on the flag set in options.
05643    */
05644    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05645       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05646    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05647       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05648    }
05649    /* Set the path to the tmpfile as
05650       VM_SPOOL_DIR/context/ext/temp
05651       and attempt to create the folder structure.
05652    */
05653    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05654    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05655       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05656       ast_free(tmp);
05657       return -1;
05658    }
05659    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05660    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05661       ast_copy_string(prefile, tempfile, sizeof(prefile));
05662 
05663    DISPOSE(tempfile, -1);
05664    /* It's easier just to try to make it than to check for its existence */
05665 #ifndef IMAP_STORAGE
05666    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05667 #else
05668    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05669    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05670       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05671    }
05672 #endif
05673 
05674    /* Check current or macro-calling context for special extensions */
05675    if (ast_test_flag(vmu, VM_OPERATOR)) {
05676       if (!ast_strlen_zero(vmu->exit)) {
05677          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05678             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05679             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05680             ouseexten = 1;
05681          }
05682       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05683          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05684          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05685          ouseexten = 1;
05686       } else if (!ast_strlen_zero(chan->macrocontext)
05687          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05688             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05689          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05690          ousemacro = 1;
05691       }
05692    }
05693 
05694    if (!ast_strlen_zero(vmu->exit)) {
05695       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05696          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05697          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05698       }
05699    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05700       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05701       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05702    } else if (!ast_strlen_zero(chan->macrocontext)
05703       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05704          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05705       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05706       ausemacro = 1;
05707    }
05708 
05709    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05710       for (code = alldtmf; *code; code++) {
05711          char e[2] = "";
05712          e[0] = *code;
05713          if (strchr(ecodes, e[0]) == NULL
05714             && ast_canmatch_extension(chan, chan->context, e, 1,
05715                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05716             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05717          }
05718       }
05719    }
05720 
05721    /* Play the beginning intro if desired */
05722    if (!ast_strlen_zero(prefile)) {
05723 #ifdef ODBC_STORAGE
05724       int success = 
05725 #endif
05726          RETRIEVE(prefile, -1, ext, context);
05727       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05728          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05729             res = ast_waitstream(chan, ecodes);
05730 #ifdef ODBC_STORAGE
05731          if (success == -1) {
05732             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05733             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05734             store_file(prefile, vmu->mailbox, vmu->context, -1);
05735          }
05736 #endif
05737       } else {
05738          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05739          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05740       }
05741       DISPOSE(prefile, -1);
05742       if (res < 0) {
05743          ast_debug(1, "Hang up during prefile playback\n");
05744          free_user(vmu);
05745          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05746          ast_free(tmp);
05747          return -1;
05748       }
05749    }
05750    if (res == '#') {
05751       /* On a '#' we skip the instructions */
05752       ast_set_flag(options, OPT_SILENT);
05753       res = 0;
05754    }
05755    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05756    if (vmu->maxmsg == 0) {
05757       if (option_debug > 2)
05758          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05759       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05760       goto leave_vm_out;
05761    }
05762    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05763       res = ast_stream_and_wait(chan, INTRO, ecodes);
05764       if (res == '#') {
05765          ast_set_flag(options, OPT_SILENT);
05766          res = 0;
05767       }
05768    }
05769    if (res > 0)
05770       ast_stopstream(chan);
05771    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05772     other than the operator -- an automated attendant or mailbox login for example */
05773    if (res == '*') {
05774       chan->exten[0] = 'a';
05775       chan->exten[1] = '\0';
05776       if (!ast_strlen_zero(vmu->exit)) {
05777          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05778       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05779          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05780       }
05781       chan->priority = 0;
05782       free_user(vmu);
05783       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05784       ast_free(tmp);
05785       return 0;
05786    }
05787 
05788    /* Check for a '0' here */
05789    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05790    transfer:
05791       if (ouseexten || ousemacro) {
05792          chan->exten[0] = 'o';
05793          chan->exten[1] = '\0';
05794          if (!ast_strlen_zero(vmu->exit)) {
05795             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05796          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05797             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05798          }
05799          ast_play_and_wait(chan, "transfer");
05800          chan->priority = 0;
05801          free_user(vmu);
05802          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05803       }
05804       ast_free(tmp);
05805       return OPERATOR_EXIT;
05806    }
05807 
05808    /* Allow all other digits to exit Voicemail and return to the dialplan */
05809    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05810       if (!ast_strlen_zero(options->exitcontext))
05811          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05812       free_user(vmu);
05813       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05814       ast_free(tmp);
05815       return res;
05816    }
05817 
05818    if (res < 0) {
05819       free_user(vmu);
05820       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05821       ast_free(tmp);
05822       return -1;
05823    }
05824    /* The meat of recording the message...  All the announcements and beeps have been played*/
05825    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05826    if (!ast_strlen_zero(fmt)) {
05827       msgnum = 0;
05828 
05829 #ifdef IMAP_STORAGE
05830       /* Is ext a mailbox? */
05831       /* must open stream for this user to get info! */
05832       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05833       if (res < 0) {
05834          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05835          ast_free(tmp);
05836          return -1;
05837       }
05838       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05839       /* It is possible under certain circumstances that inboxcount did not
05840        * create a vm_state when it was needed. This is a catchall which will
05841        * rarely be used.
05842        */
05843          if (!(vms = create_vm_state_from_user(vmu))) {
05844             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05845             ast_free(tmp);
05846             return -1;
05847          }
05848       }
05849       vms->newmessages++;
05850       
05851       /* here is a big difference! We add one to it later */
05852       msgnum = newmsgs + oldmsgs;
05853       ast_debug(3, "Messagecount set to %d\n", msgnum);
05854       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05855       /* set variable for compatibility */
05856       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05857 
05858       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05859          goto leave_vm_out;
05860       }
05861 #else
05862       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05863          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05864          if (!res)
05865             res = ast_waitstream(chan, "");
05866          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05867          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05868          inprocess_count(vmu->mailbox, vmu->context, -1);
05869          goto leave_vm_out;
05870       }
05871 
05872 #endif
05873       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05874       txtdes = mkstemp(tmptxtfile);
05875       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05876       if (txtdes < 0) {
05877          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05878          if (!res)
05879             res = ast_waitstream(chan, "");
05880          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05881          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05882          inprocess_count(vmu->mailbox, vmu->context, -1);
05883          goto leave_vm_out;
05884       }
05885 
05886       /* Now play the beep once we have the message number for our next message. */
05887       if (res >= 0) {
05888          /* Unless we're *really* silent, try to send the beep */
05889          res = ast_stream_and_wait(chan, "beep", "");
05890       }
05891             
05892       /* Store information in real-time storage */
05893       if (ast_check_realtime("voicemail_data")) {
05894          snprintf(priority, sizeof(priority), "%d", chan->priority);
05895          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05896          get_date(date, sizeof(date));
05897          ast_callerid_merge(callerid, sizeof(callerid),
05898             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05899             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05900             "Unknown");
05901          ast_store_realtime("voicemail_data",
05902             "origmailbox", ext,
05903             "context", chan->context,
05904             "macrocontext", chan->macrocontext,
05905             "exten", chan->exten,
05906             "priority", priority,
05907             "callerchan", chan->name,
05908             "callerid", callerid,
05909             "origdate", date,
05910             "origtime", origtime,
05911             "category", S_OR(category, ""),
05912             "filename", tmptxtfile,
05913             SENTINEL);
05914       }
05915 
05916       /* Store information */
05917       txt = fdopen(txtdes, "w+");
05918       if (txt) {
05919          get_date(date, sizeof(date));
05920          ast_callerid_merge(callerid, sizeof(callerid),
05921             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05922             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05923             "Unknown");
05924          fprintf(txt, 
05925             ";\n"
05926             "; Message Information file\n"
05927             ";\n"
05928             "[message]\n"
05929             "origmailbox=%s\n"
05930             "context=%s\n"
05931             "macrocontext=%s\n"
05932             "exten=%s\n"
05933             "rdnis=%s\n"
05934             "priority=%d\n"
05935             "callerchan=%s\n"
05936             "callerid=%s\n"
05937             "origdate=%s\n"
05938             "origtime=%ld\n"
05939             "category=%s\n",
05940             ext,
05941             chan->context,
05942             chan->macrocontext, 
05943             chan->exten,
05944             S_COR(chan->redirecting.from.number.valid,
05945                chan->redirecting.from.number.str, "unknown"),
05946             chan->priority,
05947             chan->name,
05948             callerid,
05949             date, (long) time(NULL),
05950             category ? category : "");
05951       } else {
05952          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05953          inprocess_count(vmu->mailbox, vmu->context, -1);
05954          if (ast_check_realtime("voicemail_data")) {
05955             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05956          }
05957          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05958          goto leave_vm_out;
05959       }
05960       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05961 
05962       if (txt) {
05963          fprintf(txt, "flag=%s\n", flag);
05964          if (sound_duration < vmu->minsecs) {
05965             fclose(txt);
05966             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05967             ast_filedelete(tmptxtfile, NULL);
05968             unlink(tmptxtfile);
05969             if (ast_check_realtime("voicemail_data")) {
05970                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05971             }
05972             inprocess_count(vmu->mailbox, vmu->context, -1);
05973          } else {
05974             fprintf(txt, "duration=%d\n", duration);
05975             fclose(txt);
05976             if (vm_lock_path(dir)) {
05977                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05978                /* Delete files */
05979                ast_filedelete(tmptxtfile, NULL);
05980                unlink(tmptxtfile);
05981                inprocess_count(vmu->mailbox, vmu->context, -1);
05982             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05983                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05984                unlink(tmptxtfile);
05985                ast_unlock_path(dir);
05986                inprocess_count(vmu->mailbox, vmu->context, -1);
05987                if (ast_check_realtime("voicemail_data")) {
05988                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05989                }
05990             } else {
05991 #ifndef IMAP_STORAGE
05992                msgnum = last_message_index(vmu, dir) + 1;
05993 #endif
05994                make_file(fn, sizeof(fn), dir, msgnum);
05995 
05996                /* assign a variable with the name of the voicemail file */ 
05997 #ifndef IMAP_STORAGE
05998                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05999 #else
06000                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06001 #endif
06002 
06003                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06004                ast_filerename(tmptxtfile, fn, NULL);
06005                rename(tmptxtfile, txtfile);
06006                inprocess_count(vmu->mailbox, vmu->context, -1);
06007 
06008                /* Properly set permissions on voicemail text descriptor file.
06009                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06010                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06011                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06012 
06013                ast_unlock_path(dir);
06014                if (ast_check_realtime("voicemail_data")) {
06015                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06016                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06017                }
06018                /* We must store the file first, before copying the message, because
06019                 * ODBC storage does the entire copy with SQL.
06020                 */
06021                if (ast_fileexists(fn, NULL, NULL) > 0) {
06022                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06023                }
06024 
06025                /* Are there to be more recipients of this message? */
06026                while (tmpptr) {
06027                   struct ast_vm_user recipu, *recip;
06028                   char *exten, *cntx;
06029 
06030                   exten = strsep(&tmpptr, "&");
06031                   cntx = strchr(exten, '@');
06032                   if (cntx) {
06033                      *cntx = '\0';
06034                      cntx++;
06035                   }
06036                   if ((recip = find_user(&recipu, cntx, exten))) {
06037                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06038                      free_user(recip);
06039                   }
06040                }
06041 #ifndef IMAP_STORAGE
06042                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06043                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06044                   char sfn[PATH_MAX];
06045                   char dfn[PATH_MAX];
06046                   int x;
06047                   /* It's easier just to try to make it than to check for its existence */
06048                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06049                   x = last_message_index(vmu, urgdir) + 1;
06050                   make_file(sfn, sizeof(sfn), dir, msgnum);
06051                   make_file(dfn, sizeof(dfn), urgdir, x);
06052                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06053                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06054                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06055                   ast_copy_string(fn, dfn, sizeof(fn));
06056                   msgnum = x;
06057                }
06058 #endif
06059                /* Notification needs to happen after the copy, though. */
06060                if (ast_fileexists(fn, NULL, NULL)) {
06061 #ifdef IMAP_STORAGE
06062                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06063                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06064                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06065                      flag);
06066 #else
06067                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06068                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06069                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06070                      flag);
06071 #endif
06072                }
06073 
06074                /* Disposal needs to happen after the optional move and copy */
06075                if (ast_fileexists(fn, NULL, NULL)) {
06076                   DISPOSE(dir, msgnum);
06077                }
06078             }
06079          }
06080       } else {
06081          inprocess_count(vmu->mailbox, vmu->context, -1);
06082       }
06083       if (res == '0') {
06084          goto transfer;
06085       } else if (res > 0 && res != 't')
06086          res = 0;
06087 
06088       if (sound_duration < vmu->minsecs)
06089          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06090          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06091       else
06092          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06093    } else
06094       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06095 leave_vm_out:
06096    free_user(vmu);
06097 
06098 #ifdef IMAP_STORAGE
06099    /* expunge message - use UID Expunge if supported on IMAP server*/
06100    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06101    if (expungeonhangup == 1) {
06102       ast_mutex_lock(&vms->lock);
06103 #ifdef HAVE_IMAP_TK2006
06104       if (LEVELUIDPLUS (vms->mailstream)) {
06105          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06106       } else 
06107 #endif
06108          mail_expunge(vms->mailstream);
06109       ast_mutex_unlock(&vms->lock);
06110    }
06111 #endif
06112 
06113    ast_free(tmp);
06114    return res;
06115 }
06116 
06117 #if !defined(IMAP_STORAGE)
06118 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06119 {
06120    /* we know the actual number of messages, so stop process when number is hit */
06121 
06122    int x, dest;
06123    char sfn[PATH_MAX];
06124    char dfn[PATH_MAX];
06125 
06126    if (vm_lock_path(dir)) {
06127       return ERROR_LOCK_PATH;
06128    }
06129 
06130    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06131       make_file(sfn, sizeof(sfn), dir, x);
06132       if (EXISTS(dir, x, sfn, NULL)) {
06133 
06134          if (x != dest) {
06135             make_file(dfn, sizeof(dfn), dir, dest);
06136             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06137          }
06138 
06139          dest++;
06140       }
06141    }
06142    ast_unlock_path(dir);
06143 
06144    return dest;
06145 }
06146 #endif
06147 
06148 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06149 {
06150    int d;
06151    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06152    return d;
06153 }
06154 
06155 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06156 {
06157 #ifdef IMAP_STORAGE
06158    /* we must use mbox(x) folder names, and copy the message there */
06159    /* simple. huh? */
06160    char sequence[10];
06161    char mailbox[256];
06162    int res;
06163 
06164    /* get the real IMAP message number for this message */
06165    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06166    
06167    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06168    ast_mutex_lock(&vms->lock);
06169    /* if save to Old folder, put in INBOX as read */
06170    if (box == OLD_FOLDER) {
06171       mail_setflag(vms->mailstream, sequence, "\\Seen");
06172       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06173    } else if (box == NEW_FOLDER) {
06174       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06175       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06176    }
06177    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06178       ast_mutex_unlock(&vms->lock);
06179       return 0;
06180    }
06181    /* Create the folder if it don't exist */
06182    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06183    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06184    if (mail_create(vms->mailstream, mailbox) == NIL) 
06185       ast_debug(5, "Folder exists.\n");
06186    else
06187       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06188    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06189    ast_mutex_unlock(&vms->lock);
06190    return res;
06191 #else
06192    char *dir = vms->curdir;
06193    char *username = vms->username;
06194    char *context = vmu->context;
06195    char sfn[PATH_MAX];
06196    char dfn[PATH_MAX];
06197    char ddir[PATH_MAX];
06198    const char *dbox = mbox(vmu, box);
06199    int x, i;
06200    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06201 
06202    if (vm_lock_path(ddir))
06203       return ERROR_LOCK_PATH;
06204 
06205    x = last_message_index(vmu, ddir) + 1;
06206 
06207    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06208       x--;
06209       for (i = 1; i <= x; i++) {
06210          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06211          make_file(sfn, sizeof(sfn), ddir, i);
06212          make_file(dfn, sizeof(dfn), ddir, i - 1);
06213          if (EXISTS(ddir, i, sfn, NULL)) {
06214             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06215          } else
06216             break;
06217       }
06218    } else {
06219       if (x >= vmu->maxmsg) {
06220          ast_unlock_path(ddir);
06221          return -1;
06222       }
06223    }
06224    make_file(sfn, sizeof(sfn), dir, msg);
06225    make_file(dfn, sizeof(dfn), ddir, x);
06226    if (strcmp(sfn, dfn)) {
06227       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06228    }
06229    ast_unlock_path(ddir);
06230 #endif
06231    return 0;
06232 }
06233 
06234 static int adsi_logo(unsigned char *buf)
06235 {
06236    int bytes = 0;
06237    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06238    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06239    return bytes;
06240 }
06241 
06242 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06243 {
06244    unsigned char buf[256];
06245    int bytes = 0;
06246    int x;
06247    char num[5];
06248 
06249    *useadsi = 0;
06250    bytes += ast_adsi_data_mode(buf + bytes);
06251    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06252 
06253    bytes = 0;
06254    bytes += adsi_logo(buf);
06255    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06256 #ifdef DISPLAY
06257    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06258 #endif
06259    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06260    bytes += ast_adsi_data_mode(buf + bytes);
06261    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06262 
06263    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06264       bytes = 0;
06265       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06266       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06267       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06268       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06269       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06270       return 0;
06271    }
06272 
06273 #ifdef DISPLAY
06274    /* Add a dot */
06275    bytes = 0;
06276    bytes += ast_adsi_logo(buf);
06277    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06278    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06279    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06280    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06281 #endif
06282    bytes = 0;
06283    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06284    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06285    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06286    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06287    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06288    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06289    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06290 
06291 #ifdef DISPLAY
06292    /* Add another dot */
06293    bytes = 0;
06294    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06295    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06296 
06297    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06298    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06299 #endif
06300 
06301    bytes = 0;
06302    /* These buttons we load but don't use yet */
06303    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06304    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06305    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06306    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06307    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06308    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06309    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06310 
06311 #ifdef DISPLAY
06312    /* Add another dot */
06313    bytes = 0;
06314    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06315    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06316    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06317 #endif
06318 
06319    bytes = 0;
06320    for (x = 0; x < 5; x++) {
06321       snprintf(num, sizeof(num), "%d", x);
06322       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06323    }
06324    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06325    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06326 
06327 #ifdef DISPLAY
06328    /* Add another dot */
06329    bytes = 0;
06330    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
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    if (ast_adsi_end_download(chan)) {
06336       bytes = 0;
06337       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06338       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06339       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06340       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06341       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06342       return 0;
06343    }
06344    bytes = 0;
06345    bytes += ast_adsi_download_disconnect(buf + bytes);
06346    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06347    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06348 
06349    ast_debug(1, "Done downloading scripts...\n");
06350 
06351 #ifdef DISPLAY
06352    /* Add last dot */
06353    bytes = 0;
06354    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06355    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06356 #endif
06357    ast_debug(1, "Restarting session...\n");
06358 
06359    bytes = 0;
06360    /* Load the session now */
06361    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06362       *useadsi = 1;
06363       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06364    } else
06365       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06366 
06367    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06368    return 0;
06369 }
06370 
06371 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06372 {
06373    int x;
06374    if (!ast_adsi_available(chan))
06375       return;
06376    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06377    if (x < 0)
06378       return;
06379    if (!x) {
06380       if (adsi_load_vmail(chan, useadsi)) {
06381          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06382          return;
06383       }
06384    } else
06385       *useadsi = 1;
06386 }
06387 
06388 static void adsi_login(struct ast_channel *chan)
06389 {
06390    unsigned char buf[256];
06391    int bytes = 0;
06392    unsigned char keys[8];
06393    int x;
06394    if (!ast_adsi_available(chan))
06395       return;
06396 
06397    for (x = 0; x < 8; x++)
06398       keys[x] = 0;
06399    /* Set one key for next */
06400    keys[3] = ADSI_KEY_APPS + 3;
06401 
06402    bytes += adsi_logo(buf + bytes);
06403    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06404    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06405    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06406    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06407    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06408    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06409    bytes += ast_adsi_set_keys(buf + bytes, keys);
06410    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06411    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06412 }
06413 
06414 static void adsi_password(struct ast_channel *chan)
06415 {
06416    unsigned char buf[256];
06417    int bytes = 0;
06418    unsigned char keys[8];
06419    int x;
06420    if (!ast_adsi_available(chan))
06421       return;
06422 
06423    for (x = 0; x < 8; x++)
06424       keys[x] = 0;
06425    /* Set one key for next */
06426    keys[3] = ADSI_KEY_APPS + 3;
06427 
06428    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06429    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06430    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06431    bytes += ast_adsi_set_keys(buf + bytes, keys);
06432    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06433    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06434 }
06435 
06436 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06437 {
06438    unsigned char buf[256];
06439    int bytes = 0;
06440    unsigned char keys[8];
06441    int x, y;
06442 
06443    if (!ast_adsi_available(chan))
06444       return;
06445 
06446    for (x = 0; x < 5; x++) {
06447       y = ADSI_KEY_APPS + 12 + start + x;
06448       if (y > ADSI_KEY_APPS + 12 + 4)
06449          y = 0;
06450       keys[x] = ADSI_KEY_SKT | y;
06451    }
06452    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06453    keys[6] = 0;
06454    keys[7] = 0;
06455 
06456    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06457    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06458    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06459    bytes += ast_adsi_set_keys(buf + bytes, keys);
06460    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06461 
06462    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06463 }
06464 
06465 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06466 {
06467    int bytes = 0;
06468    unsigned char buf[256]; 
06469    char buf1[256], buf2[256];
06470    char fn2[PATH_MAX];
06471 
06472    char cid[256] = "";
06473    char *val;
06474    char *name, *num;
06475    char datetime[21] = "";
06476    FILE *f;
06477 
06478    unsigned char keys[8];
06479 
06480    int x;
06481 
06482    if (!ast_adsi_available(chan))
06483       return;
06484 
06485    /* Retrieve important info */
06486    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06487    f = fopen(fn2, "r");
06488    if (f) {
06489       while (!feof(f)) {   
06490          if (!fgets((char *) buf, sizeof(buf), f)) {
06491             continue;
06492          }
06493          if (!feof(f)) {
06494             char *stringp = NULL;
06495             stringp = (char *) buf;
06496             strsep(&stringp, "=");
06497             val = strsep(&stringp, "=");
06498             if (!ast_strlen_zero(val)) {
06499                if (!strcmp((char *) buf, "callerid"))
06500                   ast_copy_string(cid, val, sizeof(cid));
06501                if (!strcmp((char *) buf, "origdate"))
06502                   ast_copy_string(datetime, val, sizeof(datetime));
06503             }
06504          }
06505       }
06506       fclose(f);
06507    }
06508    /* New meaning for keys */
06509    for (x = 0; x < 5; x++)
06510       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06511    keys[6] = 0x0;
06512    keys[7] = 0x0;
06513 
06514    if (!vms->curmsg) {
06515       /* No prev key, provide "Folder" instead */
06516       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06517    }
06518    if (vms->curmsg >= vms->lastmsg) {
06519       /* If last message ... */
06520       if (vms->curmsg) {
06521          /* but not only message, provide "Folder" instead */
06522          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06523          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06524 
06525       } else {
06526          /* Otherwise if only message, leave blank */
06527          keys[3] = 1;
06528       }
06529    }
06530 
06531    if (!ast_strlen_zero(cid)) {
06532       ast_callerid_parse(cid, &name, &num);
06533       if (!name)
06534          name = num;
06535    } else
06536       name = "Unknown Caller";
06537 
06538    /* If deleted, show "undeleted" */
06539 
06540    if (vms->deleted[vms->curmsg])
06541       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06542 
06543    /* Except "Exit" */
06544    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06545    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06546       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06547    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06548 
06549    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06550    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06551    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06552    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06553    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06554    bytes += ast_adsi_set_keys(buf + bytes, keys);
06555    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06556 
06557    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06558 }
06559 
06560 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06561 {
06562    int bytes = 0;
06563    unsigned char buf[256];
06564    unsigned char keys[8];
06565 
06566    int x;
06567 
06568    if (!ast_adsi_available(chan))
06569       return;
06570 
06571    /* New meaning for keys */
06572    for (x = 0; x < 5; x++)
06573       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06574 
06575    keys[6] = 0x0;
06576    keys[7] = 0x0;
06577 
06578    if (!vms->curmsg) {
06579       /* No prev key, provide "Folder" instead */
06580       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06581    }
06582    if (vms->curmsg >= vms->lastmsg) {
06583       /* If last message ... */
06584       if (vms->curmsg) {
06585          /* but not only message, provide "Folder" instead */
06586          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06587       } else {
06588          /* Otherwise if only message, leave blank */
06589          keys[3] = 1;
06590       }
06591    }
06592 
06593    /* If deleted, show "undeleted" */
06594    if (vms->deleted[vms->curmsg]) 
06595       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06596 
06597    /* Except "Exit" */
06598    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06599    bytes += ast_adsi_set_keys(buf + bytes, keys);
06600    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06601 
06602    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06603 }
06604 
06605 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06606 {
06607    unsigned char buf[256] = "";
06608    char buf1[256] = "", buf2[256] = "";
06609    int bytes = 0;
06610    unsigned char keys[8];
06611    int x;
06612 
06613    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06614    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06615    if (!ast_adsi_available(chan))
06616       return;
06617    if (vms->newmessages) {
06618       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06619       if (vms->oldmessages) {
06620          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06621          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06622       } else {
06623          snprintf(buf2, sizeof(buf2), "%s.", newm);
06624       }
06625    } else if (vms->oldmessages) {
06626       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06627       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06628    } else {
06629       strcpy(buf1, "You have no messages.");
06630       buf2[0] = ' ';
06631       buf2[1] = '\0';
06632    }
06633    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06634    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06635    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06636 
06637    for (x = 0; x < 6; x++)
06638       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06639    keys[6] = 0;
06640    keys[7] = 0;
06641 
06642    /* Don't let them listen if there are none */
06643    if (vms->lastmsg < 0)
06644       keys[0] = 1;
06645    bytes += ast_adsi_set_keys(buf + bytes, keys);
06646 
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_status2(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 *mess = (vms->lastmsg == 0) ? "message" : "messages";
06661 
06662    if (!ast_adsi_available(chan))
06663       return;
06664 
06665    /* Original command keys */
06666    for (x = 0; x < 6; x++)
06667       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06668 
06669    keys[6] = 0;
06670    keys[7] = 0;
06671 
06672    if ((vms->lastmsg + 1) < 1)
06673       keys[0] = 0;
06674 
06675    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06676       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06677 
06678    if (vms->lastmsg + 1)
06679       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06680    else
06681       strcpy(buf2, "no messages.");
06682    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06683    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06684    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06685    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06686    bytes += ast_adsi_set_keys(buf + bytes, keys);
06687 
06688    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06689 
06690    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06691    
06692 }
06693 
06694 /*
06695 static void adsi_clear(struct ast_channel *chan)
06696 {
06697    char buf[256];
06698    int bytes=0;
06699    if (!ast_adsi_available(chan))
06700       return;
06701    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06702    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06703 
06704    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06705 }
06706 */
06707 
06708 static void adsi_goodbye(struct ast_channel *chan)
06709 {
06710    unsigned char buf[256];
06711    int bytes = 0;
06712 
06713    if (!ast_adsi_available(chan))
06714       return;
06715    bytes += adsi_logo(buf + bytes);
06716    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06717    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06718    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06719    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06720 
06721    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06722 }
06723 
06724 /*!\brief get_folder: Folder menu
06725  * Plays "press 1 for INBOX messages" etc.
06726  * Should possibly be internationalized
06727  */
06728 static int get_folder(struct ast_channel *chan, int start)
06729 {
06730    int x;
06731    int d;
06732    char fn[PATH_MAX];
06733    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06734    if (d)
06735       return d;
06736    for (x = start; x < 5; x++) { /* For all folders */
06737       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06738          return d;
06739       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06740       if (d)
06741          return d;
06742       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06743 
06744       /* The inbox folder can have its name changed under certain conditions
06745        * so this checks if the sound file exists for the inbox folder name and
06746        * if it doesn't, plays the default name instead. */
06747       if (x == 0) {
06748          if (ast_fileexists(fn, NULL, NULL)) {
06749             d = vm_play_folder_name(chan, fn);
06750          } else {
06751             ast_verb(1, "failed to find %s\n", fn);
06752             d = vm_play_folder_name(chan, "vm-INBOX");
06753          }
06754       } else {
06755          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06756          d = vm_play_folder_name(chan, fn);
06757       }
06758 
06759       if (d)
06760          return d;
06761       d = ast_waitfordigit(chan, 500);
06762       if (d)
06763          return d;
06764    }
06765 
06766    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06767    if (d)
06768       return d;
06769    d = ast_waitfordigit(chan, 4000);
06770    return d;
06771 }
06772 
06773 /*!
06774  * \brief plays a prompt and waits for a keypress.
06775  * \param chan
06776  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06777  * \param start Does not appear to be used at this time.
06778  *
06779  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06780  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06781  * prompting for the number inputs that correspond to the available folders.
06782  * 
06783  * \return zero on success, or -1 on error.
06784  */
06785 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06786 {
06787    int res = 0;
06788    int loops = 0;
06789 
06790    res = ast_play_and_wait(chan, fn);  /* Folder name */
06791    while (((res < '0') || (res > '9')) &&
06792          (res != '#') && (res >= 0) &&
06793          loops < 4) {
06794       res = get_folder(chan, 0);
06795       loops++;
06796    }
06797    if (loops == 4) { /* give up */
06798       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06799       return '#';
06800    }
06801    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06802    return res;
06803 }
06804 
06805 /*!
06806  * \brief presents the option to prepend to an existing message when forwarding it.
06807  * \param chan
06808  * \param vmu
06809  * \param curdir
06810  * \param curmsg
06811  * \param vm_fmts
06812  * \param context
06813  * \param record_gain
06814  * \param duration
06815  * \param vms
06816  * \param flag 
06817  *
06818  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06819  *
06820  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06821  * \return zero on success, -1 on error.
06822  */
06823 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06824          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06825 {
06826    int cmd = 0;
06827    int retries = 0, prepend_duration = 0, already_recorded = 0;
06828    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06829    char textfile[PATH_MAX];
06830    struct ast_config *msg_cfg;
06831    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06832 #ifndef IMAP_STORAGE
06833    signed char zero_gain = 0;
06834 #endif
06835    const char *duration_str;
06836 
06837    /* Must always populate duration correctly */
06838    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06839    strcpy(textfile, msgfile);
06840    strcpy(backup, msgfile);
06841    strcpy(backup_textfile, msgfile);
06842    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06843    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06844    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06845 
06846    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06847       *duration = atoi(duration_str);
06848    } else {
06849       *duration = 0;
06850    }
06851 
06852    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06853       if (cmd)
06854          retries = 0;
06855       switch (cmd) {
06856       case '1': 
06857 
06858 #ifdef IMAP_STORAGE
06859          /* Record new intro file */
06860          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06861          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06862          ast_play_and_wait(chan, INTRO);
06863          ast_play_and_wait(chan, "beep");
06864          play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06865          cmd = 't';
06866 #else
06867 
06868          /* prepend a message to the current message, update the metadata and return */
06869 
06870          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06871          strcpy(textfile, msgfile);
06872          strncat(textfile, ".txt", sizeof(textfile) - 1);
06873          *duration = 0;
06874 
06875          /* if we can't read the message metadata, stop now */
06876          if (!msg_cfg) {
06877             cmd = 0;
06878             break;
06879          }
06880 
06881          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06882 #ifndef IMAP_STORAGE
06883          if (already_recorded) {
06884             ast_filecopy(backup, msgfile, NULL);
06885             copy(backup_textfile, textfile);
06886          }
06887          else {
06888             ast_filecopy(msgfile, backup, NULL);
06889             copy(textfile, backup_textfile);
06890          }
06891 #endif
06892          already_recorded = 1;
06893 
06894          if (record_gain)
06895             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06896 
06897          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06898 
06899          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06900             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06901             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06902             ast_filerename(backup, msgfile, NULL);
06903          }
06904 
06905          if (record_gain)
06906             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06907 
06908          
06909          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06910             *duration = atoi(duration_str);
06911 
06912          if (prepend_duration) {
06913             struct ast_category *msg_cat;
06914             /* need enough space for a maximum-length message duration */
06915             char duration_buf[12];
06916 
06917             *duration += prepend_duration;
06918             msg_cat = ast_category_get(msg_cfg, "message");
06919             snprintf(duration_buf, 11, "%ld", *duration);
06920             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06921                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06922             }
06923          }
06924 
06925 #endif
06926          break;
06927       case '2': 
06928          /* NULL out introfile so we know there is no intro! */
06929 #ifdef IMAP_STORAGE
06930          *vms->introfn = '\0';
06931 #endif
06932          cmd = 't';
06933          break;
06934       case '*':
06935          cmd = '*';
06936          break;
06937       default: 
06938          /* If time_out and return to menu, reset already_recorded */
06939          already_recorded = 0;
06940 
06941          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06942             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06943          if (!cmd) {
06944             cmd = ast_play_and_wait(chan, "vm-starmain");
06945             /* "press star to return to the main menu" */
06946          }
06947          if (!cmd) {
06948             cmd = ast_waitfordigit(chan, 6000);
06949          }
06950          if (!cmd) {
06951             retries++;
06952          }
06953          if (retries > 3) {
06954             cmd = '*'; /* Let's cancel this beast */
06955          }
06956          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06957       }
06958    }
06959 
06960    if (msg_cfg)
06961       ast_config_destroy(msg_cfg);
06962    if (prepend_duration)
06963       *duration = prepend_duration;
06964 
06965    if (already_recorded && cmd == -1) {
06966       /* restore original message if prepention cancelled */
06967       ast_filerename(backup, msgfile, NULL);
06968       rename(backup_textfile, textfile);
06969    }
06970 
06971    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
06972       cmd = 0;
06973    return cmd;
06974 }
06975 
06976 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06977 {
06978    struct ast_event *event;
06979    char *mailbox, *context;
06980 
06981    /* Strip off @default */
06982    context = mailbox = ast_strdupa(box);
06983    strsep(&context, "@");
06984    if (ast_strlen_zero(context))
06985       context = "default";
06986 
06987    if (!(event = ast_event_new(AST_EVENT_MWI,
06988          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06989          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06990          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06991          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06992          AST_EVENT_IE_END))) {
06993       return;
06994    }
06995 
06996    ast_event_queue_and_cache(event);
06997 }
06998 
06999 /*!
07000  * \brief Sends email notification that a user has a new voicemail waiting for them.
07001  * \param chan
07002  * \param vmu
07003  * \param vms
07004  * \param msgnum
07005  * \param duration
07006  * \param fmt
07007  * \param cidnum The Caller ID phone number value.
07008  * \param cidname The Caller ID name value.
07009  * \param flag
07010  *
07011  * \return zero on success, -1 on error.
07012  */
07013 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)
07014 {
07015    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07016    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07017    const char *category;
07018    char *myserveremail = serveremail;
07019 
07020    ast_channel_lock(chan);
07021    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07022       category = ast_strdupa(category);
07023    }
07024    ast_channel_unlock(chan);
07025 
07026 #ifndef IMAP_STORAGE
07027    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07028 #else
07029    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07030 #endif
07031    make_file(fn, sizeof(fn), todir, msgnum);
07032    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07033 
07034    if (!ast_strlen_zero(vmu->attachfmt)) {
07035       if (strstr(fmt, vmu->attachfmt))
07036          fmt = vmu->attachfmt;
07037       else
07038          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);
07039    }
07040 
07041    /* Attach only the first format */
07042    fmt = ast_strdupa(fmt);
07043    stringp = fmt;
07044    strsep(&stringp, "|");
07045 
07046    if (!ast_strlen_zero(vmu->serveremail))
07047       myserveremail = vmu->serveremail;
07048 
07049    if (!ast_strlen_zero(vmu->email)) {
07050       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07051 
07052       if (attach_user_voicemail)
07053          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07054 
07055       /* XXX possible imap issue, should category be NULL XXX */
07056       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07057 
07058       if (attach_user_voicemail)
07059          DISPOSE(todir, msgnum);
07060    }
07061 
07062    if (!ast_strlen_zero(vmu->pager)) {
07063       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07064    }
07065 
07066    if (ast_test_flag(vmu, VM_DELETE))
07067       DELETE(todir, msgnum, fn, vmu);
07068 
07069    /* Leave voicemail for someone */
07070    if (ast_app_has_voicemail(ext_context, NULL)) 
07071       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07072 
07073    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07074 
07075    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);
07076    run_externnotify(vmu->context, vmu->mailbox, flag);
07077 
07078 #ifdef IMAP_STORAGE
07079    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07080    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07081       vm_imap_delete(NULL, vms->curmsg, vmu);
07082       vms->newmessages--;  /* Fix new message count */
07083    }
07084 #endif
07085 
07086    return 0;
07087 }
07088 
07089 /*!
07090  * \brief Sends a voicemail message to a mailbox recipient.
07091  * \param chan
07092  * \param context
07093  * \param vms
07094  * \param sender
07095  * \param fmt
07096  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07097  *             Will be 0 when called to forward an existing message (option 8)
07098  *             Will be 1 when called to leave a message (option 3->5)
07099  * \param record_gain 
07100  * \param urgent
07101  *
07102  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07103  * 
07104  * When in the leave message mode (is_new_message == 1):
07105  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07106  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07107  *
07108  * When in the forward message mode (is_new_message == 0):
07109  *   - retreives the current message to be forwarded
07110  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07111  *   - determines the target mailbox and folders
07112  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07113  *
07114  * \return zero on success, -1 on error.
07115  */
07116 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)
07117 {
07118 #ifdef IMAP_STORAGE
07119    int todircount = 0;
07120    struct vm_state *dstvms;
07121 #endif
07122    char username[70]="";
07123    char fn[PATH_MAX]; /* for playback of name greeting */
07124    char ecodes[16] = "#";
07125    int res = 0, cmd = 0;
07126    struct ast_vm_user *receiver = NULL, *vmtmp;
07127    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07128    char *stringp;
07129    const char *s;
07130    int saved_messages = 0;
07131    int valid_extensions = 0;
07132    char *dir;
07133    int curmsg;
07134    char urgent_str[7] = "";
07135    int prompt_played = 0;
07136 #ifndef IMAP_STORAGE
07137    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07138 #endif
07139    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07140       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07141    }
07142 
07143    if (vms == NULL) return -1;
07144    dir = vms->curdir;
07145    curmsg = vms->curmsg;
07146 
07147    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07148    while (!res && !valid_extensions) {
07149       int use_directory = 0;
07150       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07151          int done = 0;
07152          int retries = 0;
07153          cmd = 0;
07154          while ((cmd >= 0) && !done ){
07155             if (cmd)
07156                retries = 0;
07157             switch (cmd) {
07158             case '1': 
07159                use_directory = 0;
07160                done = 1;
07161                break;
07162             case '2': 
07163                use_directory = 1;
07164                done = 1;
07165                break;
07166             case '*': 
07167                cmd = 't';
07168                done = 1;
07169                break;
07170             default: 
07171                /* Press 1 to enter an extension press 2 to use the directory */
07172                cmd = ast_play_and_wait(chan, "vm-forward");
07173                if (!cmd) {
07174                   cmd = ast_waitfordigit(chan, 3000);
07175                }
07176                if (!cmd) {
07177                   retries++;
07178                }
07179                if (retries > 3) {
07180                   cmd = 't';
07181                   done = 1;
07182                }
07183                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07184             }
07185          }
07186          if (cmd < 0 || cmd == 't')
07187             break;
07188       }
07189       
07190       if (use_directory) {
07191          /* use app_directory */
07192          
07193          char old_context[sizeof(chan->context)];
07194          char old_exten[sizeof(chan->exten)];
07195          int old_priority;
07196          struct ast_app* directory_app;
07197 
07198          directory_app = pbx_findapp("Directory");
07199          if (directory_app) {
07200             char vmcontext[256];
07201             /* make backup copies */
07202             memcpy(old_context, chan->context, sizeof(chan->context));
07203             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07204             old_priority = chan->priority;
07205             
07206             /* call the the Directory, changes the channel */
07207             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07208             res = pbx_exec(chan, directory_app, vmcontext);
07209             
07210             ast_copy_string(username, chan->exten, sizeof(username));
07211             
07212             /* restore the old context, exten, and priority */
07213             memcpy(chan->context, old_context, sizeof(chan->context));
07214             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07215             chan->priority = old_priority;
07216          } else {
07217             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07218             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07219          }
07220       } else {
07221          /* Ask for an extension */
07222          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07223          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07224          prompt_played++;
07225          if (res || prompt_played > 4)
07226             break;
07227          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07228             break;
07229       }
07230       
07231       /* start all over if no username */
07232       if (ast_strlen_zero(username))
07233          continue;
07234       stringp = username;
07235       s = strsep(&stringp, "*");
07236       /* start optimistic */
07237       valid_extensions = 1;
07238       while (s) {
07239          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07240             int oldmsgs;
07241             int newmsgs;
07242             int capacity;
07243             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07244                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07245                /* Shouldn't happen, but allow trying another extension if it does */
07246                res = ast_play_and_wait(chan, "pbx-invalid");
07247                valid_extensions = 0;
07248                break;
07249             }
07250             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07251             if ((newmsgs + oldmsgs) >= capacity) {
07252                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07253                res = ast_play_and_wait(chan, "vm-mailboxfull");
07254                valid_extensions = 0;
07255                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07256                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07257                   free_user(vmtmp);
07258                }
07259                inprocess_count(receiver->mailbox, receiver->context, -1);
07260                break;
07261             }
07262             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07263          } else {
07264             /* XXX Optimization for the future.  When we encounter a single bad extension,
07265              * bailing out on all of the extensions may not be the way to go.  We should
07266              * probably just bail on that single extension, then allow the user to enter
07267              * several more. XXX
07268              */
07269             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07270                free_user(receiver);
07271             }
07272             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07273             /* "I am sorry, that's not a valid extension.  Please try again." */
07274             res = ast_play_and_wait(chan, "pbx-invalid");
07275             valid_extensions = 0;
07276             break;
07277          }
07278 
07279          /* play name if available, else play extension number */
07280          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07281          RETRIEVE(fn, -1, s, receiver->context);
07282          if (ast_fileexists(fn, NULL, NULL) > 0) {
07283             res = ast_stream_and_wait(chan, fn, ecodes);
07284             if (res) {
07285                DISPOSE(fn, -1);
07286                return res;
07287             }
07288          } else {
07289             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07290          }
07291          DISPOSE(fn, -1);
07292 
07293          s = strsep(&stringp, "*");
07294       }
07295       /* break from the loop of reading the extensions */
07296       if (valid_extensions)
07297          break;
07298    }
07299    /* check if we're clear to proceed */
07300    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07301       return res;
07302    if (is_new_message == 1) {
07303       struct leave_vm_options leave_options;
07304       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07305       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07306 
07307       /* Send VoiceMail */
07308       memset(&leave_options, 0, sizeof(leave_options));
07309       leave_options.record_gain = record_gain;
07310       cmd = leave_voicemail(chan, mailbox, &leave_options);
07311    } else {
07312       /* Forward VoiceMail */
07313       long duration = 0;
07314       struct vm_state vmstmp;
07315       int copy_msg_result = 0;
07316       memcpy(&vmstmp, vms, sizeof(vmstmp));
07317 
07318       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07319 
07320       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07321       if (!cmd) {
07322          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07323 #ifdef IMAP_STORAGE
07324             int attach_user_voicemail;
07325             char *myserveremail = serveremail;
07326             
07327             /* get destination mailbox */
07328             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07329             if (!dstvms) {
07330                dstvms = create_vm_state_from_user(vmtmp);
07331             }
07332             if (dstvms) {
07333                init_mailstream(dstvms, 0);
07334                if (!dstvms->mailstream) {
07335                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07336                } else {
07337                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07338                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07339                }
07340             } else {
07341                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07342             }
07343             if (!ast_strlen_zero(vmtmp->serveremail))
07344                myserveremail = vmtmp->serveremail;
07345             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07346             /* NULL category for IMAP storage */
07347             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07348                dstvms->curbox,
07349                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07350                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07351                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07352                NULL, urgent_str);
07353 #else
07354             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07355 #endif
07356             saved_messages++;
07357             AST_LIST_REMOVE_CURRENT(list);
07358             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07359             free_user(vmtmp);
07360             if (res)
07361                break;
07362          }
07363          AST_LIST_TRAVERSE_SAFE_END;
07364          if (saved_messages > 0 && !copy_msg_result) {
07365             /* give confirmation that the message was saved */
07366             /* commented out since we can't forward batches yet
07367             if (saved_messages == 1)
07368                res = ast_play_and_wait(chan, "vm-message");
07369             else
07370                res = ast_play_and_wait(chan, "vm-messages");
07371             if (!res)
07372                res = ast_play_and_wait(chan, "vm-saved"); */
07373 #ifdef IMAP_STORAGE
07374             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07375             if (ast_strlen_zero(vmstmp.introfn))
07376 #endif
07377             res = ast_play_and_wait(chan, "vm-msgsaved");
07378          }
07379 #ifndef IMAP_STORAGE
07380          else {
07381             /* with IMAP, mailbox full warning played by imap_check_limits */
07382             res = ast_play_and_wait(chan, "vm-mailboxfull");
07383          }
07384          /* Restore original message without prepended message if backup exists */
07385          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07386          strcpy(textfile, msgfile);
07387          strcpy(backup, msgfile);
07388          strcpy(backup_textfile, msgfile);
07389          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07390          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07391          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07392          if (ast_fileexists(backup, NULL, NULL) > 0) {
07393             ast_filerename(backup, msgfile, NULL);
07394             rename(backup_textfile, textfile);
07395          }
07396 #endif
07397       }
07398       DISPOSE(dir, curmsg);
07399 #ifndef IMAP_STORAGE
07400       if (cmd) { /* assuming hangup, cleanup backup file */
07401          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07402          strcpy(textfile, msgfile);
07403          strcpy(backup_textfile, msgfile);
07404          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07405          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07406          rename(backup_textfile, textfile);
07407       }
07408 #endif
07409    }
07410 
07411    /* If anything failed above, we still have this list to free */
07412    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07413       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07414       free_user(vmtmp);
07415    }
07416    return res ? res : cmd;
07417 }
07418 
07419 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07420 {
07421    int res;
07422    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07423       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07424    return res;
07425 }
07426 
07427 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07428 {
07429    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07430    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);
07431 }
07432 
07433 static int play_message_category(struct ast_channel *chan, const char *category)
07434 {
07435    int res = 0;
07436 
07437    if (!ast_strlen_zero(category))
07438       res = ast_play_and_wait(chan, category);
07439 
07440    if (res) {
07441       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07442       res = 0;
07443    }
07444 
07445    return res;
07446 }
07447 
07448 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07449 {
07450    int res = 0;
07451    struct vm_zone *the_zone = NULL;
07452    time_t t;
07453 
07454    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07455       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07456       return 0;
07457    }
07458 
07459    /* Does this user have a timezone specified? */
07460    if (!ast_strlen_zero(vmu->zonetag)) {
07461       /* Find the zone in the list */
07462       struct vm_zone *z;
07463       AST_LIST_LOCK(&zones);
07464       AST_LIST_TRAVERSE(&zones, z, list) {
07465          if (!strcmp(z->name, vmu->zonetag)) {
07466             the_zone = z;
07467             break;
07468          }
07469       }
07470       AST_LIST_UNLOCK(&zones);
07471    }
07472 
07473 /* No internal variable parsing for now, so we'll comment it out for the time being */
07474 #if 0
07475    /* Set the DIFF_* variables */
07476    ast_localtime(&t, &time_now, NULL);
07477    tv_now = ast_tvnow();
07478    ast_localtime(&tv_now, &time_then, NULL);
07479 
07480    /* Day difference */
07481    if (time_now.tm_year == time_then.tm_year)
07482       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07483    else
07484       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07485    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07486 
07487    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07488 #endif
07489    if (the_zone) {
07490       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07491    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07492       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07493    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07494       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07495    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07496       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);
07497    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07498       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07499    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07500       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07501    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07502       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07503    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07504       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);
07505    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07506       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07507    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07508       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07509    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07510       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);
07511    } else {
07512       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07513    }
07514 #if 0
07515    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07516 #endif
07517    return res;
07518 }
07519 
07520 
07521 
07522 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07523 {
07524    int res = 0;
07525    int i;
07526    char *callerid, *name;
07527    char prefile[PATH_MAX] = "";
07528    
07529 
07530    /* If voicemail cid is not enabled, or we didn't get cid or context from
07531     * the attribute file, leave now.
07532     *
07533     * TODO Still need to change this so that if this function is called by the
07534     * message envelope (and someone is explicitly requesting to hear the CID),
07535     * it does not check to see if CID is enabled in the config file.
07536     */
07537    if ((cid == NULL)||(context == NULL))
07538       return res;
07539 
07540    /* Strip off caller ID number from name */
07541    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07542    ast_callerid_parse(cid, &name, &callerid);
07543    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07544       /* Check for internal contexts and only */
07545       /* say extension when the call didn't come from an internal context in the list */
07546       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07547          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07548          if ((strcmp(cidinternalcontexts[i], context) == 0))
07549             break;
07550       }
07551       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07552          if (!res) {
07553             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07554             if (!ast_strlen_zero(prefile)) {
07555             /* See if we can find a recorded name for this person instead of their extension number */
07556                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07557                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07558                   if (!callback)
07559                      res = wait_file2(chan, vms, "vm-from");
07560                   res = ast_stream_and_wait(chan, prefile, "");
07561                } else {
07562                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07563                   /* Say "from extension" as one saying to sound smoother */
07564                   if (!callback)
07565                      res = wait_file2(chan, vms, "vm-from-extension");
07566                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07567                }
07568             }
07569          }
07570       } else if (!res) {
07571          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07572          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07573          if (!callback)
07574             res = wait_file2(chan, vms, "vm-from-phonenumber");
07575          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07576       }
07577    } else {
07578       /* Number unknown */
07579       ast_debug(1, "VM-CID: From an unknown number\n");
07580       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07581       res = wait_file2(chan, vms, "vm-unknown-caller");
07582    }
07583    return res;
07584 }
07585 
07586 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07587 {
07588    int res = 0;
07589    int durationm;
07590    int durations;
07591    /* Verify that we have a duration for the message */
07592    if (duration == NULL)
07593       return res;
07594 
07595    /* Convert from seconds to minutes */
07596    durations = atoi(duration);
07597    durationm = (durations / 60);
07598 
07599    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07600 
07601    if ((!res) && (durationm >= minduration)) {
07602       res = wait_file2(chan, vms, "vm-duration");
07603 
07604       /* POLISH syntax */
07605       if (!strncasecmp(chan->language, "pl", 2)) {
07606          div_t num = div(durationm, 10);
07607 
07608          if (durationm == 1) {
07609             res = ast_play_and_wait(chan, "digits/1z");
07610             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07611          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07612             if (num.rem == 2) {
07613                if (!num.quot) {
07614                   res = ast_play_and_wait(chan, "digits/2-ie");
07615                } else {
07616                   res = say_and_wait(chan, durationm - 2 , chan->language);
07617                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07618                }
07619             } else {
07620                res = say_and_wait(chan, durationm, chan->language);
07621             }
07622             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07623          } else {
07624             res = say_and_wait(chan, durationm, chan->language);
07625             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07626          }
07627       /* DEFAULT syntax */
07628       } else {
07629          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07630          res = wait_file2(chan, vms, "vm-minutes");
07631       }
07632    }
07633    return res;
07634 }
07635 
07636 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07637 {
07638    int res = 0;
07639    char filename[256], *cid;
07640    const char *origtime, *context, *category, *duration, *flag;
07641    struct ast_config *msg_cfg;
07642    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07643 
07644    vms->starting = 0;
07645    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07646    adsi_message(chan, vms);
07647    if (!vms->curmsg) {
07648       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07649    } else if (vms->curmsg == vms->lastmsg) {
07650       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07651    }
07652 
07653    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07654    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07655    msg_cfg = ast_config_load(filename, config_flags);
07656    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07657       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07658       return 0;
07659    }
07660    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07661 
07662    /* Play the word urgent if we are listening to urgent messages */
07663    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07664       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07665    }
07666 
07667    if (!res) {
07668       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07669       /* POLISH syntax */
07670       if (!strncasecmp(chan->language, "pl", 2)) {
07671          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07672             int ten, one;
07673             char nextmsg[256];
07674             ten = (vms->curmsg + 1) / 10;
07675             one = (vms->curmsg + 1) % 10;
07676 
07677             if (vms->curmsg < 20) {
07678                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07679                res = wait_file2(chan, vms, nextmsg);
07680             } else {
07681                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07682                res = wait_file2(chan, vms, nextmsg);
07683                if (one > 0) {
07684                   if (!res) {
07685                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07686                      res = wait_file2(chan, vms, nextmsg);
07687                   }
07688                }
07689             }
07690          }
07691          if (!res)
07692             res = wait_file2(chan, vms, "vm-message");
07693       /* HEBREW syntax */
07694       } else if (!strncasecmp(chan->language, "he", 2)) {
07695          if (!vms->curmsg) {
07696             res = wait_file2(chan, vms, "vm-message");
07697             res = wait_file2(chan, vms, "vm-first");
07698          } else if (vms->curmsg == vms->lastmsg) {
07699             res = wait_file2(chan, vms, "vm-message");
07700             res = wait_file2(chan, vms, "vm-last");
07701          } else {
07702             res = wait_file2(chan, vms, "vm-message");
07703             res = wait_file2(chan, vms, "vm-number");
07704             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07705          }
07706       /* VIETNAMESE syntax */
07707       } else if (!strncasecmp(chan->language, "vi", 2)) {
07708          if (!vms->curmsg) {
07709             res = wait_file2(chan, vms, "vm-message");
07710             res = wait_file2(chan, vms, "vm-first");
07711          } else if (vms->curmsg == vms->lastmsg) {
07712             res = wait_file2(chan, vms, "vm-message");
07713             res = wait_file2(chan, vms, "vm-last");
07714          } else {
07715             res = wait_file2(chan, vms, "vm-message");
07716             res = wait_file2(chan, vms, "vm-number");
07717             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07718          }
07719       } else {
07720          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07721             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07722          } else { /* DEFAULT syntax */
07723             res = wait_file2(chan, vms, "vm-message");
07724          }
07725          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07726             if (!res) {
07727                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07728                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07729             }
07730          }
07731       }
07732    }
07733 
07734    if (!msg_cfg) {
07735       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07736       return 0;
07737    }
07738 
07739    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07740       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07741       DISPOSE(vms->curdir, vms->curmsg);
07742       ast_config_destroy(msg_cfg);
07743       return 0;
07744    }
07745 
07746    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07747    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07748    category = ast_variable_retrieve(msg_cfg, "message", "category");
07749 
07750    context = ast_variable_retrieve(msg_cfg, "message", "context");
07751    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07752       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07753    if (!res) {
07754       res = play_message_category(chan, category);
07755    }
07756    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07757       res = play_message_datetime(chan, vmu, origtime, filename);
07758    }
07759    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07760       res = play_message_callerid(chan, vms, cid, context, 0);
07761    }
07762    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07763       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07764    }
07765    /* Allow pressing '1' to skip envelope / callerid */
07766    if (res == '1') {
07767       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07768       res = 0;
07769    }
07770    ast_config_destroy(msg_cfg);
07771 
07772    if (!res) {
07773       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07774       vms->heard[vms->curmsg] = 1;
07775 #ifdef IMAP_STORAGE
07776       /*IMAP storage stores any prepended message from a forward
07777        * as a separate file from the rest of the message
07778        */
07779       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07780          wait_file(chan, vms, vms->introfn);
07781       }
07782 #endif
07783       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07784          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07785          res = 0;
07786       }
07787       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07788    }
07789    DISPOSE(vms->curdir, vms->curmsg);
07790    return res;
07791 }
07792 
07793 #ifdef IMAP_STORAGE
07794 static int imap_remove_file(char *dir, int msgnum)
07795 {
07796    char fn[PATH_MAX];
07797    char full_fn[PATH_MAX];
07798    char intro[PATH_MAX] = {0,};
07799    
07800    if (msgnum > -1) {
07801       make_file(fn, sizeof(fn), dir, msgnum);
07802       snprintf(intro, sizeof(intro), "%sintro", fn);
07803    } else
07804       ast_copy_string(fn, dir, sizeof(fn));
07805    
07806    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07807       ast_filedelete(fn, NULL);
07808       if (!ast_strlen_zero(intro)) {
07809          ast_filedelete(intro, NULL);
07810       }
07811       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07812       unlink(full_fn);
07813    }
07814    return 0;
07815 }
07816 
07817 
07818 
07819 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07820 {
07821    char *file, *filename;
07822    char *attachment;
07823    char arg[10];
07824    int i;
07825    BODY* body;
07826 
07827    file = strrchr(ast_strdupa(dir), '/');
07828    if (file) {
07829       *file++ = '\0';
07830    } else {
07831       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07832       return -1;
07833    }
07834 
07835    ast_mutex_lock(&vms->lock);
07836    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07837       mail_fetchstructure(vms->mailstream, i + 1, &body);
07838       /* We have the body, now we extract the file name of the first attachment. */
07839       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07840          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07841       } else {
07842          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07843          ast_mutex_unlock(&vms->lock);
07844          return -1;
07845       }
07846       filename = strsep(&attachment, ".");
07847       if (!strcmp(filename, file)) {
07848          sprintf(arg, "%d", i + 1);
07849          mail_setflag(vms->mailstream, arg, "\\DELETED");
07850       }
07851    }
07852    mail_expunge(vms->mailstream);
07853    ast_mutex_unlock(&vms->lock);
07854    return 0;
07855 }
07856 
07857 #elif !defined(IMAP_STORAGE)
07858 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07859 {
07860    int count_msg, last_msg;
07861 
07862    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07863 
07864    /* Rename the member vmbox HERE so that we don't try to return before
07865     * we know what's going on.
07866     */
07867    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07868 
07869    /* Faster to make the directory than to check if it exists. */
07870    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07871 
07872    /* traverses directory using readdir (or select query for ODBC) */
07873    count_msg = count_messages(vmu, vms->curdir);
07874    if (count_msg < 0) {
07875       return count_msg;
07876    } else {
07877       vms->lastmsg = count_msg - 1;
07878    }
07879 
07880    if (vm_allocate_dh(vms, vmu, count_msg)) {
07881       return -1;
07882    }
07883 
07884    /*
07885    The following test is needed in case sequencing gets messed up.
07886    There appears to be more than one way to mess up sequence, so
07887    we will not try to find all of the root causes--just fix it when
07888    detected.
07889    */
07890 
07891    if (vm_lock_path(vms->curdir)) {
07892       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07893       return ERROR_LOCK_PATH;
07894    }
07895 
07896    /* for local storage, checks directory for messages up to maxmsg limit */
07897    last_msg = last_message_index(vmu, vms->curdir);
07898    ast_unlock_path(vms->curdir);
07899 
07900    if (last_msg < -1) {
07901       return last_msg;
07902    } else if (vms->lastmsg != last_msg) {
07903       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);
07904       resequence_mailbox(vmu, vms->curdir, count_msg);
07905    }
07906 
07907    return 0;
07908 }
07909 #endif
07910 
07911 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07912 {
07913    int x = 0;
07914 
07915 #ifndef IMAP_STORAGE
07916    int last_msg_idx;
07917    int res = 0, nummsg;
07918    char fn2[PATH_MAX];
07919 #endif
07920 
07921    if (vms->lastmsg <= -1) {
07922       goto done;
07923    }
07924 
07925    vms->curmsg = -1;
07926 #ifndef IMAP_STORAGE
07927    /* Get the deleted messages fixed */
07928    if (vm_lock_path(vms->curdir)) {
07929       return ERROR_LOCK_PATH;
07930    }
07931 
07932    /* update count as message may have arrived while we've got mailbox open */
07933    last_msg_idx = last_message_index(vmu, vms->curdir);
07934    if (last_msg_idx != vms->lastmsg) {
07935       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07936    }
07937 
07938    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07939    for (x = 0; x < last_msg_idx + 1; x++) {
07940       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07941          /* Save this message.  It's not in INBOX or hasn't been heard */
07942          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07943          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07944             break;
07945          }
07946          vms->curmsg++;
07947          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07948          if (strcmp(vms->fn, fn2)) {
07949             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07950          }
07951       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07952          /* Move to old folder before deleting */
07953          res = save_to_folder(vmu, vms, x, 1);
07954          if (res == ERROR_LOCK_PATH) {
07955             /* If save failed do not delete the message */
07956             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07957             vms->deleted[x] = 0;
07958             vms->heard[x] = 0;
07959             --x;
07960          }
07961       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07962          /* Move to deleted folder */
07963          res = save_to_folder(vmu, vms, x, 10);
07964          if (res == ERROR_LOCK_PATH) {
07965             /* If save failed do not delete the message */
07966             vms->deleted[x] = 0;
07967             vms->heard[x] = 0;
07968             --x;
07969          }
07970       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07971          /* If realtime storage enabled - we should explicitly delete this message,
07972          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07973          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07974          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07975             DELETE(vms->curdir, x, vms->fn, vmu);
07976          }
07977       }
07978    }
07979 
07980    /* Delete ALL remaining messages */
07981    nummsg = x - 1;
07982    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07983       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07984       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07985          DELETE(vms->curdir, x, vms->fn, vmu);
07986       }
07987    }
07988    ast_unlock_path(vms->curdir);
07989 #else /* defined(IMAP_STORAGE) */
07990    if (vms->deleted) {
07991       /* Since we now expunge after each delete, deleting in reverse order
07992        * ensures that no reordering occurs between each step. */
07993       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07994          if (vms->deleted[x]) {
07995             ast_debug(3, "IMAP delete of %d\n", x);
07996             DELETE(vms->curdir, x, vms->fn, vmu);
07997          }
07998       }
07999    }
08000 #endif
08001 
08002 done:
08003    if (vms->deleted && vmu->maxmsg) {
08004       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
08005    }
08006    if (vms->heard && vmu->maxmsg) {
08007       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
08008    }
08009 
08010    return 0;
08011 }
08012 
08013 /* In Greek even though we CAN use a syntax like "friends messages"
08014  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08015  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08016  * syntax for the above three categories which is more elegant.
08017  */
08018 
08019 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08020 {
08021    int cmd;
08022    char *buf;
08023 
08024    buf = alloca(strlen(box) + 2);
08025    strcpy(buf, box);
08026    strcat(buf, "s");
08027 
08028    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08029       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08030       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08031    } else {
08032       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08033       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08034    }
08035 }
08036 
08037 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08038 {
08039    int cmd;
08040 
08041    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08042       if (!strcasecmp(box, "vm-INBOX"))
08043          cmd = ast_play_and_wait(chan, "vm-new-e");
08044       else
08045          cmd = ast_play_and_wait(chan, "vm-old-e");
08046       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08047    } else {
08048       cmd = ast_play_and_wait(chan, "vm-messages");
08049       return cmd ? cmd : ast_play_and_wait(chan, box);
08050    }
08051 }
08052 
08053 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08054 {
08055    int cmd;
08056 
08057    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08058       cmd = ast_play_and_wait(chan, "vm-messages");
08059       return cmd ? cmd : ast_play_and_wait(chan, box);
08060    } else {
08061       cmd = ast_play_and_wait(chan, box);
08062       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08063    }
08064 }
08065 
08066 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08067 {
08068    int cmd;
08069 
08070    if (  !strncasecmp(chan->language, "it", 2) ||
08071         !strncasecmp(chan->language, "es", 2) ||
08072         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08073       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08074       return cmd ? cmd : ast_play_and_wait(chan, box);
08075    } else if (!strncasecmp(chan->language, "gr", 2)) {
08076       return vm_play_folder_name_gr(chan, box);
08077    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08078       return ast_play_and_wait(chan, box);
08079    } else if (!strncasecmp(chan->language, "pl", 2)) {
08080       return vm_play_folder_name_pl(chan, box);
08081    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08082       return vm_play_folder_name_ua(chan, box);
08083    } else if (!strncasecmp(chan->language, "vi", 2)) {
08084       return ast_play_and_wait(chan, box);
08085    } else {  /* Default English */
08086       cmd = ast_play_and_wait(chan, box);
08087       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08088    }
08089 }
08090 
08091 /* GREEK SYNTAX
08092    In greek the plural for old/new is
08093    different so we need the following files
08094    We also need vm-denExeteMynhmata because
08095    this syntax is different.
08096 
08097    -> vm-Olds.wav : "Palia"
08098    -> vm-INBOXs.wav : "Nea"
08099    -> vm-denExeteMynhmata : "den exete mynhmata"
08100 */
08101 
08102 
08103 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08104 {
08105    int res = 0;
08106 
08107    if (vms->newmessages) {
08108       res = ast_play_and_wait(chan, "vm-youhave");
08109       if (!res) 
08110          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08111       if (!res) {
08112          if ((vms->newmessages == 1)) {
08113             res = ast_play_and_wait(chan, "vm-INBOX");
08114             if (!res)
08115                res = ast_play_and_wait(chan, "vm-message");
08116          } else {
08117             res = ast_play_and_wait(chan, "vm-INBOXs");
08118             if (!res)
08119                res = ast_play_and_wait(chan, "vm-messages");
08120          }
08121       }
08122    } else if (vms->oldmessages){
08123       res = ast_play_and_wait(chan, "vm-youhave");
08124       if (!res)
08125          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08126       if ((vms->oldmessages == 1)){
08127          res = ast_play_and_wait(chan, "vm-Old");
08128          if (!res)
08129             res = ast_play_and_wait(chan, "vm-message");
08130       } else {
08131          res = ast_play_and_wait(chan, "vm-Olds");
08132          if (!res)
08133             res = ast_play_and_wait(chan, "vm-messages");
08134       }
08135    } else if (!vms->oldmessages && !vms->newmessages) 
08136       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08137    return res;
08138 }
08139 
08140 /* Version of vm_intro() designed to work for many languages.
08141  *
08142  * It is hoped that this function can prevent the proliferation of 
08143  * language-specific vm_intro() functions and in time replace the language-
08144  * specific functions which already exist.  An examination of the language-
08145  * specific functions revealed that they all corrected the same deficiencies
08146  * in vm_intro_en() (which was the default function). Namely:
08147  *
08148  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08149  *     wording of the voicemail greeting hides this problem.  For example,
08150  *     vm-INBOX contains only the word "new".  This means that both of these
08151  *     sequences produce valid utterances:
08152  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08153  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08154  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08155  *     in many languages) the first utterance becomes "you have 1 the new message".
08156  *  2) The function contains hardcoded rules for pluralizing the word "message".
08157  *     These rules are correct for English, but not for many other languages.
08158  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08159  *     required in many languages.
08160  *  4) The gender of the word for "message" is not specified. This is a problem
08161  *     because in many languages the gender of the number in phrases such
08162  *     as "you have one new message" must match the gender of the word
08163  *     meaning "message".
08164  *
08165  * Fixing these problems for each new language has meant duplication of effort.
08166  * This new function solves the problems in the following general ways:
08167  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08168  *     and vm-Old respectively for those languages where it makes sense.
08169  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08170  *     on vm-message.
08171  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08172  *     prefix on vm-new and vm-old (none for English).
08173  *  4) Pass the gender of the language's word for "message" as an agument to
08174  *     this function which is can in turn pass on to the functions which 
08175  *     say numbers and put endings on nounds and adjectives.
08176  *
08177  * All languages require these messages:
08178  *  vm-youhave    "You have..."
08179  *  vm-and     "and"
08180  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08181  *
08182  * To use it for English, you will need these additional sound files:
08183  *  vm-new     "new"
08184  *  vm-message    "message", singular
08185  *  vm-messages      "messages", plural
08186  *
08187  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08188  *
08189  *  vm-newn    "novoye" (singular, neuter)
08190  *  vm-newx    "novikh" (counting plural form, genative plural)
08191  *  vm-message    "sobsheniye" (singular form)
08192  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08193  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08194  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08195  *  digits/2n     "dva" (neuter singular)
08196  */
08197 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08198 {
08199    int res;
08200    int lastnum = 0;
08201 
08202    res = ast_play_and_wait(chan, "vm-youhave");
08203 
08204    if (!res && vms->newmessages) {
08205       lastnum = vms->newmessages;
08206 
08207       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08208          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08209       }
08210 
08211       if (!res && vms->oldmessages) {
08212          res = ast_play_and_wait(chan, "vm-and");
08213       }
08214    }
08215 
08216    if (!res && vms->oldmessages) {
08217       lastnum = vms->oldmessages;
08218 
08219       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08220          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08221       }
08222    }
08223 
08224    if (!res) {
08225       if (lastnum == 0) {
08226          res = ast_play_and_wait(chan, "vm-no");
08227       }
08228       if (!res) {
08229          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08230       }
08231    }
08232 
08233    return res;
08234 }
08235 
08236 /* Default Hebrew syntax */
08237 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08238 {
08239    int res = 0;
08240 
08241    /* Introduce messages they have */
08242    if (!res) {
08243       if ((vms->newmessages) || (vms->oldmessages)) {
08244          res = ast_play_and_wait(chan, "vm-youhave");
08245       }
08246       /*
08247        * The word "shtei" refers to the number 2 in hebrew when performing a count
08248        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08249        * an element, this is one of them.
08250        */
08251       if (vms->newmessages) {
08252          if (!res) {
08253             if (vms->newmessages == 1) {
08254                res = ast_play_and_wait(chan, "vm-INBOX1");
08255             } else {
08256                if (vms->newmessages == 2) {
08257                   res = ast_play_and_wait(chan, "vm-shtei");
08258                } else {
08259                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08260                }
08261                res = ast_play_and_wait(chan, "vm-INBOX");
08262             }
08263          }
08264          if (vms->oldmessages && !res) {
08265             res = ast_play_and_wait(chan, "vm-and");
08266             if (vms->oldmessages == 1) {
08267                res = ast_play_and_wait(chan, "vm-Old1");
08268             } else {
08269                if (vms->oldmessages == 2) {
08270                   res = ast_play_and_wait(chan, "vm-shtei");
08271                } else {
08272                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08273                }
08274                res = ast_play_and_wait(chan, "vm-Old");
08275             }
08276          }
08277       }
08278       if (!res && vms->oldmessages && !vms->newmessages) {
08279          if (!res) {
08280             if (vms->oldmessages == 1) {
08281                res = ast_play_and_wait(chan, "vm-Old1");
08282             } else {
08283                if (vms->oldmessages == 2) {
08284                   res = ast_play_and_wait(chan, "vm-shtei");
08285                } else {
08286                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08287                }
08288                res = ast_play_and_wait(chan, "vm-Old");
08289             }
08290          }
08291       }
08292       if (!res) {
08293          if (!vms->oldmessages && !vms->newmessages) {
08294             if (!res) {
08295                res = ast_play_and_wait(chan, "vm-nomessages");
08296             }
08297          }
08298       }
08299    }
08300    return res;
08301 }
08302    
08303 /* Default English syntax */
08304 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08305 {
08306    int res;
08307 
08308    /* Introduce messages they have */
08309    res = ast_play_and_wait(chan, "vm-youhave");
08310    if (!res) {
08311       if (vms->urgentmessages) {
08312          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08313          if (!res)
08314             res = ast_play_and_wait(chan, "vm-Urgent");
08315          if ((vms->oldmessages || vms->newmessages) && !res) {
08316             res = ast_play_and_wait(chan, "vm-and");
08317          } else if (!res) {
08318             if ((vms->urgentmessages == 1))
08319                res = ast_play_and_wait(chan, "vm-message");
08320             else
08321                res = ast_play_and_wait(chan, "vm-messages");
08322          }
08323       }
08324       if (vms->newmessages) {
08325          res = say_and_wait(chan, vms->newmessages, chan->language);
08326          if (!res)
08327             res = ast_play_and_wait(chan, "vm-INBOX");
08328          if (vms->oldmessages && !res)
08329             res = ast_play_and_wait(chan, "vm-and");
08330          else if (!res) {
08331             if ((vms->newmessages == 1))
08332                res = ast_play_and_wait(chan, "vm-message");
08333             else
08334                res = ast_play_and_wait(chan, "vm-messages");
08335          }
08336             
08337       }
08338       if (!res && vms->oldmessages) {
08339          res = say_and_wait(chan, vms->oldmessages, chan->language);
08340          if (!res)
08341             res = ast_play_and_wait(chan, "vm-Old");
08342          if (!res) {
08343             if (vms->oldmessages == 1)
08344                res = ast_play_and_wait(chan, "vm-message");
08345             else
08346                res = ast_play_and_wait(chan, "vm-messages");
08347          }
08348       }
08349       if (!res) {
08350          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08351             res = ast_play_and_wait(chan, "vm-no");
08352             if (!res)
08353                res = ast_play_and_wait(chan, "vm-messages");
08354          }
08355       }
08356    }
08357    return res;
08358 }
08359 
08360 /* ITALIAN syntax */
08361 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08362 {
08363    /* Introduce messages they have */
08364    int res;
08365    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08366       res = ast_play_and_wait(chan, "vm-no") ||
08367          ast_play_and_wait(chan, "vm-message");
08368    else
08369       res = ast_play_and_wait(chan, "vm-youhave");
08370    if (!res && vms->newmessages) {
08371       res = (vms->newmessages == 1) ?
08372          ast_play_and_wait(chan, "digits/un") ||
08373          ast_play_and_wait(chan, "vm-nuovo") ||
08374          ast_play_and_wait(chan, "vm-message") :
08375          /* 2 or more new messages */
08376          say_and_wait(chan, vms->newmessages, chan->language) ||
08377          ast_play_and_wait(chan, "vm-nuovi") ||
08378          ast_play_and_wait(chan, "vm-messages");
08379       if (!res && vms->oldmessages)
08380          res = ast_play_and_wait(chan, "vm-and");
08381    }
08382    if (!res && vms->oldmessages) {
08383       res = (vms->oldmessages == 1) ?
08384          ast_play_and_wait(chan, "digits/un") ||
08385          ast_play_and_wait(chan, "vm-vecchio") ||
08386          ast_play_and_wait(chan, "vm-message") :
08387          /* 2 or more old messages */
08388          say_and_wait(chan, vms->oldmessages, chan->language) ||
08389          ast_play_and_wait(chan, "vm-vecchi") ||
08390          ast_play_and_wait(chan, "vm-messages");
08391    }
08392    return res;
08393 }
08394 
08395 /* POLISH syntax */
08396 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08397 {
08398    /* Introduce messages they have */
08399    int res;
08400    div_t num;
08401 
08402    if (!vms->oldmessages && !vms->newmessages) {
08403       res = ast_play_and_wait(chan, "vm-no");
08404       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08405       return res;
08406    } else {
08407       res = ast_play_and_wait(chan, "vm-youhave");
08408    }
08409 
08410    if (vms->newmessages) {
08411       num = div(vms->newmessages, 10);
08412       if (vms->newmessages == 1) {
08413          res = ast_play_and_wait(chan, "digits/1-a");
08414          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08415          res = res ? res : ast_play_and_wait(chan, "vm-message");
08416       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08417          if (num.rem == 2) {
08418             if (!num.quot) {
08419                res = ast_play_and_wait(chan, "digits/2-ie");
08420             } else {
08421                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08422                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08423             }
08424          } else {
08425             res = say_and_wait(chan, vms->newmessages, chan->language);
08426          }
08427          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08428          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08429       } else {
08430          res = say_and_wait(chan, vms->newmessages, chan->language);
08431          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08432          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08433       }
08434       if (!res && vms->oldmessages)
08435          res = ast_play_and_wait(chan, "vm-and");
08436    }
08437    if (!res && vms->oldmessages) {
08438       num = div(vms->oldmessages, 10);
08439       if (vms->oldmessages == 1) {
08440          res = ast_play_and_wait(chan, "digits/1-a");
08441          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08442          res = res ? res : ast_play_and_wait(chan, "vm-message");
08443       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08444          if (num.rem == 2) {
08445             if (!num.quot) {
08446                res = ast_play_and_wait(chan, "digits/2-ie");
08447             } else {
08448                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08449                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08450             }
08451          } else {
08452             res = say_and_wait(chan, vms->oldmessages, chan->language);
08453          }
08454          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08455          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08456       } else {
08457          res = say_and_wait(chan, vms->oldmessages, chan->language);
08458          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08459          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08460       }
08461    }
08462 
08463    return res;
08464 }
08465 
08466 /* SWEDISH syntax */
08467 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08468 {
08469    /* Introduce messages they have */
08470    int res;
08471 
08472    res = ast_play_and_wait(chan, "vm-youhave");
08473    if (res)
08474       return res;
08475 
08476    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08477       res = ast_play_and_wait(chan, "vm-no");
08478       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08479       return res;
08480    }
08481 
08482    if (vms->newmessages) {
08483       if ((vms->newmessages == 1)) {
08484          res = ast_play_and_wait(chan, "digits/ett");
08485          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08486          res = res ? res : ast_play_and_wait(chan, "vm-message");
08487       } else {
08488          res = say_and_wait(chan, vms->newmessages, chan->language);
08489          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08490          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08491       }
08492       if (!res && vms->oldmessages)
08493          res = ast_play_and_wait(chan, "vm-and");
08494    }
08495    if (!res && vms->oldmessages) {
08496       if (vms->oldmessages == 1) {
08497          res = ast_play_and_wait(chan, "digits/ett");
08498          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08499          res = res ? res : ast_play_and_wait(chan, "vm-message");
08500       } else {
08501          res = say_and_wait(chan, vms->oldmessages, chan->language);
08502          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08503          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08504       }
08505    }
08506 
08507    return res;
08508 }
08509 
08510 /* NORWEGIAN syntax */
08511 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08512 {
08513    /* Introduce messages they have */
08514    int res;
08515 
08516    res = ast_play_and_wait(chan, "vm-youhave");
08517    if (res)
08518       return res;
08519 
08520    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08521       res = ast_play_and_wait(chan, "vm-no");
08522       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08523       return res;
08524    }
08525 
08526    if (vms->newmessages) {
08527       if ((vms->newmessages == 1)) {
08528          res = ast_play_and_wait(chan, "digits/1");
08529          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08530          res = res ? res : ast_play_and_wait(chan, "vm-message");
08531       } else {
08532          res = say_and_wait(chan, vms->newmessages, chan->language);
08533          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08534          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08535       }
08536       if (!res && vms->oldmessages)
08537          res = ast_play_and_wait(chan, "vm-and");
08538    }
08539    if (!res && vms->oldmessages) {
08540       if (vms->oldmessages == 1) {
08541          res = ast_play_and_wait(chan, "digits/1");
08542          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08543          res = res ? res : ast_play_and_wait(chan, "vm-message");
08544       } else {
08545          res = say_and_wait(chan, vms->oldmessages, chan->language);
08546          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08547          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08548       }
08549    }
08550 
08551    return res;
08552 }
08553 
08554 /* GERMAN syntax */
08555 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08556 {
08557    /* Introduce messages they have */
08558    int res;
08559    res = ast_play_and_wait(chan, "vm-youhave");
08560    if (!res) {
08561       if (vms->newmessages) {
08562          if ((vms->newmessages == 1))
08563             res = ast_play_and_wait(chan, "digits/1F");
08564          else
08565             res = say_and_wait(chan, vms->newmessages, chan->language);
08566          if (!res)
08567             res = ast_play_and_wait(chan, "vm-INBOX");
08568          if (vms->oldmessages && !res)
08569             res = ast_play_and_wait(chan, "vm-and");
08570          else if (!res) {
08571             if ((vms->newmessages == 1))
08572                res = ast_play_and_wait(chan, "vm-message");
08573             else
08574                res = ast_play_and_wait(chan, "vm-messages");
08575          }
08576             
08577       }
08578       if (!res && vms->oldmessages) {
08579          if (vms->oldmessages == 1)
08580             res = ast_play_and_wait(chan, "digits/1F");
08581          else
08582             res = say_and_wait(chan, vms->oldmessages, chan->language);
08583          if (!res)
08584             res = ast_play_and_wait(chan, "vm-Old");
08585          if (!res) {
08586             if (vms->oldmessages == 1)
08587                res = ast_play_and_wait(chan, "vm-message");
08588             else
08589                res = ast_play_and_wait(chan, "vm-messages");
08590          }
08591       }
08592       if (!res) {
08593          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08594             res = ast_play_and_wait(chan, "vm-no");
08595             if (!res)
08596                res = ast_play_and_wait(chan, "vm-messages");
08597          }
08598       }
08599    }
08600    return res;
08601 }
08602 
08603 /* SPANISH syntax */
08604 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08605 {
08606    /* Introduce messages they have */
08607    int res;
08608    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08609       res = ast_play_and_wait(chan, "vm-youhaveno");
08610       if (!res)
08611          res = ast_play_and_wait(chan, "vm-messages");
08612    } else {
08613       res = ast_play_and_wait(chan, "vm-youhave");
08614    }
08615    if (!res) {
08616       if (vms->newmessages) {
08617          if (!res) {
08618             if ((vms->newmessages == 1)) {
08619                res = ast_play_and_wait(chan, "digits/1M");
08620                if (!res)
08621                   res = ast_play_and_wait(chan, "vm-message");
08622                if (!res)
08623                   res = ast_play_and_wait(chan, "vm-INBOXs");
08624             } else {
08625                res = say_and_wait(chan, vms->newmessages, chan->language);
08626                if (!res)
08627                   res = ast_play_and_wait(chan, "vm-messages");
08628                if (!res)
08629                   res = ast_play_and_wait(chan, "vm-INBOX");
08630             }
08631          }
08632          if (vms->oldmessages && !res)
08633             res = ast_play_and_wait(chan, "vm-and");
08634       }
08635       if (vms->oldmessages) {
08636          if (!res) {
08637             if (vms->oldmessages == 1) {
08638                res = ast_play_and_wait(chan, "digits/1M");
08639                if (!res)
08640                   res = ast_play_and_wait(chan, "vm-message");
08641                if (!res)
08642                   res = ast_play_and_wait(chan, "vm-Olds");
08643             } else {
08644                res = say_and_wait(chan, vms->oldmessages, chan->language);
08645                if (!res)
08646                   res = ast_play_and_wait(chan, "vm-messages");
08647                if (!res)
08648                   res = ast_play_and_wait(chan, "vm-Old");
08649             }
08650          }
08651       }
08652    }
08653 return res;
08654 }
08655 
08656 /* BRAZILIAN PORTUGUESE syntax */
08657 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08658    /* Introduce messages they have */
08659    int res;
08660    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08661       res = ast_play_and_wait(chan, "vm-nomessages");
08662       return res;
08663    } else {
08664       res = ast_play_and_wait(chan, "vm-youhave");
08665    }
08666    if (vms->newmessages) {
08667       if (!res)
08668          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08669       if ((vms->newmessages == 1)) {
08670          if (!res)
08671             res = ast_play_and_wait(chan, "vm-message");
08672          if (!res)
08673             res = ast_play_and_wait(chan, "vm-INBOXs");
08674       } else {
08675          if (!res)
08676             res = ast_play_and_wait(chan, "vm-messages");
08677          if (!res)
08678             res = ast_play_and_wait(chan, "vm-INBOX");
08679       }
08680       if (vms->oldmessages && !res)
08681          res = ast_play_and_wait(chan, "vm-and");
08682    }
08683    if (vms->oldmessages) {
08684       if (!res)
08685          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08686       if (vms->oldmessages == 1) {
08687          if (!res)
08688             res = ast_play_and_wait(chan, "vm-message");
08689          if (!res)
08690             res = ast_play_and_wait(chan, "vm-Olds");
08691       } else {
08692          if (!res)
08693             res = ast_play_and_wait(chan, "vm-messages");
08694          if (!res)
08695             res = ast_play_and_wait(chan, "vm-Old");
08696       }
08697    }
08698    return res;
08699 }
08700 
08701 /* FRENCH syntax */
08702 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08703 {
08704    /* Introduce messages they have */
08705    int res;
08706    res = ast_play_and_wait(chan, "vm-youhave");
08707    if (!res) {
08708       if (vms->newmessages) {
08709          res = say_and_wait(chan, vms->newmessages, chan->language);
08710          if (!res)
08711             res = ast_play_and_wait(chan, "vm-INBOX");
08712          if (vms->oldmessages && !res)
08713             res = ast_play_and_wait(chan, "vm-and");
08714          else if (!res) {
08715             if ((vms->newmessages == 1))
08716                res = ast_play_and_wait(chan, "vm-message");
08717             else
08718                res = ast_play_and_wait(chan, "vm-messages");
08719          }
08720             
08721       }
08722       if (!res && vms->oldmessages) {
08723          res = say_and_wait(chan, vms->oldmessages, chan->language);
08724          if (!res)
08725             res = ast_play_and_wait(chan, "vm-Old");
08726          if (!res) {
08727             if (vms->oldmessages == 1)
08728                res = ast_play_and_wait(chan, "vm-message");
08729             else
08730                res = ast_play_and_wait(chan, "vm-messages");
08731          }
08732       }
08733       if (!res) {
08734          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08735             res = ast_play_and_wait(chan, "vm-no");
08736             if (!res)
08737                res = ast_play_and_wait(chan, "vm-messages");
08738          }
08739       }
08740    }
08741    return res;
08742 }
08743 
08744 /* DUTCH syntax */
08745 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08746 {
08747    /* Introduce messages they have */
08748    int res;
08749    res = ast_play_and_wait(chan, "vm-youhave");
08750    if (!res) {
08751       if (vms->newmessages) {
08752          res = say_and_wait(chan, vms->newmessages, chan->language);
08753          if (!res) {
08754             if (vms->newmessages == 1)
08755                res = ast_play_and_wait(chan, "vm-INBOXs");
08756             else
08757                res = ast_play_and_wait(chan, "vm-INBOX");
08758          }
08759          if (vms->oldmessages && !res)
08760             res = ast_play_and_wait(chan, "vm-and");
08761          else if (!res) {
08762             if ((vms->newmessages == 1))
08763                res = ast_play_and_wait(chan, "vm-message");
08764             else
08765                res = ast_play_and_wait(chan, "vm-messages");
08766          }
08767             
08768       }
08769       if (!res && vms->oldmessages) {
08770          res = say_and_wait(chan, vms->oldmessages, chan->language);
08771          if (!res) {
08772             if (vms->oldmessages == 1)
08773                res = ast_play_and_wait(chan, "vm-Olds");
08774             else
08775                res = ast_play_and_wait(chan, "vm-Old");
08776          }
08777          if (!res) {
08778             if (vms->oldmessages == 1)
08779                res = ast_play_and_wait(chan, "vm-message");
08780             else
08781                res = ast_play_and_wait(chan, "vm-messages");
08782          }
08783       }
08784       if (!res) {
08785          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08786             res = ast_play_and_wait(chan, "vm-no");
08787             if (!res)
08788                res = ast_play_and_wait(chan, "vm-messages");
08789          }
08790       }
08791    }
08792    return res;
08793 }
08794 
08795 /* PORTUGUESE syntax */
08796 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08797 {
08798    /* Introduce messages they have */
08799    int res;
08800    res = ast_play_and_wait(chan, "vm-youhave");
08801    if (!res) {
08802       if (vms->newmessages) {
08803          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08804          if (!res) {
08805             if ((vms->newmessages == 1)) {
08806                res = ast_play_and_wait(chan, "vm-message");
08807                if (!res)
08808                   res = ast_play_and_wait(chan, "vm-INBOXs");
08809             } else {
08810                res = ast_play_and_wait(chan, "vm-messages");
08811                if (!res)
08812                   res = ast_play_and_wait(chan, "vm-INBOX");
08813             }
08814          }
08815          if (vms->oldmessages && !res)
08816             res = ast_play_and_wait(chan, "vm-and");
08817       }
08818       if (!res && vms->oldmessages) {
08819          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08820          if (!res) {
08821             if (vms->oldmessages == 1) {
08822                res = ast_play_and_wait(chan, "vm-message");
08823                if (!res)
08824                   res = ast_play_and_wait(chan, "vm-Olds");
08825             } else {
08826                res = ast_play_and_wait(chan, "vm-messages");
08827                if (!res)
08828                   res = ast_play_and_wait(chan, "vm-Old");
08829             }
08830          }
08831       }
08832       if (!res) {
08833          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08834             res = ast_play_and_wait(chan, "vm-no");
08835             if (!res)
08836                res = ast_play_and_wait(chan, "vm-messages");
08837          }
08838       }
08839    }
08840    return res;
08841 }
08842 
08843 
08844 /* CZECH syntax */
08845 /* in czech there must be declension of word new and message
08846  * czech        : english        : czech      : english
08847  * --------------------------------------------------------
08848  * vm-youhave   : you have 
08849  * vm-novou     : one new        : vm-zpravu  : message
08850  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08851  * vm-novych    : 5-infinite new : vm-zprav   : messages
08852  * vm-starou   : one old
08853  * vm-stare     : 2-4 old 
08854  * vm-starych   : 5-infinite old
08855  * jednu        : one   - falling 4. 
08856  * vm-no        : no  ( no messages )
08857  */
08858 
08859 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08860 {
08861    int res;
08862    res = ast_play_and_wait(chan, "vm-youhave");
08863    if (!res) {
08864       if (vms->newmessages) {
08865          if (vms->newmessages == 1) {
08866             res = ast_play_and_wait(chan, "digits/jednu");
08867          } else {
08868             res = say_and_wait(chan, vms->newmessages, chan->language);
08869          }
08870          if (!res) {
08871             if ((vms->newmessages == 1))
08872                res = ast_play_and_wait(chan, "vm-novou");
08873             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08874                res = ast_play_and_wait(chan, "vm-nove");
08875             if (vms->newmessages > 4)
08876                res = ast_play_and_wait(chan, "vm-novych");
08877          }
08878          if (vms->oldmessages && !res)
08879             res = ast_play_and_wait(chan, "vm-and");
08880          else if (!res) {
08881             if ((vms->newmessages == 1))
08882                res = ast_play_and_wait(chan, "vm-zpravu");
08883             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08884                res = ast_play_and_wait(chan, "vm-zpravy");
08885             if (vms->newmessages > 4)
08886                res = ast_play_and_wait(chan, "vm-zprav");
08887          }
08888       }
08889       if (!res && vms->oldmessages) {
08890          res = say_and_wait(chan, vms->oldmessages, chan->language);
08891          if (!res) {
08892             if ((vms->oldmessages == 1))
08893                res = ast_play_and_wait(chan, "vm-starou");
08894             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08895                res = ast_play_and_wait(chan, "vm-stare");
08896             if (vms->oldmessages > 4)
08897                res = ast_play_and_wait(chan, "vm-starych");
08898          }
08899          if (!res) {
08900             if ((vms->oldmessages == 1))
08901                res = ast_play_and_wait(chan, "vm-zpravu");
08902             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08903                res = ast_play_and_wait(chan, "vm-zpravy");
08904             if (vms->oldmessages > 4)
08905                res = ast_play_and_wait(chan, "vm-zprav");
08906          }
08907       }
08908       if (!res) {
08909          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08910             res = ast_play_and_wait(chan, "vm-no");
08911             if (!res)
08912                res = ast_play_and_wait(chan, "vm-zpravy");
08913          }
08914       }
08915    }
08916    return res;
08917 }
08918 
08919 /* CHINESE (Taiwan) syntax */
08920 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08921 {
08922    int res;
08923    /* Introduce messages they have */
08924    res = ast_play_and_wait(chan, "vm-you");
08925 
08926    if (!res && vms->newmessages) {
08927       res = ast_play_and_wait(chan, "vm-have");
08928       if (!res)
08929          res = say_and_wait(chan, vms->newmessages, chan->language);
08930       if (!res)
08931          res = ast_play_and_wait(chan, "vm-tong");
08932       if (!res)
08933          res = ast_play_and_wait(chan, "vm-INBOX");
08934       if (vms->oldmessages && !res)
08935          res = ast_play_and_wait(chan, "vm-and");
08936       else if (!res) 
08937          res = ast_play_and_wait(chan, "vm-messages");
08938    }
08939    if (!res && vms->oldmessages) {
08940       res = ast_play_and_wait(chan, "vm-have");
08941       if (!res)
08942          res = say_and_wait(chan, vms->oldmessages, chan->language);
08943       if (!res)
08944          res = ast_play_and_wait(chan, "vm-tong");
08945       if (!res)
08946          res = ast_play_and_wait(chan, "vm-Old");
08947       if (!res)
08948          res = ast_play_and_wait(chan, "vm-messages");
08949    }
08950    if (!res && !vms->oldmessages && !vms->newmessages) {
08951       res = ast_play_and_wait(chan, "vm-haveno");
08952       if (!res)
08953          res = ast_play_and_wait(chan, "vm-messages");
08954    }
08955    return res;
08956 }
08957 
08958 /* Vietnamese syntax */
08959 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08960 {
08961    int res;
08962 
08963    /* Introduce messages they have */
08964    res = ast_play_and_wait(chan, "vm-youhave");
08965    if (!res) {
08966       if (vms->newmessages) {
08967          res = say_and_wait(chan, vms->newmessages, chan->language);
08968          if (!res)
08969             res = ast_play_and_wait(chan, "vm-INBOX");
08970          if (vms->oldmessages && !res)
08971             res = ast_play_and_wait(chan, "vm-and");
08972       }
08973       if (!res && vms->oldmessages) {
08974          res = say_and_wait(chan, vms->oldmessages, chan->language);
08975          if (!res)
08976             res = ast_play_and_wait(chan, "vm-Old");        
08977       }
08978       if (!res) {
08979          if (!vms->oldmessages && !vms->newmessages) {
08980             res = ast_play_and_wait(chan, "vm-no");
08981             if (!res)
08982                res = ast_play_and_wait(chan, "vm-message");
08983          }
08984       }
08985    }
08986    return res;
08987 }
08988 
08989 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08990 {
08991    char prefile[256];
08992    
08993    /* Notify the user that the temp greeting is set and give them the option to remove it */
08994    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08995    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08996       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08997       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08998          ast_play_and_wait(chan, "vm-tempgreetactive");
08999       }
09000       DISPOSE(prefile, -1);
09001    }
09002 
09003    /* Play voicemail intro - syntax is different for different languages */
09004    if (0) {
09005       return 0;
09006    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09007       return vm_intro_cs(chan, vms);
09008    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09009       static int deprecation_warning = 0;
09010       if (deprecation_warning++ % 10 == 0) {
09011          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09012       }
09013       return vm_intro_cs(chan, vms);
09014    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09015       return vm_intro_de(chan, vms);
09016    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09017       return vm_intro_es(chan, vms);
09018    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09019       return vm_intro_fr(chan, vms);
09020    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09021       return vm_intro_gr(chan, vms);
09022    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09023       return vm_intro_he(chan, vms);
09024    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09025       return vm_intro_it(chan, vms);
09026    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09027       return vm_intro_nl(chan, vms);
09028    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09029       return vm_intro_no(chan, vms);
09030    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09031       return vm_intro_pl(chan, vms);
09032    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09033       return vm_intro_pt_BR(chan, vms);
09034    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09035       return vm_intro_pt(chan, vms);
09036    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09037       return vm_intro_multilang(chan, vms, "n");
09038    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09039       return vm_intro_se(chan, vms);
09040    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09041       return vm_intro_multilang(chan, vms, "n");
09042    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09043       return vm_intro_vi(chan, vms);
09044    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09045       return vm_intro_zh(chan, vms);
09046    } else {                                             /* Default to ENGLISH */
09047       return vm_intro_en(chan, vms);
09048    }
09049 }
09050 
09051 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09052 {
09053    int res = 0;
09054    /* Play instructions and wait for new command */
09055    while (!res) {
09056       if (vms->starting) {
09057          if (vms->lastmsg > -1) {
09058             if (skipadvanced)
09059                res = ast_play_and_wait(chan, "vm-onefor-full");
09060             else
09061                res = ast_play_and_wait(chan, "vm-onefor");
09062             if (!res)
09063                res = vm_play_folder_name(chan, vms->vmbox);
09064          }
09065          if (!res) {
09066             if (skipadvanced)
09067                res = ast_play_and_wait(chan, "vm-opts-full");
09068             else
09069                res = ast_play_and_wait(chan, "vm-opts");
09070          }
09071       } else {
09072          /* Added for additional help */
09073          if (skipadvanced) {
09074             res = ast_play_and_wait(chan, "vm-onefor-full");
09075             if (!res)
09076                res = vm_play_folder_name(chan, vms->vmbox);
09077             res = ast_play_and_wait(chan, "vm-opts-full");
09078          }
09079          /* Logic:
09080           * If the current message is not the first OR
09081           * if we're listening to the first new message and there are
09082           * also urgent messages, then prompt for navigation to the
09083           * previous message
09084           */
09085          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09086             res = ast_play_and_wait(chan, "vm-prev");
09087          }
09088          if (!res && !skipadvanced)
09089             res = ast_play_and_wait(chan, "vm-advopts");
09090          if (!res)
09091             res = ast_play_and_wait(chan, "vm-repeat");
09092          /* Logic:
09093           * If we're not listening to the last message OR
09094           * we're listening to the last urgent message and there are
09095           * also new non-urgent messages, then prompt for navigation
09096           * to the next message
09097           */
09098          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09099             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09100             res = ast_play_and_wait(chan, "vm-next");
09101          }
09102          if (!res) {
09103             if (!vms->deleted[vms->curmsg])
09104                res = ast_play_and_wait(chan, "vm-delete");
09105             else
09106                res = ast_play_and_wait(chan, "vm-undelete");
09107             if (!res)
09108                res = ast_play_and_wait(chan, "vm-toforward");
09109             if (!res)
09110                res = ast_play_and_wait(chan, "vm-savemessage");
09111          }
09112       }
09113       if (!res) {
09114          res = ast_play_and_wait(chan, "vm-helpexit");
09115       }
09116       if (!res)
09117          res = ast_waitfordigit(chan, 6000);
09118       if (!res) {
09119          vms->repeats++;
09120          if (vms->repeats > 2) {
09121             res = 't';
09122          }
09123       }
09124    }
09125    return res;
09126 }
09127 
09128 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09129 {
09130    int res = 0;
09131    /* Play instructions and wait for new command */
09132    while (!res) {
09133       if (vms->lastmsg > -1) {
09134          res = ast_play_and_wait(chan, "vm-listen");
09135          if (!res)
09136             res = vm_play_folder_name(chan, vms->vmbox);
09137          if (!res)
09138             res = ast_play_and_wait(chan, "press");
09139          if (!res)
09140             res = ast_play_and_wait(chan, "digits/1");
09141       }
09142       if (!res)
09143          res = ast_play_and_wait(chan, "vm-opts");
09144       if (!res) {
09145          vms->starting = 0;
09146          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09147       }
09148    }
09149    return res;
09150 }
09151 
09152 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09153 {
09154    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09155       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09156    } else {             /* Default to ENGLISH */
09157       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09158    }
09159 }
09160 
09161 
09162 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09163 {
09164    int cmd = 0;
09165    int duration = 0;
09166    int tries = 0;
09167    char newpassword[80] = "";
09168    char newpassword2[80] = "";
09169    char prefile[PATH_MAX] = "";
09170    unsigned char buf[256];
09171    int bytes = 0;
09172 
09173    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09174    if (ast_adsi_available(chan)) {
09175       bytes += adsi_logo(buf + bytes);
09176       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09177       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09178       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09179       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09180       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09181    }
09182 
09183    /* If forcename is set, have the user record their name */
09184    if (ast_test_flag(vmu, VM_FORCENAME)) {
09185       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09186       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09187          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09188          if (cmd < 0 || cmd == 't' || cmd == '#')
09189             return cmd;
09190       }
09191    }
09192 
09193    /* If forcegreetings is set, have the user record their greetings */
09194    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09195       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09196       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09197          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09198          if (cmd < 0 || cmd == 't' || cmd == '#')
09199             return cmd;
09200       }
09201 
09202       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09203       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09204          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09205          if (cmd < 0 || cmd == 't' || cmd == '#')
09206             return cmd;
09207       }
09208    }
09209 
09210    /*
09211     * Change the password last since new users will be able to skip over any steps this one comes before
09212     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09213     */
09214    for (;;) {
09215       newpassword[1] = '\0';
09216       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09217       if (cmd == '#')
09218          newpassword[0] = '\0';
09219       if (cmd < 0 || cmd == 't' || cmd == '#')
09220          return cmd;
09221       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09222       if (cmd < 0 || cmd == 't' || cmd == '#')
09223          return cmd;
09224       cmd = check_password(vmu, newpassword); /* perform password validation */
09225       if (cmd != 0) {
09226          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09227          cmd = ast_play_and_wait(chan, vm_invalid_password);
09228       } else {
09229          newpassword2[1] = '\0';
09230          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09231          if (cmd == '#')
09232             newpassword2[0] = '\0';
09233          if (cmd < 0 || cmd == 't' || cmd == '#')
09234             return cmd;
09235          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09236          if (cmd < 0 || cmd == 't' || cmd == '#')
09237             return cmd;
09238          if (!strcmp(newpassword, newpassword2))
09239             break;
09240          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09241          cmd = ast_play_and_wait(chan, vm_mismatch);
09242       }
09243       if (++tries == 3)
09244          return -1;
09245       if (cmd != 0) {
09246          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09247       }
09248    }
09249    if (pwdchange & PWDCHANGE_INTERNAL)
09250       vm_change_password(vmu, newpassword);
09251    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09252       vm_change_password_shell(vmu, newpassword);
09253 
09254    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09255    cmd = ast_play_and_wait(chan, vm_passchanged);
09256 
09257    return cmd;
09258 }
09259 
09260 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09261 {
09262    int cmd = 0;
09263    int retries = 0;
09264    int duration = 0;
09265    char newpassword[80] = "";
09266    char newpassword2[80] = "";
09267    char prefile[PATH_MAX] = "";
09268    unsigned char buf[256];
09269    int bytes = 0;
09270 
09271    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09272    if (ast_adsi_available(chan)) {
09273       bytes += adsi_logo(buf + bytes);
09274       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09275       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09276       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09277       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09278       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09279    }
09280    while ((cmd >= 0) && (cmd != 't')) {
09281       if (cmd)
09282          retries = 0;
09283       switch (cmd) {
09284       case '1': /* Record your unavailable message */
09285          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09286          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09287          break;
09288       case '2':  /* Record your busy message */
09289          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09290          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09291          break;
09292       case '3': /* Record greeting */
09293          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09294          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09295          break;
09296       case '4':  /* manage the temporary greeting */
09297          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09298          break;
09299       case '5': /* change password */
09300          if (vmu->password[0] == '-') {
09301             cmd = ast_play_and_wait(chan, "vm-no");
09302             break;
09303          }
09304          newpassword[1] = '\0';
09305          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09306          if (cmd == '#')
09307             newpassword[0] = '\0';
09308          else {
09309             if (cmd < 0)
09310                break;
09311             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09312                break;
09313             }
09314          }
09315          cmd = check_password(vmu, newpassword); /* perform password validation */
09316          if (cmd != 0) {
09317             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09318             cmd = ast_play_and_wait(chan, vm_invalid_password);
09319             if (!cmd) {
09320                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09321             }
09322             break;
09323          }
09324          newpassword2[1] = '\0';
09325          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09326          if (cmd == '#')
09327             newpassword2[0] = '\0';
09328          else {
09329             if (cmd < 0)
09330                break;
09331 
09332             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09333                break;
09334             }
09335          }
09336          if (strcmp(newpassword, newpassword2)) {
09337             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09338             cmd = ast_play_and_wait(chan, vm_mismatch);
09339             if (!cmd) {
09340                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09341             }
09342             break;
09343          }
09344 
09345          if (pwdchange & PWDCHANGE_INTERNAL) {
09346             vm_change_password(vmu, newpassword);
09347          }
09348          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09349             vm_change_password_shell(vmu, newpassword);
09350          }
09351 
09352          ast_debug(1, "User %s set password to %s of length %d\n",
09353             vms->username, newpassword, (int) strlen(newpassword));
09354          cmd = ast_play_and_wait(chan, vm_passchanged);
09355          break;
09356       case '*': 
09357          cmd = 't';
09358          break;
09359       default: 
09360          cmd = 0;
09361          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09362          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09363          if (ast_fileexists(prefile, NULL, NULL)) {
09364             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09365          }
09366          DISPOSE(prefile, -1);
09367          if (!cmd) {
09368             cmd = ast_play_and_wait(chan, "vm-options");
09369          }
09370          if (!cmd) {
09371             cmd = ast_waitfordigit(chan, 6000);
09372          }
09373          if (!cmd) {
09374             retries++;
09375          }
09376          if (retries > 3) {
09377             cmd = 't';
09378          }
09379          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09380       }
09381    }
09382    if (cmd == 't')
09383       cmd = 0;
09384    return cmd;
09385 }
09386 
09387 /*!
09388  * \brief The handler for 'record a temporary greeting'. 
09389  * \param chan
09390  * \param vmu
09391  * \param vms
09392  * \param fmtc
09393  * \param record_gain
09394  *
09395  * This is option 4 from the mailbox options menu.
09396  * This function manages the following promptings:
09397  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09398  * 2: remove (delete) the temporary greeting.
09399  * *: return to the main menu.
09400  *
09401  * \return zero on success, -1 on error.
09402  */
09403 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09404 {
09405    int cmd = 0;
09406    int retries = 0;
09407    int duration = 0;
09408    char prefile[PATH_MAX] = "";
09409    unsigned char buf[256];
09410    int bytes = 0;
09411 
09412    if (ast_adsi_available(chan)) {
09413       bytes += adsi_logo(buf + bytes);
09414       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09415       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09416       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09417       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09418       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09419    }
09420 
09421    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09422    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09423    while ((cmd >= 0) && (cmd != 't')) {
09424       if (cmd)
09425          retries = 0;
09426       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09427       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09428          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09429          cmd = 't';  
09430       } else {
09431          switch (cmd) {
09432          case '1':
09433             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09434             break;
09435          case '2':
09436             DELETE(prefile, -1, prefile, vmu);
09437             ast_play_and_wait(chan, "vm-tempremoved");
09438             cmd = 't';  
09439             break;
09440          case '*': 
09441             cmd = 't';
09442             break;
09443          default:
09444             cmd = ast_play_and_wait(chan,
09445                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09446                   "vm-tempgreeting2" : "vm-tempgreeting");
09447             if (!cmd) {
09448                cmd = ast_waitfordigit(chan, 6000);
09449             }
09450             if (!cmd) {
09451                retries++;
09452             }
09453             if (retries > 3) {
09454                cmd = 't';
09455             }
09456             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09457          }
09458       }
09459       DISPOSE(prefile, -1);
09460    }
09461    if (cmd == 't')
09462       cmd = 0;
09463    return cmd;
09464 }
09465 
09466 /*!
09467  * \brief Greek syntax for 'You have N messages' greeting.
09468  * \param chan
09469  * \param vms
09470  * \param vmu
09471  *
09472  * \return zero on success, -1 on error.
09473  */   
09474 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09475 {
09476    int cmd = 0;
09477 
09478    if (vms->lastmsg > -1) {
09479       cmd = play_message(chan, vmu, vms);
09480    } else {
09481       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09482       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09483          if (!cmd) {
09484             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09485             cmd = ast_play_and_wait(chan, vms->fn);
09486          }
09487          if (!cmd)
09488             cmd = ast_play_and_wait(chan, "vm-messages");
09489       } else {
09490          if (!cmd)
09491             cmd = ast_play_and_wait(chan, "vm-messages");
09492          if (!cmd) {
09493             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09494             cmd = ast_play_and_wait(chan, vms->fn);
09495          }
09496       }
09497    } 
09498    return cmd;
09499 }
09500 
09501 /* Hebrew Syntax */
09502 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09503 {
09504    int cmd = 0;
09505 
09506    if (vms->lastmsg > -1) {
09507       cmd = play_message(chan, vmu, vms);
09508    } else {
09509       if (!strcasecmp(vms->fn, "INBOX")) {
09510          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09511       } else {
09512          cmd = ast_play_and_wait(chan, "vm-nomessages");
09513       }
09514    }
09515    return cmd;
09516 }
09517 
09518 /*! 
09519  * \brief Default English syntax for 'You have N messages' greeting.
09520  * \param chan
09521  * \param vms
09522  * \param vmu
09523  *
09524  * \return zero on success, -1 on error.
09525  */
09526 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09527 {
09528    int cmd = 0;
09529 
09530    if (vms->lastmsg > -1) {
09531       cmd = play_message(chan, vmu, vms);
09532    } else {
09533       cmd = ast_play_and_wait(chan, "vm-youhave");
09534       if (!cmd) 
09535          cmd = ast_play_and_wait(chan, "vm-no");
09536       if (!cmd) {
09537          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09538          cmd = ast_play_and_wait(chan, vms->fn);
09539       }
09540       if (!cmd)
09541          cmd = ast_play_and_wait(chan, "vm-messages");
09542    }
09543    return cmd;
09544 }
09545 
09546 /*! 
09547  *\brief Italian syntax for 'You have N messages' greeting.
09548  * \param chan
09549  * \param vms
09550  * \param vmu
09551  *
09552  * \return zero on success, -1 on error.
09553  */
09554 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09555 {
09556    int cmd;
09557 
09558    if (vms->lastmsg > -1) {
09559       cmd = play_message(chan, vmu, vms);
09560    } else {
09561       cmd = ast_play_and_wait(chan, "vm-no");
09562       if (!cmd)
09563          cmd = ast_play_and_wait(chan, "vm-message");
09564       if (!cmd) {
09565          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09566          cmd = ast_play_and_wait(chan, vms->fn);
09567       }
09568    }
09569    return cmd;
09570 }
09571 
09572 /*! 
09573  * \brief Spanish syntax for 'You have N messages' greeting.
09574  * \param chan
09575  * \param vms
09576  * \param vmu
09577  *
09578  * \return zero on success, -1 on error.
09579  */
09580 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09581 {
09582    int cmd;
09583 
09584    if (vms->lastmsg > -1) {
09585       cmd = play_message(chan, vmu, vms);
09586    } else {
09587       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09588       if (!cmd)
09589          cmd = ast_play_and_wait(chan, "vm-messages");
09590       if (!cmd) {
09591          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09592          cmd = ast_play_and_wait(chan, vms->fn);
09593       }
09594    }
09595    return cmd;
09596 }
09597 
09598 /*! 
09599  * \brief Portuguese syntax for 'You have N messages' greeting.
09600  * \param chan
09601  * \param vms
09602  * \param vmu
09603  *
09604  * \return zero on success, -1 on error.
09605  */
09606 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09607 {
09608    int cmd;
09609 
09610    if (vms->lastmsg > -1) {
09611       cmd = play_message(chan, vmu, vms);
09612    } else {
09613       cmd = ast_play_and_wait(chan, "vm-no");
09614       if (!cmd) {
09615          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09616          cmd = ast_play_and_wait(chan, vms->fn);
09617       }
09618       if (!cmd)
09619          cmd = ast_play_and_wait(chan, "vm-messages");
09620    }
09621    return cmd;
09622 }
09623 
09624 /*! 
09625  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09626  * \param chan
09627  * \param vms
09628  * \param vmu
09629  *
09630  * \return zero on success, -1 on error.
09631  */
09632 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09633 {
09634    int cmd;
09635 
09636    if (vms->lastmsg > -1) {
09637       cmd = play_message(chan, vmu, vms);
09638    } else {
09639       cmd = ast_play_and_wait(chan, "vm-you");
09640       if (!cmd) 
09641          cmd = ast_play_and_wait(chan, "vm-haveno");
09642       if (!cmd)
09643          cmd = ast_play_and_wait(chan, "vm-messages");
09644       if (!cmd) {
09645          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09646          cmd = ast_play_and_wait(chan, vms->fn);
09647       }
09648    }
09649    return cmd;
09650 }
09651 
09652 /*! 
09653  * \brief Vietnamese syntax for 'You have N messages' greeting.
09654  * \param chan
09655  * \param vms
09656  * \param vmu
09657  *
09658  * \return zero on success, -1 on error.
09659  */
09660 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09661 {
09662    int cmd = 0;
09663 
09664    if (vms->lastmsg > -1) {
09665       cmd = play_message(chan, vmu, vms);
09666    } else {
09667       cmd = ast_play_and_wait(chan, "vm-no");
09668       if (!cmd) {
09669          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09670          cmd = ast_play_and_wait(chan, vms->fn);
09671       }
09672    }
09673    return cmd;
09674 }
09675 
09676 /*!
09677  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09678  * \param chan The channel for the current user. We read the language property from this.
09679  * \param vms passed into the language-specific vm_browse_messages function.
09680  * \param vmu passed into the language-specific vm_browse_messages function.
09681  * 
09682  * The method to be invoked is determined by the value of language code property in the user's channel.
09683  * The default (when unable to match) is to use english.
09684  *
09685  * \return zero on success, -1 on error.
09686  */
09687 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09688 {
09689    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09690       return vm_browse_messages_es(chan, vms, vmu);
09691    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09692       return vm_browse_messages_gr(chan, vms, vmu);
09693    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09694       return vm_browse_messages_he(chan, vms, vmu);
09695    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09696       return vm_browse_messages_it(chan, vms, vmu);
09697    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09698       return vm_browse_messages_pt(chan, vms, vmu);
09699    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09700       return vm_browse_messages_vi(chan, vms, vmu);
09701    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09702       return vm_browse_messages_zh(chan, vms, vmu);
09703    } else {                                             /* Default to English syntax */
09704       return vm_browse_messages_en(chan, vms, vmu);
09705    }
09706 }
09707 
09708 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09709          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09710          int skipuser, int max_logins, int silent)
09711 {
09712    int useadsi = 0, valid = 0, logretries = 0;
09713    char password[AST_MAX_EXTENSION]="", *passptr;
09714    struct ast_vm_user vmus, *vmu = NULL;
09715 
09716    /* If ADSI is supported, setup login screen */
09717    adsi_begin(chan, &useadsi);
09718    if (!skipuser && useadsi)
09719       adsi_login(chan);
09720    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09721    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09722       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09723       return -1;
09724    }
09725 
09726    /* Authenticate them and get their mailbox/password */
09727 
09728    while (!valid && (logretries < max_logins)) {
09729       /* Prompt for, and read in the username */
09730       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09731          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09732          return -1;
09733       }
09734       if (ast_strlen_zero(mailbox)) {
09735          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09736             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09737          } else {
09738             ast_verb(3, "Username not entered\n"); 
09739             return -1;
09740          }
09741       } else if (mailbox[0] == '*') {
09742          /* user entered '*' */
09743          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09744          if (ast_exists_extension(chan, chan->context, "a", 1,
09745             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09746             return -1;
09747          }
09748          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09749          mailbox[0] = '\0';
09750       }
09751 
09752       if (useadsi)
09753          adsi_password(chan);
09754 
09755       if (!ast_strlen_zero(prefix)) {
09756          char fullusername[80] = "";
09757          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09758          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09759          ast_copy_string(mailbox, fullusername, mailbox_size);
09760       }
09761 
09762       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09763       vmu = find_user(&vmus, context, mailbox);
09764       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09765          /* saved password is blank, so don't bother asking */
09766          password[0] = '\0';
09767       } else {
09768          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09769          if (ast_streamfile(chan, vm_password, chan->language)) {
09770             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09771             return -1;
09772          }
09773          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09774             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09775             return -1;
09776          } else if (password[0] == '*') {
09777             /* user entered '*' */
09778             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09779             if (ast_exists_extension(chan, chan->context, "a", 1,
09780                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09781                mailbox[0] = '*';
09782                return -1;
09783             }
09784             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09785             mailbox[0] = '\0';
09786             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09787             vmu = NULL;
09788          }
09789       }
09790 
09791       if (vmu) {
09792          passptr = vmu->password;
09793          if (passptr[0] == '-') passptr++;
09794       }
09795       if (vmu && !strcmp(passptr, password))
09796          valid++;
09797       else {
09798          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09799          if (!ast_strlen_zero(prefix))
09800             mailbox[0] = '\0';
09801       }
09802       logretries++;
09803       if (!valid) {
09804          if (skipuser || logretries >= max_logins) {
09805             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09806             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09807                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09808                return -1;
09809             }
09810          } else {
09811             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09812             if (useadsi)
09813                adsi_login(chan);
09814             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09815                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09816                return -1;
09817             }
09818          }
09819          if (ast_waitstream(chan, "")) /* Channel is hung up */
09820             return -1;
09821       }
09822    }
09823    if (!valid && (logretries >= max_logins)) {
09824       ast_stopstream(chan);
09825       ast_play_and_wait(chan, "vm-goodbye");
09826       return -1;
09827    }
09828    if (vmu && !skipuser) {
09829       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09830    }
09831    return 0;
09832 }
09833 
09834 static int vm_execmain(struct ast_channel *chan, const char *data)
09835 {
09836    /* XXX This is, admittedly, some pretty horrendous code.  For some
09837       reason it just seemed a lot easier to do with GOTO's.  I feel
09838       like I'm back in my GWBASIC days. XXX */
09839    int res = -1;
09840    int cmd = 0;
09841    int valid = 0;
09842    char prefixstr[80] ="";
09843    char ext_context[256]="";
09844    int box;
09845    int useadsi = 0;
09846    int skipuser = 0;
09847    struct vm_state vms;
09848    struct ast_vm_user *vmu = NULL, vmus;
09849    char *context = NULL;
09850    int silentexit = 0;
09851    struct ast_flags flags = { 0 };
09852    signed char record_gain = 0;
09853    int play_auto = 0;
09854    int play_folder = 0;
09855    int in_urgent = 0;
09856 #ifdef IMAP_STORAGE
09857    int deleted = 0;
09858 #endif
09859 
09860    /* Add the vm_state to the active list and keep it active */
09861    memset(&vms, 0, sizeof(vms));
09862 
09863    vms.lastmsg = -1;
09864 
09865    memset(&vmus, 0, sizeof(vmus));
09866 
09867    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09868    if (chan->_state != AST_STATE_UP) {
09869       ast_debug(1, "Before ast_answer\n");
09870       ast_answer(chan);
09871    }
09872 
09873    if (!ast_strlen_zero(data)) {
09874       char *opts[OPT_ARG_ARRAY_SIZE];
09875       char *parse;
09876       AST_DECLARE_APP_ARGS(args,
09877          AST_APP_ARG(argv0);
09878          AST_APP_ARG(argv1);
09879       );
09880 
09881       parse = ast_strdupa(data);
09882 
09883       AST_STANDARD_APP_ARGS(args, parse);
09884 
09885       if (args.argc == 2) {
09886          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09887             return -1;
09888          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09889             int gain;
09890             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09891                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09892                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09893                   return -1;
09894                } else {
09895                   record_gain = (signed char) gain;
09896                }
09897             } else {
09898                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09899             }
09900          }
09901          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09902             play_auto = 1;
09903             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09904                /* See if it is a folder name first */
09905                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09906                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09907                      play_folder = -1;
09908                   }
09909                } else {
09910                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09911                }
09912             } else {
09913                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09914             }
09915             if (play_folder > 9 || play_folder < 0) {
09916                ast_log(AST_LOG_WARNING,
09917                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09918                   opts[OPT_ARG_PLAYFOLDER]);
09919                play_folder = 0;
09920             }
09921          }
09922       } else {
09923          /* old style options parsing */
09924          while (*(args.argv0)) {
09925             if (*(args.argv0) == 's')
09926                ast_set_flag(&flags, OPT_SILENT);
09927             else if (*(args.argv0) == 'p')
09928                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09929             else 
09930                break;
09931             (args.argv0)++;
09932          }
09933 
09934       }
09935 
09936       valid = ast_test_flag(&flags, OPT_SILENT);
09937 
09938       if ((context = strchr(args.argv0, '@')))
09939          *context++ = '\0';
09940 
09941       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09942          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09943       else
09944          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09945 
09946       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09947          skipuser++;
09948       else
09949          valid = 0;
09950    }
09951 
09952    if (!valid)
09953       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09954 
09955    ast_debug(1, "After vm_authenticate\n");
09956 
09957    if (vms.username[0] == '*') {
09958       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09959 
09960       /* user entered '*' */
09961       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09962          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09963          res = 0; /* prevent hangup */
09964          goto out;
09965       }
09966    }
09967 
09968    if (!res) {
09969       valid = 1;
09970       if (!skipuser)
09971          vmu = &vmus;
09972    } else {
09973       res = 0;
09974    }
09975 
09976    /* If ADSI is supported, setup login screen */
09977    adsi_begin(chan, &useadsi);
09978 
09979    ast_test_suite_assert(valid);
09980    if (!valid) {
09981       goto out;
09982    }
09983    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
09984 
09985 #ifdef IMAP_STORAGE
09986    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09987    pthread_setspecific(ts_vmstate.key, &vms);
09988 
09989    vms.interactive = 1;
09990    vms.updated = 1;
09991    if (vmu)
09992       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09993    vmstate_insert(&vms);
09994    init_vm_state(&vms);
09995 #endif
09996    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09997    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09998       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09999       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10000       return -1;
10001    }
10002    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10003       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
10004       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10005       return -1;
10006    }
10007    
10008    /* Set language from config to override channel language */
10009    if (!ast_strlen_zero(vmu->language))
10010       ast_string_field_set(chan, language, vmu->language);
10011 
10012    /* Retrieve urgent, old and new message counts */
10013    ast_debug(1, "Before open_mailbox\n");
10014    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10015    if (res < 0)
10016       goto out;
10017    vms.oldmessages = vms.lastmsg + 1;
10018    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10019    /* check INBOX */
10020    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10021    if (res < 0)
10022       goto out;
10023    vms.newmessages = vms.lastmsg + 1;
10024    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10025    /* Start in Urgent */
10026    in_urgent = 1;
10027    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10028    if (res < 0)
10029       goto out;
10030    vms.urgentmessages = vms.lastmsg + 1;
10031    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10032 
10033    /* Select proper mailbox FIRST!! */
10034    if (play_auto) {
10035       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10036       if (vms.urgentmessages) {
10037          in_urgent = 1;
10038          res = open_mailbox(&vms, vmu, 11);
10039       } else {
10040          in_urgent = 0;
10041          res = open_mailbox(&vms, vmu, play_folder);
10042       }
10043       if (res < 0)
10044          goto out;
10045 
10046       /* If there are no new messages, inform the user and hangup */
10047       if (vms.lastmsg == -1) {
10048          in_urgent = 0;
10049          cmd = vm_browse_messages(chan, &vms, vmu);
10050          res = 0;
10051          goto out;
10052       }
10053    } else {
10054       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10055          /* If we only have old messages start here */
10056          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10057          in_urgent = 0;
10058          play_folder = 1;
10059          if (res < 0)
10060             goto out;
10061       } else if (!vms.urgentmessages && vms.newmessages) {
10062          /* If we have new messages but none are urgent */
10063          in_urgent = 0;
10064          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10065          if (res < 0)
10066             goto out;
10067       }
10068    }
10069 
10070    if (useadsi)
10071       adsi_status(chan, &vms);
10072    res = 0;
10073 
10074    /* Check to see if this is a new user */
10075    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10076       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10077       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10078          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10079       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10080       if ((cmd == 't') || (cmd == '#')) {
10081          /* Timeout */
10082          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10083          res = 0;
10084          goto out;
10085       } else if (cmd < 0) {
10086          /* Hangup */
10087          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10088          res = -1;
10089          goto out;
10090       }
10091    }
10092 #ifdef IMAP_STORAGE
10093       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10094       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10095          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10096          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10097       }
10098       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10099       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10100          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10101          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10102       }
10103 #endif
10104 
10105    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10106    if (play_auto) {
10107       cmd = '1';
10108    } else {
10109       cmd = vm_intro(chan, vmu, &vms);
10110    }
10111    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10112 
10113    vms.repeats = 0;
10114    vms.starting = 1;
10115    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10116       /* Run main menu */
10117       switch (cmd) {
10118       case '1': /* First message */
10119          vms.curmsg = 0;
10120          /* Fall through */
10121       case '5': /* Play current message */
10122          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10123          cmd = vm_browse_messages(chan, &vms, vmu);
10124          break;
10125       case '2': /* Change folders */
10126          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10127          if (useadsi)
10128             adsi_folders(chan, 0, "Change to folder...");
10129 
10130          cmd = get_folder2(chan, "vm-changeto", 0);
10131          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10132          if (cmd == '#') {
10133             cmd = 0;
10134          } else if (cmd > 0) {
10135             cmd = cmd - '0';
10136             res = close_mailbox(&vms, vmu);
10137             if (res == ERROR_LOCK_PATH)
10138                goto out;
10139             /* If folder is not urgent, set in_urgent to zero! */
10140             if (cmd != 11) in_urgent = 0;
10141             res = open_mailbox(&vms, vmu, cmd);
10142             if (res < 0)
10143                goto out;
10144             play_folder = cmd;
10145             cmd = 0;
10146          }
10147          if (useadsi)
10148             adsi_status2(chan, &vms);
10149 
10150          if (!cmd) {
10151             cmd = vm_play_folder_name(chan, vms.vmbox);
10152          }
10153 
10154          vms.starting = 1;
10155          break;
10156       case '3': /* Advanced options */
10157          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10158          cmd = 0;
10159          vms.repeats = 0;
10160          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10161             switch (cmd) {
10162             case '1': /* Reply */
10163                if (vms.lastmsg > -1 && !vms.starting) {
10164                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10165                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10166                      res = cmd;
10167                      goto out;
10168                   }
10169                } else {
10170                   cmd = ast_play_and_wait(chan, "vm-sorry");
10171                }
10172                cmd = 't';
10173                break;
10174             case '2': /* Callback */
10175                if (!vms.starting)
10176                   ast_verb(3, "Callback Requested\n");
10177                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10178                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10179                   if (cmd == 9) {
10180                      silentexit = 1;
10181                      goto out;
10182                   } else if (cmd == ERROR_LOCK_PATH) {
10183                      res = cmd;
10184                      goto out;
10185                   }
10186                } else {
10187                   cmd = ast_play_and_wait(chan, "vm-sorry");
10188                }
10189                cmd = 't';
10190                break;
10191             case '3': /* Envelope */
10192                if (vms.lastmsg > -1 && !vms.starting) {
10193                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10194                   if (cmd == ERROR_LOCK_PATH) {
10195                      res = cmd;
10196                      goto out;
10197                   }
10198                } else {
10199                   cmd = ast_play_and_wait(chan, "vm-sorry");
10200                }
10201                cmd = 't';
10202                break;
10203             case '4': /* Dialout */
10204                if (!ast_strlen_zero(vmu->dialout)) {
10205                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10206                   if (cmd == 9) {
10207                      silentexit = 1;
10208                      goto out;
10209                   }
10210                } else {
10211                   cmd = ast_play_and_wait(chan, "vm-sorry");
10212                }
10213                cmd = 't';
10214                break;
10215 
10216             case '5': /* Leave VoiceMail */
10217                if (ast_test_flag(vmu, VM_SVMAIL)) {
10218                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10219                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10220                      res = cmd;
10221                      goto out;
10222                   }
10223                } else {
10224                   cmd = ast_play_and_wait(chan, "vm-sorry");
10225                }
10226                cmd = 't';
10227                break;
10228 
10229             case '*': /* Return to main menu */
10230                cmd = 't';
10231                break;
10232 
10233             default:
10234                cmd = 0;
10235                if (!vms.starting) {
10236                   cmd = ast_play_and_wait(chan, "vm-toreply");
10237                }
10238                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10239                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10240                }
10241                if (!cmd && !vms.starting) {
10242                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10243                }
10244                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10245                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10246                }
10247                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10248                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10249                }
10250                if (!cmd) {
10251                   cmd = ast_play_and_wait(chan, "vm-starmain");
10252                }
10253                if (!cmd) {
10254                   cmd = ast_waitfordigit(chan, 6000);
10255                }
10256                if (!cmd) {
10257                   vms.repeats++;
10258                }
10259                if (vms.repeats > 3) {
10260                   cmd = 't';
10261                }
10262                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10263             }
10264          }
10265          if (cmd == 't') {
10266             cmd = 0;
10267             vms.repeats = 0;
10268          }
10269          break;
10270       case '4': /* Go to the previous message */
10271          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10272          if (vms.curmsg > 0) {
10273             vms.curmsg--;
10274             cmd = play_message(chan, vmu, &vms);
10275          } else {
10276             /* Check if we were listening to new
10277                messages.  If so, go to Urgent messages
10278                instead of saying "no more messages"
10279             */
10280             if (in_urgent == 0 && vms.urgentmessages > 0) {
10281                /* Check for Urgent messages */
10282                in_urgent = 1;
10283                res = close_mailbox(&vms, vmu);
10284                if (res == ERROR_LOCK_PATH)
10285                   goto out;
10286                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10287                if (res < 0)
10288                   goto out;
10289                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10290                vms.curmsg = vms.lastmsg;
10291                if (vms.lastmsg < 0) {
10292                   cmd = ast_play_and_wait(chan, "vm-nomore");
10293                }
10294             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10295                vms.curmsg = vms.lastmsg;
10296                cmd = play_message(chan, vmu, &vms);
10297             } else {
10298                cmd = ast_play_and_wait(chan, "vm-nomore");
10299             }
10300          }
10301          break;
10302       case '6': /* Go to the next message */
10303          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10304          if (vms.curmsg < vms.lastmsg) {
10305             vms.curmsg++;
10306             cmd = play_message(chan, vmu, &vms);
10307          } else {
10308             if (in_urgent && vms.newmessages > 0) {
10309                /* Check if we were listening to urgent
10310                 * messages.  If so, go to regular new messages
10311                 * instead of saying "no more messages"
10312                 */
10313                in_urgent = 0;
10314                res = close_mailbox(&vms, vmu);
10315                if (res == ERROR_LOCK_PATH)
10316                   goto out;
10317                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10318                if (res < 0)
10319                   goto out;
10320                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10321                vms.curmsg = -1;
10322                if (vms.lastmsg < 0) {
10323                   cmd = ast_play_and_wait(chan, "vm-nomore");
10324                }
10325             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10326                vms.curmsg = 0;
10327                cmd = play_message(chan, vmu, &vms);
10328             } else {
10329                cmd = ast_play_and_wait(chan, "vm-nomore");
10330             }
10331          }
10332          break;
10333       case '7': /* Delete the current message */
10334          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10335             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10336             if (useadsi)
10337                adsi_delete(chan, &vms);
10338             if (vms.deleted[vms.curmsg]) {
10339                if (play_folder == 0) {
10340                   if (in_urgent) {
10341                      vms.urgentmessages--;
10342                   } else {
10343                      vms.newmessages--;
10344                   }
10345                }
10346                else if (play_folder == 1)
10347                   vms.oldmessages--;
10348                cmd = ast_play_and_wait(chan, "vm-deleted");
10349             } else {
10350                if (play_folder == 0) {
10351                   if (in_urgent) {
10352                      vms.urgentmessages++;
10353                   } else {
10354                      vms.newmessages++;
10355                   }
10356                }
10357                else if (play_folder == 1)
10358                   vms.oldmessages++;
10359                cmd = ast_play_and_wait(chan, "vm-undeleted");
10360             }
10361             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10362                if (vms.curmsg < vms.lastmsg) {
10363                   vms.curmsg++;
10364                   cmd = play_message(chan, vmu, &vms);
10365                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10366                   vms.curmsg = 0;
10367                   cmd = play_message(chan, vmu, &vms);
10368                } else {
10369                   /* Check if we were listening to urgent
10370                      messages.  If so, go to regular new messages
10371                      instead of saying "no more messages"
10372                   */
10373                   if (in_urgent == 1) {
10374                      /* Check for new messages */
10375                      in_urgent = 0;
10376                      res = close_mailbox(&vms, vmu);
10377                      if (res == ERROR_LOCK_PATH)
10378                         goto out;
10379                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10380                      if (res < 0)
10381                         goto out;
10382                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10383                      vms.curmsg = -1;
10384                      if (vms.lastmsg < 0) {
10385                         cmd = ast_play_and_wait(chan, "vm-nomore");
10386                      }
10387                   } else {
10388                      cmd = ast_play_and_wait(chan, "vm-nomore");
10389                   }
10390                }
10391             }
10392          } else /* Delete not valid if we haven't selected a message */
10393             cmd = 0;
10394 #ifdef IMAP_STORAGE
10395          deleted = 1;
10396 #endif
10397          break;
10398    
10399       case '8': /* Forward the current message */
10400          if (vms.lastmsg > -1) {
10401             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10402             if (cmd == ERROR_LOCK_PATH) {
10403                res = cmd;
10404                goto out;
10405             }
10406          } else {
10407             /* Check if we were listening to urgent
10408                messages.  If so, go to regular new messages
10409                instead of saying "no more messages"
10410             */
10411             if (in_urgent == 1 && vms.newmessages > 0) {
10412                /* Check for new messages */
10413                in_urgent = 0;
10414                res = close_mailbox(&vms, vmu);
10415                if (res == ERROR_LOCK_PATH)
10416                   goto out;
10417                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10418                if (res < 0)
10419                   goto out;
10420                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10421                vms.curmsg = -1;
10422                if (vms.lastmsg < 0) {
10423                   cmd = ast_play_and_wait(chan, "vm-nomore");
10424                }
10425             } else {
10426                cmd = ast_play_and_wait(chan, "vm-nomore");
10427             }
10428          }
10429          break;
10430       case '9': /* Save message to folder */
10431          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10432          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10433             /* No message selected */
10434             cmd = 0;
10435             break;
10436          }
10437          if (useadsi)
10438             adsi_folders(chan, 1, "Save to folder...");
10439          cmd = get_folder2(chan, "vm-savefolder", 1);
10440          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10441          box = 0; /* Shut up compiler */
10442          if (cmd == '#') {
10443             cmd = 0;
10444             break;
10445          } else if (cmd > 0) {
10446             box = cmd = cmd - '0';
10447             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10448             if (cmd == ERROR_LOCK_PATH) {
10449                res = cmd;
10450                goto out;
10451 #ifndef IMAP_STORAGE
10452             } else if (!cmd) {
10453                vms.deleted[vms.curmsg] = 1;
10454 #endif
10455             } else {
10456                vms.deleted[vms.curmsg] = 0;
10457                vms.heard[vms.curmsg] = 0;
10458             }
10459          }
10460          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10461          if (useadsi)
10462             adsi_message(chan, &vms);
10463          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10464          if (!cmd) {
10465             cmd = ast_play_and_wait(chan, "vm-message");
10466             if (!cmd) 
10467                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10468             if (!cmd)
10469                cmd = ast_play_and_wait(chan, "vm-savedto");
10470             if (!cmd)
10471                cmd = vm_play_folder_name(chan, vms.fn);
10472          } else {
10473             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10474          }
10475          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10476             if (vms.curmsg < vms.lastmsg) {
10477                vms.curmsg++;
10478                cmd = play_message(chan, vmu, &vms);
10479             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10480                vms.curmsg = 0;
10481                cmd = play_message(chan, vmu, &vms);
10482             } else {
10483                /* Check if we were listening to urgent
10484                   messages.  If so, go to regular new messages
10485                   instead of saying "no more messages"
10486                */
10487                if (in_urgent == 1 && vms.newmessages > 0) {
10488                   /* Check for new messages */
10489                   in_urgent = 0;
10490                   res = close_mailbox(&vms, vmu);
10491                   if (res == ERROR_LOCK_PATH)
10492                      goto out;
10493                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10494                   if (res < 0)
10495                      goto out;
10496                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10497                   vms.curmsg = -1;
10498                   if (vms.lastmsg < 0) {
10499                      cmd = ast_play_and_wait(chan, "vm-nomore");
10500                   }
10501                } else {
10502                   cmd = ast_play_and_wait(chan, "vm-nomore");
10503                }
10504             }
10505          }
10506          break;
10507       case '*': /* Help */
10508          if (!vms.starting) {
10509             cmd = ast_play_and_wait(chan, "vm-onefor");
10510             if (!strncasecmp(chan->language, "he", 2)) {
10511                cmd = ast_play_and_wait(chan, "vm-for");
10512             }
10513             if (!cmd)
10514                cmd = vm_play_folder_name(chan, vms.vmbox);
10515             if (!cmd)
10516                cmd = ast_play_and_wait(chan, "vm-opts");
10517             if (!cmd)
10518                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10519          } else
10520             cmd = 0;
10521          break;
10522       case '0': /* Mailbox options */
10523          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10524          if (useadsi)
10525             adsi_status(chan, &vms);
10526          break;
10527       default: /* Nothing */
10528          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10529          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10530          break;
10531       }
10532    }
10533    if ((cmd == 't') || (cmd == '#')) {
10534       /* Timeout */
10535       res = 0;
10536    } else {
10537       /* Hangup */
10538       res = -1;
10539    }
10540 
10541 out:
10542    if (res > -1) {
10543       ast_stopstream(chan);
10544       adsi_goodbye(chan);
10545       if (valid && res != OPERATOR_EXIT) {
10546          if (silentexit)
10547             res = ast_play_and_wait(chan, "vm-dialout");
10548          else 
10549             res = ast_play_and_wait(chan, "vm-goodbye");
10550       }
10551       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10552          res = 0;
10553       }
10554       if (useadsi)
10555          ast_adsi_unload_session(chan);
10556    }
10557    if (vmu)
10558       close_mailbox(&vms, vmu);
10559    if (valid) {
10560       int new = 0, old = 0, urgent = 0;
10561       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10562       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10563       /* Urgent flag not passwd to externnotify here */
10564       run_externnotify(vmu->context, vmu->mailbox, NULL);
10565       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10566       queue_mwi_event(ext_context, urgent, new, old);
10567    }
10568 #ifdef IMAP_STORAGE
10569    /* expunge message - use UID Expunge if supported on IMAP server*/
10570    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10571    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10572       ast_mutex_lock(&vms.lock);
10573 #ifdef HAVE_IMAP_TK2006
10574       if (LEVELUIDPLUS (vms.mailstream)) {
10575          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10576       } else 
10577 #endif
10578          mail_expunge(vms.mailstream);
10579       ast_mutex_unlock(&vms.lock);
10580    }
10581    /*  before we delete the state, we should copy pertinent info
10582     *  back to the persistent model */
10583    if (vmu) {
10584       vmstate_delete(&vms);
10585    }
10586 #endif
10587    if (vmu)
10588       free_user(vmu);
10589    if (vms.deleted)
10590       ast_free(vms.deleted);
10591    if (vms.heard)
10592       ast_free(vms.heard);
10593 
10594 #ifdef IMAP_STORAGE
10595    pthread_setspecific(ts_vmstate.key, NULL);
10596 #endif
10597    return res;
10598 }
10599 
10600 static int vm_exec(struct ast_channel *chan, const char *data)
10601 {
10602    int res = 0;
10603    char *tmp;
10604    struct leave_vm_options leave_options;
10605    struct ast_flags flags = { 0 };
10606    char *opts[OPT_ARG_ARRAY_SIZE];
10607    AST_DECLARE_APP_ARGS(args,
10608       AST_APP_ARG(argv0);
10609       AST_APP_ARG(argv1);
10610    );
10611    
10612    memset(&leave_options, 0, sizeof(leave_options));
10613 
10614    if (chan->_state != AST_STATE_UP)
10615       ast_answer(chan);
10616 
10617    if (!ast_strlen_zero(data)) {
10618       tmp = ast_strdupa(data);
10619       AST_STANDARD_APP_ARGS(args, tmp);
10620       if (args.argc == 2) {
10621          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10622             return -1;
10623          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10624          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10625             int gain;
10626 
10627             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10628                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10629                return -1;
10630             } else {
10631                leave_options.record_gain = (signed char) gain;
10632             }
10633          }
10634          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10635             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10636                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10637          }
10638       }
10639    } else {
10640       char temp[256];
10641       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10642       if (res < 0)
10643          return res;
10644       if (ast_strlen_zero(temp))
10645          return 0;
10646       args.argv0 = ast_strdupa(temp);
10647    }
10648 
10649    res = leave_voicemail(chan, args.argv0, &leave_options);
10650    if (res == 't') {
10651       ast_play_and_wait(chan, "vm-goodbye");
10652       res = 0;
10653    }
10654 
10655    if (res == OPERATOR_EXIT) {
10656       res = 0;
10657    }
10658 
10659    if (res == ERROR_LOCK_PATH) {
10660       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10661       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10662       res = 0;
10663    }
10664 
10665    return res;
10666 }
10667 
10668 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10669 {
10670    struct ast_vm_user *vmu;
10671 
10672    if (!ast_strlen_zero(box) && box[0] == '*') {
10673       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10674             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10675             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10676             "\n\tand will be ignored.\n", box, context);
10677       return NULL;
10678    }
10679 
10680    AST_LIST_TRAVERSE(&users, vmu, list) {
10681       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10682          if (strcasecmp(vmu->context, context)) {
10683             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10684                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10685                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10686                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10687          }
10688          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10689          return NULL;
10690       }
10691       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10692          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10693          return NULL;
10694       }
10695    }
10696    
10697    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10698       return NULL;
10699    
10700    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10701    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10702 
10703    AST_LIST_INSERT_TAIL(&users, vmu, list);
10704    
10705    return vmu;
10706 }
10707 
10708 static int append_mailbox(const char *context, const char *box, const char *data)
10709 {
10710    /* Assumes lock is already held */
10711    char *tmp;
10712    char *stringp;
10713    char *s;
10714    struct ast_vm_user *vmu;
10715    char *mailbox_full;
10716    int new = 0, old = 0, urgent = 0;
10717    char secretfn[PATH_MAX] = "";
10718 
10719    tmp = ast_strdupa(data);
10720 
10721    if (!(vmu = find_or_create(context, box)))
10722       return -1;
10723 
10724    populate_defaults(vmu);
10725 
10726    stringp = tmp;
10727    if ((s = strsep(&stringp, ","))) {
10728       if (!ast_strlen_zero(s) && s[0] == '*') {
10729          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10730             "\n\tmust be reset in voicemail.conf.\n", box);
10731       }
10732       /* assign password regardless of validity to prevent NULL password from being assigned */
10733       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10734    }
10735    if (stringp && (s = strsep(&stringp, ","))) {
10736       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10737    }
10738    if (stringp && (s = strsep(&stringp, ","))) {
10739       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10740    }
10741    if (stringp && (s = strsep(&stringp, ","))) {
10742       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10743    }
10744    if (stringp && (s = strsep(&stringp, ","))) {
10745       apply_options(vmu, s);
10746    }
10747 
10748    switch (vmu->passwordlocation) {
10749    case OPT_PWLOC_SPOOLDIR:
10750       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10751       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10752    }
10753 
10754    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10755    strcpy(mailbox_full, box);
10756    strcat(mailbox_full, "@");
10757    strcat(mailbox_full, context);
10758 
10759    inboxcount2(mailbox_full, &urgent, &new, &old);
10760    queue_mwi_event(mailbox_full, urgent, new, old);
10761 
10762    return 0;
10763 }
10764 
10765 AST_TEST_DEFINE(test_voicemail_vmuser)
10766 {
10767    int res = 0;
10768    struct ast_vm_user *vmu;
10769    /* language parameter seems to only be used for display in manager action */
10770    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10771       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10772       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10773       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10774       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10775       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10776       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10777       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10778       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10779 #ifdef IMAP_STORAGE
10780    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10781       "imapfolder=INBOX|imapvmshareid=6000";
10782 #endif
10783 
10784    switch (cmd) {
10785    case TEST_INIT:
10786       info->name = "vmuser";
10787       info->category = "/apps/app_voicemail/";
10788       info->summary = "Vmuser unit test";
10789       info->description =
10790          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10791       return AST_TEST_NOT_RUN;
10792    case TEST_EXECUTE:
10793       break;
10794    }
10795 
10796    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10797       return AST_TEST_NOT_RUN;
10798    }
10799    ast_set_flag(vmu, VM_ALLOCED);
10800    populate_defaults(vmu);
10801 
10802    apply_options(vmu, options_string);
10803 
10804    if (!ast_test_flag(vmu, VM_ATTACH)) {
10805       ast_test_status_update(test, "Parse failure for attach option\n");
10806       res = 1;
10807    }
10808    if (strcasecmp(vmu->attachfmt, "wav49")) {
10809       ast_test_status_update(test, "Parse failure for attachftm option\n");
10810       res = 1;
10811    }
10812    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10813       ast_test_status_update(test, "Parse failure for serveremail option\n");
10814       res = 1;
10815    }
10816    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10817       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10818       res = 1;
10819    }
10820    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10821       ast_test_status_update(test, "Parse failure for emailbody option\n");
10822       res = 1;
10823    }
10824    if (strcasecmp(vmu->zonetag, "central")) {
10825       ast_test_status_update(test, "Parse failure for tz option\n");
10826       res = 1;
10827    }
10828    if (!ast_test_flag(vmu, VM_DELETE)) {
10829       ast_test_status_update(test, "Parse failure for delete option\n");
10830       res = 1;
10831    }
10832    if (!ast_test_flag(vmu, VM_SAYCID)) {
10833       ast_test_status_update(test, "Parse failure for saycid option\n");
10834       res = 1;
10835    }
10836    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10837       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10838       res = 1;
10839    }
10840    if (!ast_test_flag(vmu, VM_REVIEW)) {
10841       ast_test_status_update(test, "Parse failure for review option\n");
10842       res = 1;
10843    }
10844    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10845       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10846       res = 1;
10847    }
10848    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10849       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10850       res = 1;
10851    }
10852    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10853       ast_test_status_update(test, "Parse failure for operator option\n");
10854       res = 1;
10855    }
10856    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10857       ast_test_status_update(test, "Parse failure for envelope option\n");
10858       res = 1;
10859    }
10860    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10861       ast_test_status_update(test, "Parse failure for moveheard option\n");
10862       res = 1;
10863    }
10864    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10865       ast_test_status_update(test, "Parse failure for sayduration option\n");
10866       res = 1;
10867    }
10868    if (vmu->saydurationm != 5) {
10869       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10870       res = 1;
10871    }
10872    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10873       ast_test_status_update(test, "Parse failure for forcename option\n");
10874       res = 1;
10875    }
10876    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10877       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10878       res = 1;
10879    }
10880    if (strcasecmp(vmu->callback, "somecontext")) {
10881       ast_test_status_update(test, "Parse failure for callbacks option\n");
10882       res = 1;
10883    }
10884    if (strcasecmp(vmu->dialout, "somecontext2")) {
10885       ast_test_status_update(test, "Parse failure for dialout option\n");
10886       res = 1;
10887    }
10888    if (strcasecmp(vmu->exit, "somecontext3")) {
10889       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10890       res = 1;
10891    }
10892    if (vmu->minsecs != 10) {
10893       ast_test_status_update(test, "Parse failure for minsecs option\n");
10894       res = 1;
10895    }
10896    if (vmu->maxsecs != 100) {
10897       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10898       res = 1;
10899    }
10900    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10901       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10902       res = 1;
10903    }
10904    if (vmu->maxdeletedmsg != 50) {
10905       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10906       res = 1;
10907    }
10908    if (vmu->volgain != 1.3) {
10909       ast_test_status_update(test, "Parse failure for volgain option\n");
10910       res = 1;
10911    }
10912    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10913       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10914       res = 1;
10915    }
10916 #ifdef IMAP_STORAGE
10917    apply_options(vmu, option_string2);
10918 
10919    if (strcasecmp(vmu->imapuser, "imapuser")) {
10920       ast_test_status_update(test, "Parse failure for imapuser option\n");
10921       res = 1;
10922    }
10923    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10924       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10925       res = 1;
10926    }
10927    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10928       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10929       res = 1;
10930    }
10931    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10932       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10933       res = 1;
10934    }
10935 #endif
10936 
10937    free_user(vmu);
10938    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10939 }
10940 
10941 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10942 {
10943    struct ast_vm_user svm;
10944    char *context, *box;
10945    AST_DECLARE_APP_ARGS(args,
10946       AST_APP_ARG(mbox);
10947       AST_APP_ARG(options);
10948    );
10949    static int dep_warning = 0;
10950 
10951    if (ast_strlen_zero(data)) {
10952       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10953       return -1;
10954    }
10955 
10956    if (!dep_warning) {
10957       dep_warning = 1;
10958       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10959    }
10960 
10961    box = ast_strdupa(data);
10962 
10963    AST_STANDARD_APP_ARGS(args, box);
10964 
10965    if (args.options) {
10966    }
10967 
10968    if ((context = strchr(args.mbox, '@'))) {
10969       *context = '\0';
10970       context++;
10971    }
10972 
10973    if (find_user(&svm, context, args.mbox)) {
10974       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10975    } else
10976       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10977 
10978    return 0;
10979 }
10980 
10981 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10982 {
10983    struct ast_vm_user svm;
10984    AST_DECLARE_APP_ARGS(arg,
10985       AST_APP_ARG(mbox);
10986       AST_APP_ARG(context);
10987    );
10988 
10989    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10990 
10991    if (ast_strlen_zero(arg.mbox)) {
10992       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10993       return -1;
10994    }
10995 
10996    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10997    return 0;
10998 }
10999 
11000 static struct ast_custom_function mailbox_exists_acf = {
11001    .name = "MAILBOX_EXISTS",
11002    .read = acf_mailbox_exists,
11003 };
11004 
11005 static int vmauthenticate(struct ast_channel *chan, const char *data)
11006 {
11007    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11008    struct ast_vm_user vmus;
11009    char *options = NULL;
11010    int silent = 0, skipuser = 0;
11011    int res = -1;
11012    
11013    if (data) {
11014       s = ast_strdupa(data);
11015       user = strsep(&s, ",");
11016       options = strsep(&s, ",");
11017       if (user) {
11018          s = user;
11019          user = strsep(&s, "@");
11020          context = strsep(&s, "");
11021          if (!ast_strlen_zero(user))
11022             skipuser++;
11023          ast_copy_string(mailbox, user, sizeof(mailbox));
11024       }
11025    }
11026 
11027    if (options) {
11028       silent = (strchr(options, 's')) != NULL;
11029    }
11030 
11031    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11032       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11033       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11034       ast_play_and_wait(chan, "auth-thankyou");
11035       res = 0;
11036    } else if (mailbox[0] == '*') {
11037       /* user entered '*' */
11038       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11039          res = 0; /* prevent hangup */
11040       }
11041    }
11042 
11043    return res;
11044 }
11045 
11046 static char *show_users_realtime(int fd, const char *context)
11047 {
11048    struct ast_config *cfg;
11049    const char *cat = NULL;
11050 
11051    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11052       "context", context, SENTINEL))) {
11053       return CLI_FAILURE;
11054    }
11055 
11056    ast_cli(fd,
11057       "\n"
11058       "=============================================================\n"
11059       "=== Configured Voicemail Users ==============================\n"
11060       "=============================================================\n"
11061       "===\n");
11062 
11063    while ((cat = ast_category_browse(cfg, cat))) {
11064       struct ast_variable *var = NULL;
11065       ast_cli(fd,
11066          "=== Mailbox ...\n"
11067          "===\n");
11068       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11069          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11070       ast_cli(fd,
11071          "===\n"
11072          "=== ---------------------------------------------------------\n"
11073          "===\n");
11074    }
11075 
11076    ast_cli(fd,
11077       "=============================================================\n"
11078       "\n");
11079 
11080    ast_config_destroy(cfg);
11081 
11082    return CLI_SUCCESS;
11083 }
11084 
11085 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11086 {
11087    int which = 0;
11088    int wordlen;
11089    struct ast_vm_user *vmu;
11090    const char *context = "";
11091 
11092    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11093    if (pos > 4)
11094       return NULL;
11095    if (pos == 3)
11096       return (state == 0) ? ast_strdup("for") : NULL;
11097    wordlen = strlen(word);
11098    AST_LIST_TRAVERSE(&users, vmu, list) {
11099       if (!strncasecmp(word, vmu->context, wordlen)) {
11100          if (context && strcmp(context, vmu->context) && ++which > state)
11101             return ast_strdup(vmu->context);
11102          /* ignore repeated contexts ? */
11103          context = vmu->context;
11104       }
11105    }
11106    return NULL;
11107 }
11108 
11109 /*! \brief Show a list of voicemail users in the CLI */
11110 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11111 {
11112    struct ast_vm_user *vmu;
11113 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11114    const char *context = NULL;
11115    int users_counter = 0;
11116 
11117    switch (cmd) {
11118    case CLI_INIT:
11119       e->command = "voicemail show users";
11120       e->usage =
11121          "Usage: voicemail show users [for <context>]\n"
11122          "       Lists all mailboxes currently set up\n";
11123       return NULL;
11124    case CLI_GENERATE:
11125       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11126    }  
11127 
11128    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11129       return CLI_SHOWUSAGE;
11130    if (a->argc == 5) {
11131       if (strcmp(a->argv[3],"for"))
11132          return CLI_SHOWUSAGE;
11133       context = a->argv[4];
11134    }
11135 
11136    if (ast_check_realtime("voicemail")) {
11137       if (!context) {
11138          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11139          return CLI_SHOWUSAGE;
11140       }
11141       return show_users_realtime(a->fd, context);
11142    }
11143 
11144    AST_LIST_LOCK(&users);
11145    if (AST_LIST_EMPTY(&users)) {
11146       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11147       AST_LIST_UNLOCK(&users);
11148       return CLI_FAILURE;
11149    }
11150    if (a->argc == 3)
11151       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11152    else {
11153       int count = 0;
11154       AST_LIST_TRAVERSE(&users, vmu, list) {
11155          if (!strcmp(context, vmu->context))
11156             count++;
11157       }
11158       if (count) {
11159          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11160       } else {
11161          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11162          AST_LIST_UNLOCK(&users);
11163          return CLI_FAILURE;
11164       }
11165    }
11166    AST_LIST_TRAVERSE(&users, vmu, list) {
11167       int newmsgs = 0, oldmsgs = 0;
11168       char count[12], tmp[256] = "";
11169 
11170       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11171          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11172          inboxcount(tmp, &newmsgs, &oldmsgs);
11173          snprintf(count, sizeof(count), "%d", newmsgs);
11174          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11175          users_counter++;
11176       }
11177    }
11178    AST_LIST_UNLOCK(&users);
11179    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11180    return CLI_SUCCESS;
11181 }
11182 
11183 /*! \brief Show a list of voicemail zones in the CLI */
11184 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11185 {
11186    struct vm_zone *zone;
11187 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11188    char *res = CLI_SUCCESS;
11189 
11190    switch (cmd) {
11191    case CLI_INIT:
11192       e->command = "voicemail show zones";
11193       e->usage =
11194          "Usage: voicemail show zones\n"
11195          "       Lists zone message formats\n";
11196       return NULL;
11197    case CLI_GENERATE:
11198       return NULL;
11199    }
11200 
11201    if (a->argc != 3)
11202       return CLI_SHOWUSAGE;
11203 
11204    AST_LIST_LOCK(&zones);
11205    if (!AST_LIST_EMPTY(&zones)) {
11206       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11207       AST_LIST_TRAVERSE(&zones, zone, list) {
11208          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11209       }
11210    } else {
11211       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11212       res = CLI_FAILURE;
11213    }
11214    AST_LIST_UNLOCK(&zones);
11215 
11216    return res;
11217 }
11218 
11219 /*! \brief Reload voicemail configuration from the CLI */
11220 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11221 {
11222    switch (cmd) {
11223    case CLI_INIT:
11224       e->command = "voicemail reload";
11225       e->usage =
11226          "Usage: voicemail reload\n"
11227          "       Reload voicemail configuration\n";
11228       return NULL;
11229    case CLI_GENERATE:
11230       return NULL;
11231    }
11232 
11233    if (a->argc != 2)
11234       return CLI_SHOWUSAGE;
11235 
11236    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11237    load_config(1);
11238    
11239    return CLI_SUCCESS;
11240 }
11241 
11242 static struct ast_cli_entry cli_voicemail[] = {
11243    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11244    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11245    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11246 };
11247 
11248 #ifdef IMAP_STORAGE
11249    #define DATA_EXPORT_VM_USERS(USER)              \
11250       USER(ast_vm_user, context, AST_DATA_STRING)        \
11251       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11252       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11253       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11254       USER(ast_vm_user, email, AST_DATA_STRING)       \
11255       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11256       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11257       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11258       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11259       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11260       USER(ast_vm_user, language, AST_DATA_STRING)       \
11261       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11262       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11263       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11264       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11265       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11266       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11267       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11268       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11269       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11270       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11271       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11272       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11273       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11274       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11275       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11276 #else
11277    #define DATA_EXPORT_VM_USERS(USER)              \
11278       USER(ast_vm_user, context, AST_DATA_STRING)        \
11279       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11280       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11281       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11282       USER(ast_vm_user, email, AST_DATA_STRING)       \
11283       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11284       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11285       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11286       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11287       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11288       USER(ast_vm_user, language, AST_DATA_STRING)       \
11289       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11290       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11291       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11292       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11293       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11294       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11295       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11296       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11297       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11298       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11299       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11300       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11301 #endif
11302 
11303 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11304 
11305 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11306    ZONE(vm_zone, name, AST_DATA_STRING)      \
11307    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11308    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11309 
11310 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11311 
11312 /*!
11313  * \internal
11314  * \brief Add voicemail user to the data_root.
11315  * \param[in] search The search tree.
11316  * \param[in] data_root The main result node.
11317  * \param[in] user The voicemail user.
11318  */
11319 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11320     struct ast_data *data_root, struct ast_vm_user *user)
11321 {
11322    struct ast_data *data_user, *data_zone;
11323    struct ast_data *data_state;
11324    struct vm_zone *zone = NULL;
11325    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11326    char ext_context[256] = "";
11327 
11328    data_user = ast_data_add_node(data_root, "user");
11329    if (!data_user) {
11330       return -1;
11331    }
11332 
11333    ast_data_add_structure(ast_vm_user, data_user, user);
11334 
11335    AST_LIST_LOCK(&zones);
11336    AST_LIST_TRAVERSE(&zones, zone, list) {
11337       if (!strcmp(zone->name, user->zonetag)) {
11338          break;
11339       }
11340    }
11341    AST_LIST_UNLOCK(&zones);
11342 
11343    /* state */
11344    data_state = ast_data_add_node(data_user, "state");
11345    if (!data_state) {
11346       return -1;
11347    }
11348    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11349    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11350    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11351    ast_data_add_int(data_state, "newmsg", newmsg);
11352    ast_data_add_int(data_state, "oldmsg", oldmsg);
11353 
11354    if (zone) {
11355       data_zone = ast_data_add_node(data_user, "zone");
11356       ast_data_add_structure(vm_zone, data_zone, zone);
11357    }
11358 
11359    if (!ast_data_search_match(search, data_user)) {
11360       ast_data_remove_node(data_root, data_user);
11361    }
11362 
11363    return 0;
11364 }
11365 
11366 static int vm_users_data_provider_get(const struct ast_data_search *search,
11367    struct ast_data *data_root)
11368 {
11369    struct ast_vm_user *user;
11370 
11371    AST_LIST_LOCK(&users);
11372    AST_LIST_TRAVERSE(&users, user, list) {
11373       vm_users_data_provider_get_helper(search, data_root, user);
11374    }
11375    AST_LIST_UNLOCK(&users);
11376 
11377    return 0;
11378 }
11379 
11380 static const struct ast_data_handler vm_users_data_provider = {
11381    .version = AST_DATA_HANDLER_VERSION,
11382    .get = vm_users_data_provider_get
11383 };
11384 
11385 static const struct ast_data_entry vm_data_providers[] = {
11386    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11387 };
11388 
11389 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11390 {
11391    int new = 0, old = 0, urgent = 0;
11392 
11393    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11394 
11395    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11396       mwi_sub->old_urgent = urgent;
11397       mwi_sub->old_new = new;
11398       mwi_sub->old_old = old;
11399       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11400       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11401    }
11402 }
11403 
11404 static void poll_subscribed_mailboxes(void)
11405 {
11406    struct mwi_sub *mwi_sub;
11407 
11408    AST_RWLIST_RDLOCK(&mwi_subs);
11409    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11410       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11411          poll_subscribed_mailbox(mwi_sub);
11412       }
11413    }
11414    AST_RWLIST_UNLOCK(&mwi_subs);
11415 }
11416 
11417 static void *mb_poll_thread(void *data)
11418 {
11419    while (poll_thread_run) {
11420       struct timespec ts = { 0, };
11421       struct timeval wait;
11422 
11423       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11424       ts.tv_sec = wait.tv_sec;
11425       ts.tv_nsec = wait.tv_usec * 1000;
11426 
11427       ast_mutex_lock(&poll_lock);
11428       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11429       ast_mutex_unlock(&poll_lock);
11430 
11431       if (!poll_thread_run)
11432          break;
11433 
11434       poll_subscribed_mailboxes();
11435    }
11436 
11437    return NULL;
11438 }
11439 
11440 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11441 {
11442    ast_free(mwi_sub);
11443 }
11444 
11445 static int handle_unsubscribe(void *datap)
11446 {
11447    struct mwi_sub *mwi_sub;
11448    uint32_t *uniqueid = datap;
11449    
11450    AST_RWLIST_WRLOCK(&mwi_subs);
11451    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11452       if (mwi_sub->uniqueid == *uniqueid) {
11453          AST_LIST_REMOVE_CURRENT(entry);
11454          break;
11455       }
11456    }
11457    AST_RWLIST_TRAVERSE_SAFE_END
11458    AST_RWLIST_UNLOCK(&mwi_subs);
11459 
11460    if (mwi_sub)
11461       mwi_sub_destroy(mwi_sub);
11462 
11463    ast_free(uniqueid);  
11464    return 0;
11465 }
11466 
11467 static int handle_subscribe(void *datap)
11468 {
11469    unsigned int len;
11470    struct mwi_sub *mwi_sub;
11471    struct mwi_sub_task *p = datap;
11472 
11473    len = sizeof(*mwi_sub);
11474    if (!ast_strlen_zero(p->mailbox))
11475       len += strlen(p->mailbox);
11476 
11477    if (!ast_strlen_zero(p->context))
11478       len += strlen(p->context) + 1; /* Allow for seperator */
11479 
11480    if (!(mwi_sub = ast_calloc(1, len)))
11481       return -1;
11482 
11483    mwi_sub->uniqueid = p->uniqueid;
11484    if (!ast_strlen_zero(p->mailbox))
11485       strcpy(mwi_sub->mailbox, p->mailbox);
11486 
11487    if (!ast_strlen_zero(p->context)) {
11488       strcat(mwi_sub->mailbox, "@");
11489       strcat(mwi_sub->mailbox, p->context);
11490    }
11491 
11492    AST_RWLIST_WRLOCK(&mwi_subs);
11493    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11494    AST_RWLIST_UNLOCK(&mwi_subs);
11495    ast_free((void *) p->mailbox);
11496    ast_free((void *) p->context);
11497    ast_free(p);
11498    poll_subscribed_mailbox(mwi_sub);
11499    return 0;
11500 }
11501 
11502 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11503 {
11504    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11505    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11506       return;
11507 
11508    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11509       return;
11510 
11511    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11512    *uniqueid = u;
11513    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11514       ast_free(uniqueid);
11515    }
11516 }
11517 
11518 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11519 {
11520    struct mwi_sub_task *mwist;
11521    
11522    if (ast_event_get_type(event) != AST_EVENT_SUB)
11523       return;
11524 
11525    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11526       return;
11527 
11528    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11529       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11530       return;
11531    }
11532    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11533    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11534    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11535    
11536    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11537       ast_free(mwist);
11538    }
11539 }
11540 
11541 static void start_poll_thread(void)
11542 {
11543    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11544       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11545       AST_EVENT_IE_END);
11546 
11547    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11548       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11549       AST_EVENT_IE_END);
11550 
11551    if (mwi_sub_sub)
11552       ast_event_report_subs(mwi_sub_sub);
11553 
11554    poll_thread_run = 1;
11555 
11556    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11557 }
11558 
11559 static void stop_poll_thread(void)
11560 {
11561    poll_thread_run = 0;
11562 
11563    if (mwi_sub_sub) {
11564       ast_event_unsubscribe(mwi_sub_sub);
11565       mwi_sub_sub = NULL;
11566    }
11567 
11568    if (mwi_unsub_sub) {
11569       ast_event_unsubscribe(mwi_unsub_sub);
11570       mwi_unsub_sub = NULL;
11571    }
11572 
11573    ast_mutex_lock(&poll_lock);
11574    ast_cond_signal(&poll_cond);
11575    ast_mutex_unlock(&poll_lock);
11576 
11577    pthread_join(poll_thread, NULL);
11578 
11579    poll_thread = AST_PTHREADT_NULL;
11580 }
11581 
11582 /*! \brief Manager list voicemail users command */
11583 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11584 {
11585    struct ast_vm_user *vmu = NULL;
11586    const char *id = astman_get_header(m, "ActionID");
11587    char actionid[128] = "";
11588 
11589    if (!ast_strlen_zero(id))
11590       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11591 
11592    AST_LIST_LOCK(&users);
11593 
11594    if (AST_LIST_EMPTY(&users)) {
11595       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11596       AST_LIST_UNLOCK(&users);
11597       return RESULT_SUCCESS;
11598    }
11599    
11600    astman_send_ack(s, m, "Voicemail user list will follow");
11601    
11602    AST_LIST_TRAVERSE(&users, vmu, list) {
11603       char dirname[256];
11604 
11605 #ifdef IMAP_STORAGE
11606       int new, old;
11607       inboxcount(vmu->mailbox, &new, &old);
11608 #endif
11609       
11610       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11611       astman_append(s,
11612          "%s"
11613          "Event: VoicemailUserEntry\r\n"
11614          "VMContext: %s\r\n"
11615          "VoiceMailbox: %s\r\n"
11616          "Fullname: %s\r\n"
11617          "Email: %s\r\n"
11618          "Pager: %s\r\n"
11619          "ServerEmail: %s\r\n"
11620          "MailCommand: %s\r\n"
11621          "Language: %s\r\n"
11622          "TimeZone: %s\r\n"
11623          "Callback: %s\r\n"
11624          "Dialout: %s\r\n"
11625          "UniqueID: %s\r\n"
11626          "ExitContext: %s\r\n"
11627          "SayDurationMinimum: %d\r\n"
11628          "SayEnvelope: %s\r\n"
11629          "SayCID: %s\r\n"
11630          "AttachMessage: %s\r\n"
11631          "AttachmentFormat: %s\r\n"
11632          "DeleteMessage: %s\r\n"
11633          "VolumeGain: %.2f\r\n"
11634          "CanReview: %s\r\n"
11635          "CallOperator: %s\r\n"
11636          "MaxMessageCount: %d\r\n"
11637          "MaxMessageLength: %d\r\n"
11638          "NewMessageCount: %d\r\n"
11639 #ifdef IMAP_STORAGE
11640          "OldMessageCount: %d\r\n"
11641          "IMAPUser: %s\r\n"
11642 #endif
11643          "\r\n",
11644          actionid,
11645          vmu->context,
11646          vmu->mailbox,
11647          vmu->fullname,
11648          vmu->email,
11649          vmu->pager,
11650          vmu->serveremail,
11651          vmu->mailcmd,
11652          vmu->language,
11653          vmu->zonetag,
11654          vmu->callback,
11655          vmu->dialout,
11656          vmu->uniqueid,
11657          vmu->exit,
11658          vmu->saydurationm,
11659          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11660          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11661          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11662          vmu->attachfmt,
11663          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11664          vmu->volgain,
11665          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11666          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11667          vmu->maxmsg,
11668          vmu->maxsecs,
11669 #ifdef IMAP_STORAGE
11670          new, old, vmu->imapuser
11671 #else
11672          count_messages(vmu, dirname)
11673 #endif
11674          );
11675    }     
11676    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11677 
11678    AST_LIST_UNLOCK(&users);
11679 
11680    return RESULT_SUCCESS;
11681 }
11682 
11683 /*! \brief Free the users structure. */
11684 static void free_vm_users(void) 
11685 {
11686    struct ast_vm_user *current;
11687    AST_LIST_LOCK(&users);
11688    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11689       ast_set_flag(current, VM_ALLOCED);
11690       free_user(current);
11691    }
11692    AST_LIST_UNLOCK(&users);
11693 }
11694 
11695 /*! \brief Free the zones structure. */
11696 static void free_vm_zones(void)
11697 {
11698    struct vm_zone *zcur;
11699    AST_LIST_LOCK(&zones);
11700    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11701       free_zone(zcur);
11702    AST_LIST_UNLOCK(&zones);
11703 }
11704 
11705 static const char *substitute_escapes(const char *value)
11706 {
11707    char *current;
11708 
11709    /* Add 16 for fudge factor */
11710    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11711 
11712    ast_str_reset(str);
11713    
11714    /* Substitute strings \r, \n, and \t into the appropriate characters */
11715    for (current = (char *) value; *current; current++) {
11716       if (*current == '\\') {
11717          current++;
11718          if (!*current) {
11719             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11720             break;
11721          }
11722          switch (*current) {
11723          case '\\':
11724             ast_str_append(&str, 0, "\\");
11725             break;
11726          case 'r':
11727             ast_str_append(&str, 0, "\r");
11728             break;
11729          case 'n':
11730 #ifdef IMAP_STORAGE
11731             if (!str->used || str->str[str->used - 1] != '\r') {
11732                ast_str_append(&str, 0, "\r");
11733             }
11734 #endif
11735             ast_str_append(&str, 0, "\n");
11736             break;
11737          case 't':
11738             ast_str_append(&str, 0, "\t");
11739             break;
11740          default:
11741             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11742             break;
11743          }
11744       } else {
11745          ast_str_append(&str, 0, "%c", *current);
11746       }
11747    }
11748 
11749    return ast_str_buffer(str);
11750 }
11751 
11752 static int load_config(int reload)
11753 {
11754    struct ast_config *cfg, *ucfg;
11755    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11756    int res;
11757 
11758    ast_unload_realtime("voicemail");
11759    ast_unload_realtime("voicemail_data");
11760 
11761    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11762       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11763          return 0;
11764       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11765          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11766          ucfg = NULL;
11767       }
11768       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11769       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11770          ast_config_destroy(ucfg);
11771          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11772          return 0;
11773       }
11774    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11775       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11776       return 0;
11777    } else {
11778       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11779       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11780          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11781          ucfg = NULL;
11782       }
11783    }
11784 
11785    res = actual_load_config(reload, cfg, ucfg);
11786 
11787    ast_config_destroy(cfg);
11788    ast_config_destroy(ucfg);
11789 
11790    return res;
11791 }
11792 
11793 #ifdef TEST_FRAMEWORK
11794 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11795 {
11796    ast_unload_realtime("voicemail");
11797    ast_unload_realtime("voicemail_data");
11798    return actual_load_config(reload, cfg, ucfg);
11799 }
11800 #endif
11801 
11802 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11803 {
11804    struct ast_vm_user *current;
11805    char *cat;
11806    struct ast_variable *var;
11807    const char *val;
11808    char *q, *stringp, *tmp;
11809    int x;
11810    int tmpadsi[4];
11811    char secretfn[PATH_MAX] = "";
11812 
11813 #ifdef IMAP_STORAGE
11814    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11815 #endif
11816    /* set audio control prompts */
11817    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11818    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11819    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11820    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11821    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11822 
11823    /* Free all the users structure */  
11824    free_vm_users();
11825 
11826    /* Free all the zones structure */
11827    free_vm_zones();
11828 
11829    AST_LIST_LOCK(&users);  
11830 
11831    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11832    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11833 
11834    if (cfg) {
11835       /* General settings */
11836 
11837       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11838          val = "default";
11839       ast_copy_string(userscontext, val, sizeof(userscontext));
11840       /* Attach voice message to mail message ? */
11841       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11842          val = "yes";
11843       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11844 
11845       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11846          val = "no";
11847       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11848 
11849       volgain = 0.0;
11850       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11851          sscanf(val, "%30lf", &volgain);
11852 
11853 #ifdef ODBC_STORAGE
11854       strcpy(odbc_database, "asterisk");
11855       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11856          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11857       }
11858       strcpy(odbc_table, "voicemessages");
11859       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11860          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11861       }
11862 #endif      
11863       /* Mail command */
11864       strcpy(mailcmd, SENDMAIL);
11865       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11866          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11867 
11868       maxsilence = 0;
11869       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11870          maxsilence = atoi(val);
11871          if (maxsilence > 0)
11872             maxsilence *= 1000;
11873       }
11874       
11875       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11876          maxmsg = MAXMSG;
11877       } else {
11878          maxmsg = atoi(val);
11879          if (maxmsg < 0) {
11880             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11881             maxmsg = MAXMSG;
11882          } else if (maxmsg > MAXMSGLIMIT) {
11883             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11884             maxmsg = MAXMSGLIMIT;
11885          }
11886       }
11887 
11888       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11889          maxdeletedmsg = 0;
11890       } else {
11891          if (sscanf(val, "%30d", &x) == 1)
11892             maxdeletedmsg = x;
11893          else if (ast_true(val))
11894             maxdeletedmsg = MAXMSG;
11895          else
11896             maxdeletedmsg = 0;
11897 
11898          if (maxdeletedmsg < 0) {
11899             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11900             maxdeletedmsg = MAXMSG;
11901          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11902             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11903             maxdeletedmsg = MAXMSGLIMIT;
11904          }
11905       }
11906 
11907       /* Load date format config for voicemail mail */
11908       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11909          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11910       }
11911 
11912       /* Load date format config for voicemail pager mail */
11913       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11914          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11915       }
11916 
11917       /* External password changing command */
11918       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11919          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11920          pwdchange = PWDCHANGE_EXTERNAL;
11921       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11922          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11923          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11924       }
11925  
11926       /* External password validation command */
11927       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11928          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11929          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11930       }
11931 
11932 #ifdef IMAP_STORAGE
11933       /* IMAP server address */
11934       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11935          ast_copy_string(imapserver, val, sizeof(imapserver));
11936       } else {
11937          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11938       }
11939       /* IMAP server port */
11940       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11941          ast_copy_string(imapport, val, sizeof(imapport));
11942       } else {
11943          ast_copy_string(imapport, "143", sizeof(imapport));
11944       }
11945       /* IMAP server flags */
11946       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11947          ast_copy_string(imapflags, val, sizeof(imapflags));
11948       }
11949       /* IMAP server master username */
11950       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11951          ast_copy_string(authuser, val, sizeof(authuser));
11952       }
11953       /* IMAP server master password */
11954       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11955          ast_copy_string(authpassword, val, sizeof(authpassword));
11956       }
11957       /* Expunge on exit */
11958       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11959          if (ast_false(val))
11960             expungeonhangup = 0;
11961          else
11962             expungeonhangup = 1;
11963       } else {
11964          expungeonhangup = 1;
11965       }
11966       /* IMAP voicemail folder */
11967       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11968          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11969       } else {
11970          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11971       }
11972       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11973          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11974       }
11975       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11976          imapgreetings = ast_true(val);
11977       } else {
11978          imapgreetings = 0;
11979       }
11980       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11981          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11982       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11983          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11984          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11985       } else {
11986          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11987       }
11988 
11989       /* There is some very unorthodox casting done here. This is due
11990        * to the way c-client handles the argument passed in. It expects a 
11991        * void pointer and casts the pointer directly to a long without
11992        * first dereferencing it. */
11993       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11994          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11995       } else {
11996          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11997       }
11998 
11999       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12000          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12001       } else {
12002          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12003       }
12004 
12005       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12006          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12007       } else {
12008          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12009       }
12010 
12011       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12012          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12013       } else {
12014          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12015       }
12016 
12017       /* Increment configuration version */
12018       imapversion++;
12019 #endif
12020       /* External voicemail notify application */
12021       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12022          ast_copy_string(externnotify, val, sizeof(externnotify));
12023          ast_debug(1, "found externnotify: %s\n", externnotify);
12024       } else {
12025          externnotify[0] = '\0';
12026       }
12027 
12028       /* SMDI voicemail notification */
12029       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12030          ast_debug(1, "Enabled SMDI voicemail notification\n");
12031          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12032             smdi_iface = ast_smdi_interface_find(val);
12033          } else {
12034             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12035             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12036          }
12037          if (!smdi_iface) {
12038             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12039          } 
12040       }
12041 
12042       /* Silence treshold */
12043       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12044       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12045          silencethreshold = atoi(val);
12046       
12047       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12048          val = ASTERISK_USERNAME;
12049       ast_copy_string(serveremail, val, sizeof(serveremail));
12050       
12051       vmmaxsecs = 0;
12052       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12053          if (sscanf(val, "%30d", &x) == 1) {
12054             vmmaxsecs = x;
12055          } else {
12056             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12057          }
12058       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12059          static int maxmessage_deprecate = 0;
12060          if (maxmessage_deprecate == 0) {
12061             maxmessage_deprecate = 1;
12062             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12063          }
12064          if (sscanf(val, "%30d", &x) == 1) {
12065             vmmaxsecs = x;
12066          } else {
12067             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12068          }
12069       }
12070 
12071       vmminsecs = 0;
12072       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12073          if (sscanf(val, "%30d", &x) == 1) {
12074             vmminsecs = x;
12075             if (maxsilence / 1000 >= vmminsecs) {
12076                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12077             }
12078          } else {
12079             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12080          }
12081       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12082          static int maxmessage_deprecate = 0;
12083          if (maxmessage_deprecate == 0) {
12084             maxmessage_deprecate = 1;
12085             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12086          }
12087          if (sscanf(val, "%30d", &x) == 1) {
12088             vmminsecs = x;
12089             if (maxsilence / 1000 >= vmminsecs) {
12090                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12091             }
12092          } else {
12093             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12094          }
12095       }
12096 
12097       val = ast_variable_retrieve(cfg, "general", "format");
12098       if (!val) {
12099          val = "wav";   
12100       } else {
12101          tmp = ast_strdupa(val);
12102          val = ast_format_str_reduce(tmp);
12103          if (!val) {
12104             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12105             val = "wav";
12106          }
12107       }
12108       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12109 
12110       skipms = 3000;
12111       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12112          if (sscanf(val, "%30d", &x) == 1) {
12113             maxgreet = x;
12114          } else {
12115             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12116          }
12117       }
12118 
12119       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12120          if (sscanf(val, "%30d", &x) == 1) {
12121             skipms = x;
12122          } else {
12123             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12124          }
12125       }
12126 
12127       maxlogins = 3;
12128       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12129          if (sscanf(val, "%30d", &x) == 1) {
12130             maxlogins = x;
12131          } else {
12132             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12133          }
12134       }
12135 
12136       minpassword = MINPASSWORD;
12137       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12138          if (sscanf(val, "%30d", &x) == 1) {
12139             minpassword = x;
12140          } else {
12141             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12142          }
12143       }
12144 
12145       /* Force new user to record name ? */
12146       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12147          val = "no";
12148       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12149 
12150       /* Force new user to record greetings ? */
12151       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12152          val = "no";
12153       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12154 
12155       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12156          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12157          stringp = ast_strdupa(val);
12158          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12159             if (!ast_strlen_zero(stringp)) {
12160                q = strsep(&stringp, ",");
12161                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12162                   q++;
12163                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12164                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12165             } else {
12166                cidinternalcontexts[x][0] = '\0';
12167             }
12168          }
12169       }
12170       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12171          ast_debug(1, "VM Review Option disabled globally\n");
12172          val = "no";
12173       }
12174       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12175 
12176       /* Temporary greeting reminder */
12177       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12178          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12179          val = "no";
12180       } else {
12181          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12182       }
12183       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12184       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12185          ast_debug(1, "VM next message wrap disabled globally\n");
12186          val = "no";
12187       }
12188       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12189 
12190       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12191          ast_debug(1, "VM Operator break disabled globally\n");
12192          val = "no";
12193       }
12194       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12195 
12196       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12197          ast_debug(1, "VM CID Info before msg disabled globally\n");
12198          val = "no";
12199       } 
12200       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12201 
12202       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12203          ast_debug(1, "Send Voicemail msg disabled globally\n");
12204          val = "no";
12205       }
12206       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12207    
12208       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12209          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12210          val = "yes";
12211       }
12212       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12213 
12214       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12215          ast_debug(1, "Move Heard enabled globally\n");
12216          val = "yes";
12217       }
12218       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12219 
12220       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12221          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12222          val = "no";
12223       }
12224       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12225 
12226       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12227          ast_debug(1, "Duration info before msg enabled globally\n");
12228          val = "yes";
12229       }
12230       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12231 
12232       saydurationminfo = 2;
12233       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12234          if (sscanf(val, "%30d", &x) == 1) {
12235             saydurationminfo = x;
12236          } else {
12237             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12238          }
12239       }
12240 
12241       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12242          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12243          val = "no";
12244       }
12245       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12246 
12247       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12248          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12249          ast_debug(1, "found dialout context: %s\n", dialcontext);
12250       } else {
12251          dialcontext[0] = '\0';  
12252       }
12253       
12254       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12255          ast_copy_string(callcontext, val, sizeof(callcontext));
12256          ast_debug(1, "found callback context: %s\n", callcontext);
12257       } else {
12258          callcontext[0] = '\0';
12259       }
12260 
12261       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12262          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12263          ast_debug(1, "found operator context: %s\n", exitcontext);
12264       } else {
12265          exitcontext[0] = '\0';
12266       }
12267       
12268       /* load password sounds configuration */
12269       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12270          ast_copy_string(vm_password, val, sizeof(vm_password));
12271       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12272          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12273       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12274          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12275       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12276          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12277       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12278          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12279       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12280          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12281       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12282          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12283       }
12284       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12285          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12286       }
12287       /* load configurable audio prompts */
12288       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12289          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12290       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12291          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12292       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12293          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12294       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12295          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12296       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12297          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12298 
12299       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12300          val = "no";
12301       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12302 
12303       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12304          val = "voicemail.conf";
12305       }
12306       if (!(strcmp(val, "spooldir"))) {
12307          passwordlocation = OPT_PWLOC_SPOOLDIR;
12308       } else {
12309          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12310       }
12311 
12312       poll_freq = DEFAULT_POLL_FREQ;
12313       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12314          if (sscanf(val, "%30u", &poll_freq) != 1) {
12315             poll_freq = DEFAULT_POLL_FREQ;
12316             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12317          }
12318       }
12319 
12320       poll_mailboxes = 0;
12321       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12322          poll_mailboxes = ast_true(val);
12323 
12324       memset(fromstring, 0, sizeof(fromstring));
12325       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12326       strcpy(charset, "ISO-8859-1");
12327       if (emailbody) {
12328          ast_free(emailbody);
12329          emailbody = NULL;
12330       }
12331       if (emailsubject) {
12332          ast_free(emailsubject);
12333          emailsubject = NULL;
12334       }
12335       if (pagerbody) {
12336          ast_free(pagerbody);
12337          pagerbody = NULL;
12338       }
12339       if (pagersubject) {
12340          ast_free(pagersubject);
12341          pagersubject = NULL;
12342       }
12343       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12344          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12345       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12346          ast_copy_string(fromstring, val, sizeof(fromstring));
12347       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12348          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12349       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12350          ast_copy_string(charset, val, sizeof(charset));
12351       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12352          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12353          for (x = 0; x < 4; x++) {
12354             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12355          }
12356       }
12357       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12358          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12359          for (x = 0; x < 4; x++) {
12360             memcpy(&adsisec[x], &tmpadsi[x], 1);
12361          }
12362       }
12363       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12364          if (atoi(val)) {
12365             adsiver = atoi(val);
12366          }
12367       }
12368       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12369          ast_copy_string(zonetag, val, sizeof(zonetag));
12370       }
12371       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12372          ast_copy_string(locale, val, sizeof(locale));
12373       }
12374       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12375          emailsubject = ast_strdup(substitute_escapes(val));
12376       }
12377       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12378          emailbody = ast_strdup(substitute_escapes(val));
12379       }
12380       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12381          pagersubject = ast_strdup(substitute_escapes(val));
12382       }
12383       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12384          pagerbody = ast_strdup(substitute_escapes(val));
12385       }
12386 
12387       /* load mailboxes from users.conf */
12388       if (ucfg) { 
12389          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12390             if (!strcasecmp(cat, "general")) {
12391                continue;
12392             }
12393             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12394                continue;
12395             if ((current = find_or_create(userscontext, cat))) {
12396                populate_defaults(current);
12397                apply_options_full(current, ast_variable_browse(ucfg, cat));
12398                ast_copy_string(current->context, userscontext, sizeof(current->context));
12399                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12400                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12401                }
12402 
12403                switch (current->passwordlocation) {
12404                case OPT_PWLOC_SPOOLDIR:
12405                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12406                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12407                }
12408             }
12409          }
12410       }
12411 
12412       /* load mailboxes from voicemail.conf */
12413       cat = ast_category_browse(cfg, NULL);
12414       while (cat) {
12415          if (strcasecmp(cat, "general")) {
12416             var = ast_variable_browse(cfg, cat);
12417             if (strcasecmp(cat, "zonemessages")) {
12418                /* Process mailboxes in this context */
12419                while (var) {
12420                   append_mailbox(cat, var->name, var->value);
12421                   var = var->next;
12422                }
12423             } else {
12424                /* Timezones in this context */
12425                while (var) {
12426                   struct vm_zone *z;
12427                   if ((z = ast_malloc(sizeof(*z)))) {
12428                      char *msg_format, *tzone;
12429                      msg_format = ast_strdupa(var->value);
12430                      tzone = strsep(&msg_format, "|,");
12431                      if (msg_format) {
12432                         ast_copy_string(z->name, var->name, sizeof(z->name));
12433                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12434                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12435                         AST_LIST_LOCK(&zones);
12436                         AST_LIST_INSERT_HEAD(&zones, z, list);
12437                         AST_LIST_UNLOCK(&zones);
12438                      } else {
12439                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12440                         ast_free(z);
12441                      }
12442                   } else {
12443                      AST_LIST_UNLOCK(&users);
12444                      return -1;
12445                   }
12446                   var = var->next;
12447                }
12448             }
12449          }
12450          cat = ast_category_browse(cfg, cat);
12451       }
12452 
12453       AST_LIST_UNLOCK(&users);
12454 
12455       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12456          start_poll_thread();
12457       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12458          stop_poll_thread();;
12459 
12460       return 0;
12461    } else {
12462       AST_LIST_UNLOCK(&users);
12463       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12464       return 0;
12465    }
12466 }
12467 
12468 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12469 {
12470    int res = -1;
12471    char dir[PATH_MAX];
12472    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12473    ast_debug(2, "About to try retrieving name file %s\n", dir);
12474    RETRIEVE(dir, -1, mailbox, context);
12475    if (ast_fileexists(dir, NULL, NULL)) {
12476       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12477    }
12478    DISPOSE(dir, -1);
12479    return res;
12480 }
12481 
12482 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12483    struct ast_config *pwconf;
12484    struct ast_flags config_flags = { 0 };
12485 
12486    pwconf = ast_config_load(secretfn, config_flags);
12487    if (pwconf) {
12488       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12489       if (val) {
12490          ast_copy_string(password, val, passwordlen);
12491          return;
12492       }
12493    }
12494    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12495 }
12496 
12497 static int write_password_to_file(const char *secretfn, const char *password) {
12498    struct ast_config *conf;
12499    struct ast_category *cat;
12500    struct ast_variable *var;
12501 
12502    if (!(conf=ast_config_new())) {
12503       ast_log(LOG_ERROR, "Error creating new config structure\n");
12504       return -1;
12505    }
12506    if (!(cat=ast_category_new("general","",1))) {
12507       ast_log(LOG_ERROR, "Error creating new category structure\n");
12508       return -1;
12509    }
12510    if (!(var=ast_variable_new("password",password,""))) {
12511       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12512       return -1;
12513    }
12514    ast_category_append(conf,cat);
12515    ast_variable_append(cat,var);
12516    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12517       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12518       return -1;
12519    }
12520    return 0;
12521 }
12522 
12523 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12524 {
12525    char *context;
12526    char *args_copy;
12527    int res;
12528 
12529    if (ast_strlen_zero(data)) {
12530       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12531       return -1;
12532    }
12533 
12534    args_copy = ast_strdupa(data);
12535    if ((context = strchr(args_copy, '@'))) {
12536       *context++ = '\0';
12537    } else {
12538       context = "default";
12539    }
12540 
12541    if ((res = sayname(chan, args_copy, context) < 0)) {
12542       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12543       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12544       if (!res) {
12545          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12546       }
12547    }
12548 
12549    return res;
12550 }
12551 
12552 #ifdef TEST_FRAMEWORK
12553 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12554 {
12555    return 0;
12556 }
12557 
12558 static struct ast_frame *fake_read(struct ast_channel *ast)
12559 {
12560    return &ast_null_frame;
12561 }
12562 
12563 AST_TEST_DEFINE(test_voicemail_vmsayname)
12564 {
12565    char dir[PATH_MAX];
12566    char dir2[PATH_MAX];
12567    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12568    static const char TEST_EXTENSION[] = "1234";
12569 
12570    struct ast_channel *test_channel1 = NULL;
12571    int res = -1;
12572 
12573    static const struct ast_channel_tech fake_tech = {
12574       .write = fake_write,
12575       .read = fake_read,
12576    };
12577 
12578    switch (cmd) {
12579    case TEST_INIT:
12580       info->name = "vmsayname_exec";
12581       info->category = "/apps/app_voicemail/";
12582       info->summary = "Vmsayname unit test";
12583       info->description =
12584          "This tests passing various parameters to vmsayname";
12585       return AST_TEST_NOT_RUN;
12586    case TEST_EXECUTE:
12587       break;
12588    }
12589 
12590    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12591         NULL, NULL, 0, 0, "TestChannel1"))) {
12592       goto exit_vmsayname_test;
12593    }
12594 
12595    /* normally this is done in the channel driver */
12596    test_channel1->nativeformats = AST_FORMAT_GSM;
12597    test_channel1->writeformat = AST_FORMAT_GSM;
12598    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12599    test_channel1->readformat = AST_FORMAT_GSM;
12600    test_channel1->rawreadformat = AST_FORMAT_GSM;
12601    test_channel1->tech = &fake_tech;
12602 
12603    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12604    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12605    if (!(res = vmsayname_exec(test_channel1, dir))) {
12606       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12607       if (ast_fileexists(dir, NULL, NULL)) {
12608          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12609          res = -1;
12610          goto exit_vmsayname_test;
12611       } else {
12612          /* no greeting already exists as expected, let's create one to fully test sayname */
12613          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12614             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12615             goto exit_vmsayname_test;
12616          }
12617          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12618          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12619          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12620          if ((res = symlink(dir, dir2))) {
12621             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12622             goto exit_vmsayname_test;
12623          }
12624          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12625          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12626          res = vmsayname_exec(test_channel1, dir);
12627 
12628          /* TODO: there may be a better way to do this */
12629          unlink(dir2);
12630          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12631          rmdir(dir2);
12632          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12633          rmdir(dir2);
12634       }
12635    }
12636 
12637 exit_vmsayname_test:
12638 
12639    if (test_channel1) {
12640       ast_hangup(test_channel1);
12641    }
12642 
12643    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12644 }
12645 
12646 AST_TEST_DEFINE(test_voicemail_msgcount)
12647 {
12648    int i, j, res = AST_TEST_PASS, syserr;
12649    struct ast_vm_user *vmu;
12650    struct vm_state vms;
12651 #ifdef IMAP_STORAGE
12652    struct ast_channel *chan = NULL;
12653 #endif
12654    struct {
12655       char dir[256];
12656       char file[256];
12657       char txtfile[256];
12658    } tmp[3];
12659    char syscmd[256];
12660    const char origweasels[] = "tt-weasels";
12661    const char testcontext[] = "test";
12662    const char testmailbox[] = "00000000";
12663    const char testspec[] = "00000000@test";
12664    FILE *txt;
12665    int new, old, urgent;
12666    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12667    const int folder2mbox[3] = { 1, 11, 0 };
12668    const int expected_results[3][12] = {
12669       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12670       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12671       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12672       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12673    };
12674 
12675    switch (cmd) {
12676    case TEST_INIT:
12677       info->name = "test_voicemail_msgcount";
12678       info->category = "/apps/app_voicemail/";
12679       info->summary = "Test Voicemail status checks";
12680       info->description =
12681          "Verify that message counts are correct when retrieved through the public API";
12682       return AST_TEST_NOT_RUN;
12683    case TEST_EXECUTE:
12684       break;
12685    }
12686 
12687    /* Make sure the original path was completely empty */
12688    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12689    if ((syserr = ast_safe_system(syscmd))) {
12690       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12691          syserr > 0 ? strerror(syserr) : "unable to fork()");
12692       return AST_TEST_FAIL;
12693    }
12694 
12695 #ifdef IMAP_STORAGE
12696    if (!(chan = ast_dummy_channel_alloc())) {
12697       ast_test_status_update(test, "Unable to create dummy channel\n");
12698       return AST_TEST_FAIL;
12699    }
12700 #endif
12701 
12702    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12703       !(vmu = find_or_create(testcontext, testmailbox))) {
12704       ast_test_status_update(test, "Cannot create vmu structure\n");
12705       ast_unreplace_sigchld();
12706 #ifdef IMAP_STORAGE
12707       chan = ast_channel_unref(chan);
12708 #endif
12709       return AST_TEST_FAIL;
12710    }
12711 
12712    populate_defaults(vmu);
12713    memset(&vms, 0, sizeof(vms));
12714 
12715    /* Create temporary voicemail */
12716    for (i = 0; i < 3; i++) {
12717       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12718       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12719       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12720 
12721       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12722          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12723             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12724          if ((syserr = ast_safe_system(syscmd))) {
12725             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12726                syserr > 0 ? strerror(syserr) : "unable to fork()");
12727             ast_unreplace_sigchld();
12728 #ifdef IMAP_STORAGE
12729             chan = ast_channel_unref(chan);
12730 #endif
12731             return AST_TEST_FAIL;
12732          }
12733       }
12734 
12735       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12736          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12737          fclose(txt);
12738       } else {
12739          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12740          res = AST_TEST_FAIL;
12741          break;
12742       }
12743       open_mailbox(&vms, vmu, folder2mbox[i]);
12744       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12745 
12746       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12747       for (j = 0; j < 3; j++) {
12748          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12749          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12750             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12751                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12752             res = AST_TEST_FAIL;
12753          }
12754       }
12755 
12756       new = old = urgent = 0;
12757       if (ast_app_inboxcount(testspec, &new, &old)) {
12758          ast_test_status_update(test, "inboxcount returned failure\n");
12759          res = AST_TEST_FAIL;
12760       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12761          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12762             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12763          res = AST_TEST_FAIL;
12764       }
12765 
12766       new = old = urgent = 0;
12767       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12768          ast_test_status_update(test, "inboxcount2 returned failure\n");
12769          res = AST_TEST_FAIL;
12770       } else if (old != expected_results[i][6 + 0] ||
12771             urgent != expected_results[i][6 + 1] ||
12772                new != expected_results[i][6 + 2]    ) {
12773          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12774             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12775          res = AST_TEST_FAIL;
12776       }
12777 
12778       new = old = urgent = 0;
12779       for (j = 0; j < 3; j++) {
12780          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12781             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12782                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12783             res = AST_TEST_FAIL;
12784          }
12785       }
12786    }
12787 
12788    for (i = 0; i < 3; i++) {
12789       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12790        * server, in which case, the rm below will not affect the
12791        * voicemails. */
12792       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12793       DISPOSE(tmp[i].dir, 0);
12794    }
12795 
12796    if (vms.deleted) {
12797       ast_free(vms.deleted);
12798    }
12799    if (vms.heard) {
12800       ast_free(vms.heard);
12801    }
12802 
12803 #ifdef IMAP_STORAGE
12804    chan = ast_channel_unref(chan);
12805 #endif
12806 
12807    /* And remove test directory */
12808    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12809    if ((syserr = ast_safe_system(syscmd))) {
12810       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12811          syserr > 0 ? strerror(syserr) : "unable to fork()");
12812    }
12813 
12814    return res;
12815 }
12816 
12817 AST_TEST_DEFINE(test_voicemail_notify_endl)
12818 {
12819    int res = AST_TEST_PASS;
12820    char testcontext[] = "test";
12821    char testmailbox[] = "00000000";
12822    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12823    char attach[256], attach2[256];
12824    char buf[256] = ""; /* No line should actually be longer than 80 */
12825    struct ast_channel *chan = NULL;
12826    struct ast_vm_user *vmu, vmus = {
12827       .flags = 0,
12828    };
12829    FILE *file;
12830    struct {
12831       char *name;
12832       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12833       void *location;
12834       union {
12835          int intval;
12836          char *strval;
12837       } u;
12838    } test_items[] = {
12839       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12840       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12841       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12842       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12843       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12844       { "attach2", STRPTR, attach2, .u.strval = "" },
12845       { "attach", STRPTR, attach, .u.strval = "" },
12846    };
12847    int which;
12848 
12849    switch (cmd) {
12850    case TEST_INIT:
12851       info->name = "test_voicemail_notify_endl";
12852       info->category = "/apps/app_voicemail/";
12853       info->summary = "Test Voicemail notification end-of-line";
12854       info->description =
12855          "Verify that notification emails use a consistent end-of-line character";
12856       return AST_TEST_NOT_RUN;
12857    case TEST_EXECUTE:
12858       break;
12859    }
12860 
12861    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12862    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12863 
12864    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12865       !(vmu = find_or_create(testcontext, testmailbox))) {
12866       ast_test_status_update(test, "Cannot create vmu structure\n");
12867       return AST_TEST_NOT_RUN;
12868    }
12869 
12870    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12871       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12872       return AST_TEST_NOT_RUN;
12873    }
12874 
12875    populate_defaults(vmu);
12876    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12877 #ifdef IMAP_STORAGE
12878    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12879 #endif
12880 
12881    file = tmpfile();
12882    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12883       /* Kill previous test, if any */
12884       rewind(file);
12885       if (ftruncate(fileno(file), 0)) {
12886          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12887          res = AST_TEST_FAIL;
12888          break;
12889       }
12890 
12891       /* Make each change, in order, to the test mailbox */
12892       if (test_items[which].type == INT) {
12893          *((int *) test_items[which].location) = test_items[which].u.intval;
12894       } else if (test_items[which].type == FLAGVAL) {
12895          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12896             ast_clear_flag(vmu, test_items[which].u.intval);
12897          } else {
12898             ast_set_flag(vmu, test_items[which].u.intval);
12899          }
12900       } else if (test_items[which].type == STATIC) {
12901          strcpy(test_items[which].location, test_items[which].u.strval);
12902       } else if (test_items[which].type == STRPTR) {
12903          test_items[which].location = test_items[which].u.strval;
12904       }
12905 
12906       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12907       rewind(file);
12908       while (fgets(buf, sizeof(buf), file)) {
12909          if (
12910 #ifdef IMAP_STORAGE
12911          buf[strlen(buf) - 2] != '\r'
12912 #else
12913          buf[strlen(buf) - 2] == '\r'
12914 #endif
12915          || buf[strlen(buf) - 1] != '\n') {
12916             res = AST_TEST_FAIL;
12917          }
12918       }
12919    }
12920    fclose(file);
12921    return res;
12922 }
12923 
12924 AST_TEST_DEFINE(test_voicemail_load_config)
12925 {
12926    int res = AST_TEST_PASS;
12927    struct ast_vm_user *vmu;
12928    struct ast_config *cfg;
12929    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12930    int fd;
12931    FILE *file;
12932    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12933 
12934    switch (cmd) {
12935    case TEST_INIT:
12936       info->name = "test_voicemail_load_config";
12937       info->category = "/apps/app_voicemail/";
12938       info->summary = "Test loading Voicemail config";
12939       info->description =
12940          "Verify that configuration is loaded consistently. "
12941          "This is to test regressions of ASTERISK-18838 where it was noticed that "
12942          "some options were loaded after the mailboxes were instantiated, causing "
12943          "those options not to be set correctly.";
12944       return AST_TEST_NOT_RUN;
12945    case TEST_EXECUTE:
12946       break;
12947    }
12948 
12949    /* build a config file by hand... */
12950    if ((fd = mkstemp(config_filename)) < 0) {
12951       return AST_TEST_FAIL;
12952    }
12953    if (!(file = fdopen(fd, "w"))) {
12954       close(fd);
12955       unlink(config_filename);
12956       return AST_TEST_FAIL;
12957    }
12958    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
12959    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
12960    fputs("00000002 => 9999,Mrs. Test\n", file);
12961    fclose(file);
12962 
12963    if (!(cfg = ast_config_load(config_filename, config_flags))) {
12964       res = AST_TEST_FAIL;
12965       goto cleanup;
12966    }
12967 
12968    load_config_from_memory(1, cfg, NULL);
12969    ast_config_destroy(cfg);
12970 
12971 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
12972    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
12973    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
12974 
12975    AST_LIST_LOCK(&users);
12976    AST_LIST_TRAVERSE(&users, vmu, list) {
12977       if (!strcmp(vmu->mailbox, "00000001")) {
12978          if (0); /* trick to get CHECK to work */
12979          CHECK(vmu, callback, "othercontext")
12980          CHECK(vmu, locale, "nl_NL.UTF-8")
12981          CHECK(vmu, zonetag, "central")
12982       } else if (!strcmp(vmu->mailbox, "00000002")) {
12983          if (0); /* trick to get CHECK to work */
12984          CHECK(vmu, callback, "somecontext")
12985          CHECK(vmu, locale, "de_DE.UTF-8")
12986          CHECK(vmu, zonetag, "european")
12987       }
12988    }
12989    AST_LIST_UNLOCK(&users);
12990 
12991 #undef CHECK
12992 
12993    /* restore config */
12994    load_config(1); /* this might say "Failed to load configuration file." */
12995 
12996 cleanup:
12997    unlink(config_filename);
12998    return res;
12999 }
13000 
13001 #endif /* defined(TEST_FRAMEWORK) */
13002 
13003 static int reload(void)
13004 {
13005    return load_config(1);
13006 }
13007 
13008 static int unload_module(void)
13009 {
13010    int res;
13011 
13012    res = ast_unregister_application(app);
13013    res |= ast_unregister_application(app2);
13014    res |= ast_unregister_application(app3);
13015    res |= ast_unregister_application(app4);
13016    res |= ast_unregister_application(sayname_app);
13017    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13018    res |= ast_manager_unregister("VoicemailUsersList");
13019    res |= ast_data_unregister(NULL);
13020 #ifdef TEST_FRAMEWORK
13021    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13022    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13023    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13024    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13025    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13026 #endif
13027    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13028    ast_uninstall_vm_functions();
13029    ao2_ref(inprocess_container, -1);
13030 
13031    if (poll_thread != AST_PTHREADT_NULL)
13032       stop_poll_thread();
13033 
13034    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13035    ast_unload_realtime("voicemail");
13036    ast_unload_realtime("voicemail_data");
13037 
13038    free_vm_users();
13039    free_vm_zones();
13040    return res;
13041 }
13042 
13043 static int load_module(void)
13044 {
13045    int res;
13046    my_umask = umask(0);
13047    umask(my_umask);
13048 
13049    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13050       return AST_MODULE_LOAD_DECLINE;
13051    }
13052 
13053    /* compute the location of the voicemail spool directory */
13054    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13055    
13056    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13057       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13058    }
13059 
13060    if ((res = load_config(0)))
13061       return res;
13062 
13063    res = ast_register_application_xml(app, vm_exec);
13064    res |= ast_register_application_xml(app2, vm_execmain);
13065    res |= ast_register_application_xml(app3, vm_box_exists);
13066    res |= ast_register_application_xml(app4, vmauthenticate);
13067    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13068    res |= ast_custom_function_register(&mailbox_exists_acf);
13069    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13070 #ifdef TEST_FRAMEWORK
13071    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13072    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13073    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13074    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13075    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13076 #endif
13077 
13078    if (res)
13079       return res;
13080 
13081    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13082    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13083 
13084    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13085    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13086    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13087 
13088    return res;
13089 }
13090 
13091 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13092 {
13093    int cmd = 0;
13094    char destination[80] = "";
13095    int retries = 0;
13096 
13097    if (!num) {
13098       ast_verb(3, "Destination number will be entered manually\n");
13099       while (retries < 3 && cmd != 't') {
13100          destination[1] = '\0';
13101          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13102          if (!cmd)
13103             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13104          if (!cmd)
13105             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13106          if (!cmd) {
13107             cmd = ast_waitfordigit(chan, 6000);
13108             if (cmd)
13109                destination[0] = cmd;
13110          }
13111          if (!cmd) {
13112             retries++;
13113          } else {
13114 
13115             if (cmd < 0)
13116                return 0;
13117             if (cmd == '*') {
13118                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13119                return 0;
13120             }
13121             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13122                retries++;
13123             else
13124                cmd = 't';
13125          }
13126          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13127       }
13128       if (retries >= 3) {
13129          return 0;
13130       }
13131       
13132    } else {
13133       if (option_verbose > 2)
13134          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13135       ast_copy_string(destination, num, sizeof(destination));
13136    }
13137 
13138    if (!ast_strlen_zero(destination)) {
13139       if (destination[strlen(destination) -1 ] == '*')
13140          return 0; 
13141       if (option_verbose > 2)
13142          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13143       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13144       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13145       chan->priority = 0;
13146       return 9;
13147    }
13148    return 0;
13149 }
13150 
13151 /*!
13152  * \brief The advanced options within a message.
13153  * \param chan
13154  * \param vmu 
13155  * \param vms
13156  * \param msg
13157  * \param option
13158  * \param record_gain
13159  *
13160  * Provides handling for the play message envelope, call the person back, or reply to message. 
13161  *
13162  * \return zero on success, -1 on error.
13163  */
13164 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)
13165 {
13166    int res = 0;
13167    char filename[PATH_MAX];
13168    struct ast_config *msg_cfg = NULL;
13169    const char *origtime, *context;
13170    char *name, *num;
13171    int retries = 0;
13172    char *cid;
13173    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13174 
13175    vms->starting = 0; 
13176 
13177    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13178 
13179    /* Retrieve info from VM attribute file */
13180    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13181    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13182    msg_cfg = ast_config_load(filename, config_flags);
13183    DISPOSE(vms->curdir, vms->curmsg);
13184    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13185       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13186       return 0;
13187    }
13188 
13189    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13190       ast_config_destroy(msg_cfg);
13191       return 0;
13192    }
13193 
13194    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13195 
13196    context = ast_variable_retrieve(msg_cfg, "message", "context");
13197    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13198       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13199    switch (option) {
13200    case 3: /* Play message envelope */
13201       if (!res)
13202          res = play_message_datetime(chan, vmu, origtime, filename);
13203       if (!res)
13204          res = play_message_callerid(chan, vms, cid, context, 0);
13205 
13206       res = 't';
13207       break;
13208 
13209    case 2:  /* Call back */
13210 
13211       if (ast_strlen_zero(cid))
13212          break;
13213 
13214       ast_callerid_parse(cid, &name, &num);
13215       while ((res > -1) && (res != 't')) {
13216          switch (res) {
13217          case '1':
13218             if (num) {
13219                /* Dial the CID number */
13220                res = dialout(chan, vmu, num, vmu->callback);
13221                if (res) {
13222                   ast_config_destroy(msg_cfg);
13223                   return 9;
13224                }
13225             } else {
13226                res = '2';
13227             }
13228             break;
13229 
13230          case '2':
13231             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13232             if (!ast_strlen_zero(vmu->dialout)) {
13233                res = dialout(chan, vmu, NULL, vmu->dialout);
13234                if (res) {
13235                   ast_config_destroy(msg_cfg);
13236                   return 9;
13237                }
13238             } else {
13239                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13240                res = ast_play_and_wait(chan, "vm-sorry");
13241             }
13242             ast_config_destroy(msg_cfg);
13243             return res;
13244          case '*':
13245             res = 't';
13246             break;
13247          case '3':
13248          case '4':
13249          case '5':
13250          case '6':
13251          case '7':
13252          case '8':
13253          case '9':
13254          case '0':
13255 
13256             res = ast_play_and_wait(chan, "vm-sorry");
13257             retries++;
13258             break;
13259          default:
13260             if (num) {
13261                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13262                res = ast_play_and_wait(chan, "vm-num-i-have");
13263                if (!res)
13264                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13265                if (!res)
13266                   res = ast_play_and_wait(chan, "vm-tocallnum");
13267                /* Only prompt for a caller-specified number if there is a dialout context specified */
13268                if (!ast_strlen_zero(vmu->dialout)) {
13269                   if (!res)
13270                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13271                }
13272             } else {
13273                res = ast_play_and_wait(chan, "vm-nonumber");
13274                if (!ast_strlen_zero(vmu->dialout)) {
13275                   if (!res)
13276                      res = ast_play_and_wait(chan, "vm-toenternumber");
13277                }
13278             }
13279             if (!res) {
13280                res = ast_play_and_wait(chan, "vm-star-cancel");
13281             }
13282             if (!res) {
13283                res = ast_waitfordigit(chan, 6000);
13284             }
13285             if (!res) {
13286                retries++;
13287                if (retries > 3) {
13288                   res = 't';
13289                }
13290             }
13291             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13292             break; 
13293             
13294          }
13295          if (res == 't')
13296             res = 0;
13297          else if (res == '*')
13298             res = -1;
13299       }
13300       break;
13301       
13302    case 1:  /* Reply */
13303       /* Send reply directly to sender */
13304       if (ast_strlen_zero(cid))
13305          break;
13306 
13307       ast_callerid_parse(cid, &name, &num);
13308       if (!num) {
13309          ast_verb(3, "No CID number available, no reply sent\n");
13310          if (!res)
13311             res = ast_play_and_wait(chan, "vm-nonumber");
13312          ast_config_destroy(msg_cfg);
13313          return res;
13314       } else {
13315          struct ast_vm_user vmu2;
13316          if (find_user(&vmu2, vmu->context, num)) {
13317             struct leave_vm_options leave_options;
13318             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13319             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13320 
13321             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13322             
13323             memset(&leave_options, 0, sizeof(leave_options));
13324             leave_options.record_gain = record_gain;
13325             res = leave_voicemail(chan, mailbox, &leave_options);
13326             if (!res)
13327                res = 't';
13328             ast_config_destroy(msg_cfg);
13329             return res;
13330          } else {
13331             /* Sender has no mailbox, can't reply */
13332             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13333             ast_play_and_wait(chan, "vm-nobox");
13334             res = 't';
13335             ast_config_destroy(msg_cfg);
13336             return res;
13337          }
13338       } 
13339       res = 0;
13340 
13341       break;
13342    }
13343 
13344 #ifndef IMAP_STORAGE
13345    ast_config_destroy(msg_cfg);
13346 
13347    if (!res) {
13348       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13349       vms->heard[msg] = 1;
13350       res = wait_file(chan, vms, vms->fn);
13351    }
13352 #endif
13353    return res;
13354 }
13355 
13356 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13357          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13358          signed char record_gain, struct vm_state *vms, char *flag)
13359 {
13360    /* Record message & let caller review or re-record it, or set options if applicable */
13361    int res = 0;
13362    int cmd = 0;
13363    int max_attempts = 3;
13364    int attempts = 0;
13365    int recorded = 0;
13366    int msg_exists = 0;
13367    signed char zero_gain = 0;
13368    char tempfile[PATH_MAX];
13369    char *acceptdtmf = "#";
13370    char *canceldtmf = "";
13371    int canceleddtmf = 0;
13372 
13373    /* Note that urgent and private are for flagging messages as such in the future */
13374 
13375    /* barf if no pointer passed to store duration in */
13376    if (duration == NULL) {
13377       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13378       return -1;
13379    }
13380 
13381    if (!outsidecaller)
13382       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13383    else
13384       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13385 
13386    cmd = '3';  /* Want to start by recording */
13387 
13388    while ((cmd >= 0) && (cmd != 't')) {
13389       switch (cmd) {
13390       case '1':
13391          if (!msg_exists) {
13392             /* In this case, 1 is to record a message */
13393             cmd = '3';
13394             break;
13395          } else {
13396             /* Otherwise 1 is to save the existing message */
13397             ast_verb(3, "Saving message as is\n");
13398             if (!outsidecaller) 
13399                ast_filerename(tempfile, recordfile, NULL);
13400             ast_stream_and_wait(chan, "vm-msgsaved", "");
13401             if (!outsidecaller) {
13402                /* Saves to IMAP server only if imapgreeting=yes */
13403                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13404                DISPOSE(recordfile, -1);
13405             }
13406             cmd = 't';
13407             return res;
13408          }
13409       case '2':
13410          /* Review */
13411          ast_verb(3, "Reviewing the message\n");
13412          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13413          break;
13414       case '3':
13415          msg_exists = 0;
13416          /* Record */
13417          if (recorded == 1) 
13418             ast_verb(3, "Re-recording the message\n");
13419          else  
13420             ast_verb(3, "Recording the message\n");
13421          
13422          if (recorded && outsidecaller) {
13423             cmd = ast_play_and_wait(chan, INTRO);
13424             cmd = ast_play_and_wait(chan, "beep");
13425          }
13426          recorded = 1;
13427          /* 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 */
13428          if (record_gain)
13429             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13430          if (ast_test_flag(vmu, VM_OPERATOR))
13431             canceldtmf = "0";
13432          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13433          if (strchr(canceldtmf, cmd)) {
13434          /* need this flag here to distinguish between pressing '0' during message recording or after */
13435             canceleddtmf = 1;
13436          }
13437          if (record_gain)
13438             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13439          if (cmd == -1) {
13440             /* User has hung up, no options to give */
13441             if (!outsidecaller) {
13442                /* user was recording a greeting and they hung up, so let's delete the recording. */
13443                ast_filedelete(tempfile, NULL);
13444             }     
13445             return cmd;
13446          }
13447          if (cmd == '0') {
13448             break;
13449          } else if (cmd == '*') {
13450             break;
13451 #if 0
13452          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13453             /* Message is too short */
13454             ast_verb(3, "Message too short\n");
13455             cmd = ast_play_and_wait(chan, "vm-tooshort");
13456             cmd = ast_filedelete(tempfile, NULL);
13457             break;
13458          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13459             /* Message is all silence */
13460             ast_verb(3, "Nothing recorded\n");
13461             cmd = ast_filedelete(tempfile, NULL);
13462             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13463             if (!cmd)
13464                cmd = ast_play_and_wait(chan, "vm-speakup");
13465             break;
13466 #endif
13467          } else {
13468             /* If all is well, a message exists */
13469             msg_exists = 1;
13470             cmd = 0;
13471          }
13472          break;
13473       case '4':
13474          if (outsidecaller) {  /* only mark vm messages */
13475             /* Mark Urgent */
13476             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13477                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13478                res = ast_play_and_wait(chan, "vm-marked-urgent");
13479                strcpy(flag, "Urgent");
13480             } else if (flag) {
13481                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13482                res = ast_play_and_wait(chan, "vm-urgent-removed");
13483                strcpy(flag, "");
13484             } else {
13485                ast_play_and_wait(chan, "vm-sorry");
13486             }
13487             cmd = 0;
13488          } else {
13489             cmd = ast_play_and_wait(chan, "vm-sorry");
13490          }
13491          break;
13492       case '5':
13493       case '6':
13494       case '7':
13495       case '8':
13496       case '9':
13497       case '*':
13498       case '#':
13499          cmd = ast_play_and_wait(chan, "vm-sorry");
13500          break;
13501 #if 0 
13502 /*  XXX Commented out for the moment because of the dangers of deleting
13503     a message while recording (can put the message numbers out of sync) */
13504       case '*':
13505          /* Cancel recording, delete message, offer to take another message*/
13506          cmd = ast_play_and_wait(chan, "vm-deleted");
13507          cmd = ast_filedelete(tempfile, NULL);
13508          if (outsidecaller) {
13509             res = vm_exec(chan, NULL);
13510             return res;
13511          }
13512          else
13513             return 1;
13514 #endif
13515       case '0':
13516          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13517             cmd = ast_play_and_wait(chan, "vm-sorry");
13518             break;
13519          }
13520          if (msg_exists || recorded) {
13521             cmd = ast_play_and_wait(chan, "vm-saveoper");
13522             if (!cmd)
13523                cmd = ast_waitfordigit(chan, 3000);
13524             if (cmd == '1') {
13525                ast_filerename(tempfile, recordfile, NULL);
13526                ast_play_and_wait(chan, "vm-msgsaved");
13527                cmd = '0';
13528             } else if (cmd == '4') {
13529                if (flag) {
13530                   ast_play_and_wait(chan, "vm-marked-urgent");
13531                   strcpy(flag, "Urgent");
13532                }
13533                ast_play_and_wait(chan, "vm-msgsaved");
13534                cmd = '0';
13535             } else {
13536                ast_play_and_wait(chan, "vm-deleted");
13537                DELETE(tempfile, -1, tempfile, vmu);
13538                cmd = '0';
13539             }
13540          }
13541          return cmd;
13542       default:
13543          /* If the caller is an ouside caller, and the review option is enabled,
13544             allow them to review the message, but let the owner of the box review
13545             their OGM's */
13546          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13547             return cmd;
13548          if (msg_exists) {
13549             cmd = ast_play_and_wait(chan, "vm-review");
13550             if (!cmd && outsidecaller) {
13551                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13552                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13553                } else if (flag) {
13554                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13555                }
13556             }
13557          } else {
13558             cmd = ast_play_and_wait(chan, "vm-torerecord");
13559             if (!cmd)
13560                cmd = ast_waitfordigit(chan, 600);
13561          }
13562          
13563          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13564             cmd = ast_play_and_wait(chan, "vm-reachoper");
13565             if (!cmd)
13566                cmd = ast_waitfordigit(chan, 600);
13567          }
13568 #if 0
13569          if (!cmd)
13570             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13571 #endif
13572          if (!cmd)
13573             cmd = ast_waitfordigit(chan, 6000);
13574          if (!cmd) {
13575             attempts++;
13576          }
13577          if (attempts > max_attempts) {
13578             cmd = 't';
13579          }
13580       }
13581    }
13582    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13583       /* Hang up or timeout, so delete the recording. */
13584       ast_filedelete(tempfile, NULL);
13585    }
13586 
13587    if (cmd != 't' && outsidecaller)
13588       ast_play_and_wait(chan, "vm-goodbye");
13589 
13590    return cmd;
13591 }
13592 
13593 /* This is a workaround so that menuselect displays a proper description
13594  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13595  */
13596 
13597 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13598       .load = load_module,
13599       .unload = unload_module,
13600       .reload = reload,
13601       .nonoptreq = "res_adsi,res_smdi",
13602       );

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