Tue Aug 20 16:34:24 2013

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: 376262 $")
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 language[MAX_LANGUAGE];     /*!< Config: Language setting */
00625    char zonetag[80];                /*!< Time zone */
00626    char locale[20];                 /*!< The locale (for presentation of date/time) */
00627    char callback[80];
00628    char dialout[80];
00629    char uniqueid[80];               /*!< Unique integer identifier */
00630    char exit[80];
00631    char attachfmt[20];              /*!< Attachment format */
00632    unsigned int flags;              /*!< VM_ flags */ 
00633    int saydurationm;
00634    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00635    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00636    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00637    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00638    int passwordlocation;            /*!< Storage location of the password */
00639 #ifdef IMAP_STORAGE
00640    char imapuser[80];               /*!< IMAP server login */
00641    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00642    char imapfolder[64];             /*!< IMAP voicemail folder */
00643    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00644    int imapversion;                 /*!< If configuration changes, use the new values */
00645 #endif
00646    double volgain;                  /*!< Volume gain for voicemails sent via email */
00647    AST_LIST_ENTRY(ast_vm_user) list;
00648 };
00649 
00650 /*! Voicemail time zones */
00651 struct vm_zone {
00652    AST_LIST_ENTRY(vm_zone) list;
00653    char name[80];
00654    char timezone[80];
00655    char msg_format[512];
00656 };
00657 
00658 #define VMSTATE_MAX_MSG_ARRAY 256
00659 
00660 /*! Voicemail mailbox state */
00661 struct vm_state {
00662    char curbox[80];
00663    char username[80];
00664    char context[80];
00665    char curdir[PATH_MAX];
00666    char vmbox[PATH_MAX];
00667    char fn[PATH_MAX];
00668    char intro[PATH_MAX];
00669    int *deleted;
00670    int *heard;
00671    int dh_arraysize; /* used for deleted / heard allocation */
00672    int curmsg;
00673    int lastmsg;
00674    int newmessages;
00675    int oldmessages;
00676    int urgentmessages;
00677    int starting;
00678    int repeats;
00679 #ifdef IMAP_STORAGE
00680    ast_mutex_t lock;
00681    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00682    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00683    MAILSTREAM *mailstream;
00684    int vmArrayIndex;
00685    char imapuser[80];                   /*!< IMAP server login */
00686    char imapfolder[64];                 /*!< IMAP voicemail folder */
00687    int imapversion;
00688    int interactive;
00689    char introfn[PATH_MAX];              /*!< Name of prepended file */
00690    unsigned int quota_limit;
00691    unsigned int quota_usage;
00692    struct vm_state *persist_vms;
00693 #endif
00694 };
00695 
00696 #ifdef ODBC_STORAGE
00697 static char odbc_database[80];
00698 static char odbc_table[80];
00699 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00700 #define DISPOSE(a,b) remove_file(a,b)
00701 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00702 #define EXISTS(a,b,c,d) (message_exists(a,b))
00703 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00704 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00705 #define DELETE(a,b,c,d) (delete_file(a,b))
00706 #else
00707 #ifdef IMAP_STORAGE
00708 #define DISPOSE(a,b) (imap_remove_file(a,b))
00709 #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))
00710 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00711 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00712 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00713 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00714 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00715 #else
00716 #define RETRIEVE(a,b,c,d)
00717 #define DISPOSE(a,b)
00718 #define STORE(a,b,c,d,e,f,g,h,i,j)
00719 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00720 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00721 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00722 #define DELETE(a,b,c,d) (vm_delete(c))
00723 #endif
00724 #endif
00725 
00726 static char VM_SPOOL_DIR[PATH_MAX];
00727 
00728 static char ext_pass_cmd[128];
00729 static char ext_pass_check_cmd[128];
00730 
00731 static int my_umask;
00732 
00733 #define PWDCHANGE_INTERNAL (1 << 1)
00734 #define PWDCHANGE_EXTERNAL (1 << 2)
00735 static int pwdchange = PWDCHANGE_INTERNAL;
00736 
00737 #ifdef ODBC_STORAGE
00738 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00739 #else
00740 # ifdef IMAP_STORAGE
00741 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00742 # else
00743 # define tdesc "Comedian Mail (Voicemail System)"
00744 # endif
00745 #endif
00746 
00747 static char userscontext[AST_MAX_EXTENSION] = "default";
00748 
00749 static char *addesc = "Comedian Mail";
00750 
00751 /* Leave a message */
00752 static char *app = "VoiceMail";
00753 
00754 /* Check mail, control, etc */
00755 static char *app2 = "VoiceMailMain";
00756 
00757 static char *app3 = "MailboxExists";
00758 static char *app4 = "VMAuthenticate";
00759 
00760 static char *sayname_app = "VMSayName";
00761 
00762 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00763 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00764 static char zonetag[80];
00765 static char locale[20];
00766 static int maxsilence;
00767 static int maxmsg;
00768 static int maxdeletedmsg;
00769 static int silencethreshold = 128;
00770 static char serveremail[80];
00771 static char mailcmd[160];  /* Configurable mail cmd */
00772 static char externnotify[160]; 
00773 static struct ast_smdi_interface *smdi_iface = NULL;
00774 static char vmfmts[80];
00775 static double volgain;
00776 static int vmminsecs;
00777 static int vmmaxsecs;
00778 static int maxgreet;
00779 static int skipms;
00780 static int maxlogins;
00781 static int minpassword;
00782 static int passwordlocation;
00783 
00784 /*! Poll mailboxes for changes since there is something external to
00785  *  app_voicemail that may change them. */
00786 static unsigned int poll_mailboxes;
00787 
00788 /*! Polling frequency */
00789 static unsigned int poll_freq;
00790 /*! By default, poll every 30 seconds */
00791 #define DEFAULT_POLL_FREQ 30
00792 
00793 AST_MUTEX_DEFINE_STATIC(poll_lock);
00794 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00795 static pthread_t poll_thread = AST_PTHREADT_NULL;
00796 static unsigned char poll_thread_run;
00797 
00798 /*! Subscription to ... MWI event subscriptions */
00799 static struct ast_event_sub *mwi_sub_sub;
00800 /*! Subscription to ... MWI event un-subscriptions */
00801 static struct ast_event_sub *mwi_unsub_sub;
00802 
00803 /*!
00804  * \brief An MWI subscription
00805  *
00806  * This is so we can keep track of which mailboxes are subscribed to.
00807  * This way, we know which mailboxes to poll when the pollmailboxes
00808  * option is being used.
00809  */
00810 struct mwi_sub {
00811    AST_RWLIST_ENTRY(mwi_sub) entry;
00812    int old_urgent;
00813    int old_new;
00814    int old_old;
00815    uint32_t uniqueid;
00816    char mailbox[1];
00817 };
00818 
00819 struct mwi_sub_task {
00820    const char *mailbox;
00821    const char *context;
00822    uint32_t uniqueid;
00823 };
00824 
00825 static struct ast_taskprocessor *mwi_subscription_tps;
00826 
00827 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00828 
00829 /* custom audio control prompts for voicemail playback */
00830 static char listen_control_forward_key[12];
00831 static char listen_control_reverse_key[12];
00832 static char listen_control_pause_key[12];
00833 static char listen_control_restart_key[12];
00834 static char listen_control_stop_key[12];
00835 
00836 /* custom password sounds */
00837 static char vm_password[80] = "vm-password";
00838 static char vm_newpassword[80] = "vm-newpassword";
00839 static char vm_passchanged[80] = "vm-passchanged";
00840 static char vm_reenterpassword[80] = "vm-reenterpassword";
00841 static char vm_mismatch[80] = "vm-mismatch";
00842 static char vm_invalid_password[80] = "vm-invalid-password";
00843 static char vm_pls_try_again[80] = "vm-pls-try-again";
00844 
00845 /*
00846  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00847  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00848  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00849  * app.c's __ast_play_and_record function
00850  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00851  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00852  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00853  * more effort than either of the other two.
00854  */
00855 static char vm_prepend_timeout[80] = "vm-then-pound";
00856 
00857 static struct ast_flags globalflags = {0};
00858 
00859 static int saydurationminfo;
00860 
00861 static char dialcontext[AST_MAX_CONTEXT] = "";
00862 static char callcontext[AST_MAX_CONTEXT] = "";
00863 static char exitcontext[AST_MAX_CONTEXT] = "";
00864 
00865 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00866 
00867 
00868 static char *emailbody = NULL;
00869 static char *emailsubject = NULL;
00870 static char *pagerbody = NULL;
00871 static char *pagersubject = NULL;
00872 static char fromstring[100];
00873 static char pagerfromstring[100];
00874 static char charset[32] = "ISO-8859-1";
00875 
00876 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00877 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00878 static int adsiver = 1;
00879 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00880 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00881 
00882 /* Forward declarations - generic */
00883 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00884 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);
00885 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00886 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00887          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00888          signed char record_gain, struct vm_state *vms, char *flag);
00889 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00890 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00891 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);
00892 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);
00893 static void apply_options(struct ast_vm_user *vmu, const char *options);
00894 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);
00895 static int is_valid_dtmf(const char *key);
00896 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00897 static int write_password_to_file(const char *secretfn, const char *password);
00898 static const char *substitute_escapes(const char *value);
00899 static void free_user(struct ast_vm_user *vmu);
00900 
00901 struct ao2_container *inprocess_container;
00902 
00903 struct inprocess {
00904    int count;
00905    char *context;
00906    char mailbox[0];
00907 };
00908 
00909 static int inprocess_hash_fn(const void *obj, const int flags)
00910 {
00911    const struct inprocess *i = obj;
00912    return atoi(i->mailbox);
00913 }
00914 
00915 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00916 {
00917    struct inprocess *i = obj, *j = arg;
00918    if (strcmp(i->mailbox, j->mailbox)) {
00919       return 0;
00920    }
00921    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00922 }
00923 
00924 static int inprocess_count(const char *context, const char *mailbox, int delta)
00925 {
00926    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00927    arg->context = arg->mailbox + strlen(mailbox) + 1;
00928    strcpy(arg->mailbox, mailbox); /* SAFE */
00929    strcpy(arg->context, context); /* SAFE */
00930    ao2_lock(inprocess_container);
00931    if ((i = ao2_find(inprocess_container, arg, 0))) {
00932       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00933       ao2_unlock(inprocess_container);
00934       ao2_ref(i, -1);
00935       return ret;
00936    }
00937    if (delta < 0) {
00938       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00939    }
00940    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00941       ao2_unlock(inprocess_container);
00942       return 0;
00943    }
00944    i->context = i->mailbox + strlen(mailbox) + 1;
00945    strcpy(i->mailbox, mailbox); /* SAFE */
00946    strcpy(i->context, context); /* SAFE */
00947    i->count = delta;
00948    ao2_link(inprocess_container, i);
00949    ao2_unlock(inprocess_container);
00950    ao2_ref(i, -1);
00951    return 0;
00952 }
00953 
00954 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00955 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00956 #endif
00957 
00958 /*!
00959  * \brief Strips control and non 7-bit clean characters from input string.
00960  *
00961  * \note To map control and none 7-bit characters to a 7-bit clean characters
00962  *  please use ast_str_encode_mine().
00963  */
00964 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00965 {
00966    char *bufptr = buf;
00967    for (; *input; input++) {
00968       if (*input < 32) {
00969          continue;
00970       }
00971       *bufptr++ = *input;
00972       if (bufptr == buf + buflen - 1) {
00973          break;
00974       }
00975    }
00976    *bufptr = '\0';
00977    return buf;
00978 }
00979 
00980 
00981 /*!
00982  * \brief Sets default voicemail system options to a voicemail user.
00983  *
00984  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00985  * - all the globalflags
00986  * - the saydurationminfo
00987  * - the callcontext
00988  * - the dialcontext
00989  * - the exitcontext
00990  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00991  * - volume gain
00992  * - emailsubject, emailbody set to NULL
00993  */
00994 static void populate_defaults(struct ast_vm_user *vmu)
00995 {
00996    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
00997    vmu->passwordlocation = passwordlocation;
00998    if (saydurationminfo) {
00999       vmu->saydurationm = saydurationminfo;
01000    }
01001    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01002    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01003    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01004    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01005    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01006    if (vmminsecs) {
01007       vmu->minsecs = vmminsecs;
01008    }
01009    if (vmmaxsecs) {
01010       vmu->maxsecs = vmmaxsecs;
01011    }
01012    if (maxmsg) {
01013       vmu->maxmsg = maxmsg;
01014    }
01015    if (maxdeletedmsg) {
01016       vmu->maxdeletedmsg = maxdeletedmsg;
01017    }
01018    vmu->volgain = volgain;
01019    ast_free(vmu->emailsubject);
01020    vmu->emailsubject = NULL;
01021    ast_free(vmu->emailbody);
01022    vmu->emailbody = NULL;
01023 #ifdef IMAP_STORAGE
01024    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01025 #endif
01026 }
01027 
01028 /*!
01029  * \brief Sets a a specific property value.
01030  * \param vmu The voicemail user object to work with.
01031  * \param var The name of the property to be set.
01032  * \param value The value to be set to the property.
01033  * 
01034  * The property name must be one of the understood properties. See the source for details.
01035  */
01036 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01037 {
01038    int x;
01039    if (!strcasecmp(var, "attach")) {
01040       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01041    } else if (!strcasecmp(var, "attachfmt")) {
01042       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01043    } else if (!strcasecmp(var, "serveremail")) {
01044       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01045    } else if (!strcasecmp(var, "emailbody")) {
01046       vmu->emailbody = ast_strdup(substitute_escapes(value));
01047    } else if (!strcasecmp(var, "emailsubject")) {
01048       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01049    } else if (!strcasecmp(var, "language")) {
01050       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01051    } else if (!strcasecmp(var, "tz")) {
01052       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01053    } else if (!strcasecmp(var, "locale")) {
01054       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01055 #ifdef IMAP_STORAGE
01056    } else if (!strcasecmp(var, "imapuser")) {
01057       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01058       vmu->imapversion = imapversion;
01059    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01060       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01061       vmu->imapversion = imapversion;
01062    } else if (!strcasecmp(var, "imapfolder")) {
01063       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01064    } else if (!strcasecmp(var, "imapvmshareid")) {
01065       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01066       vmu->imapversion = imapversion;
01067 #endif
01068    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01069       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01070    } else if (!strcasecmp(var, "saycid")){
01071       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01072    } else if (!strcasecmp(var, "sendvoicemail")){
01073       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01074    } else if (!strcasecmp(var, "review")){
01075       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01076    } else if (!strcasecmp(var, "tempgreetwarn")){
01077       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01078    } else if (!strcasecmp(var, "messagewrap")){
01079       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01080    } else if (!strcasecmp(var, "operator")) {
01081       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01082    } else if (!strcasecmp(var, "envelope")){
01083       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01084    } else if (!strcasecmp(var, "moveheard")){
01085       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01086    } else if (!strcasecmp(var, "sayduration")){
01087       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01088    } else if (!strcasecmp(var, "saydurationm")){
01089       if (sscanf(value, "%30d", &x) == 1) {
01090          vmu->saydurationm = x;
01091       } else {
01092          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01093       }
01094    } else if (!strcasecmp(var, "forcename")){
01095       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01096    } else if (!strcasecmp(var, "forcegreetings")){
01097       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01098    } else if (!strcasecmp(var, "callback")) {
01099       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01100    } else if (!strcasecmp(var, "dialout")) {
01101       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01102    } else if (!strcasecmp(var, "exitcontext")) {
01103       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01104    } else if (!strcasecmp(var, "minsecs")) {
01105       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01106          vmu->minsecs = x;
01107       } else {
01108          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01109          vmu->minsecs = vmminsecs;
01110       }
01111    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01112       vmu->maxsecs = atoi(value);
01113       if (vmu->maxsecs <= 0) {
01114          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01115          vmu->maxsecs = vmmaxsecs;
01116       } else {
01117          vmu->maxsecs = atoi(value);
01118       }
01119       if (!strcasecmp(var, "maxmessage"))
01120          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01121    } else if (!strcasecmp(var, "maxmsg")) {
01122       vmu->maxmsg = atoi(value);
01123       /* Accept maxmsg=0 (Greetings only voicemail) */
01124       if (vmu->maxmsg < 0) {
01125          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01126          vmu->maxmsg = MAXMSG;
01127       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01128          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01129          vmu->maxmsg = MAXMSGLIMIT;
01130       }
01131    } else if (!strcasecmp(var, "nextaftercmd")) {
01132       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01133    } else if (!strcasecmp(var, "backupdeleted")) {
01134       if (sscanf(value, "%30d", &x) == 1)
01135          vmu->maxdeletedmsg = x;
01136       else if (ast_true(value))
01137          vmu->maxdeletedmsg = MAXMSG;
01138       else
01139          vmu->maxdeletedmsg = 0;
01140 
01141       if (vmu->maxdeletedmsg < 0) {
01142          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01143          vmu->maxdeletedmsg = MAXMSG;
01144       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01145          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01146          vmu->maxdeletedmsg = MAXMSGLIMIT;
01147       }
01148    } else if (!strcasecmp(var, "volgain")) {
01149       sscanf(value, "%30lf", &vmu->volgain);
01150    } else if (!strcasecmp(var, "passwordlocation")) {
01151       if (!strcasecmp(value, "spooldir")) {
01152          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01153       } else {
01154          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01155       }
01156    } else if (!strcasecmp(var, "options")) {
01157       apply_options(vmu, value);
01158    }
01159 }
01160 
01161 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01162 {
01163    int fds[2], pid = 0;
01164 
01165    memset(buf, 0, len);
01166 
01167    if (pipe(fds)) {
01168       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01169    } else {
01170       /* good to go*/
01171       pid = ast_safe_fork(0);
01172 
01173       if (pid < 0) {
01174          /* ok maybe not */
01175          close(fds[0]);
01176          close(fds[1]);
01177          snprintf(buf, len, "FAILURE: Fork failed");
01178       } else if (pid) {
01179          /* parent */
01180          close(fds[1]);
01181          if (read(fds[0], buf, len) < 0) {
01182             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01183          }
01184          close(fds[0]);
01185       } else {
01186          /*  child */
01187          AST_DECLARE_APP_ARGS(arg,
01188             AST_APP_ARG(v)[20];
01189          );
01190          char *mycmd = ast_strdupa(command);
01191 
01192          close(fds[0]);
01193          dup2(fds[1], STDOUT_FILENO);
01194          close(fds[1]);
01195          ast_close_fds_above_n(STDOUT_FILENO);
01196 
01197          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01198 
01199          execv(arg.v[0], arg.v); 
01200          printf("FAILURE: %s", strerror(errno));
01201          _exit(0);
01202       }
01203    }
01204    return buf;
01205 }
01206 
01207 /*!
01208  * \brief Check that password meets minimum required length
01209  * \param vmu The voicemail user to change the password for.
01210  * \param password The password string to check
01211  *
01212  * \return zero on ok, 1 on not ok.
01213  */
01214 static int check_password(struct ast_vm_user *vmu, char *password)
01215 {
01216    /* check minimum length */
01217    if (strlen(password) < minpassword)
01218       return 1;
01219    /* check that password does not contain '*' character */
01220    if (!ast_strlen_zero(password) && password[0] == '*')
01221       return 1;
01222    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01223       char cmd[255], buf[255];
01224 
01225       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01226 
01227       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01228       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01229          ast_debug(5, "Result: %s\n", buf);
01230          if (!strncasecmp(buf, "VALID", 5)) {
01231             ast_debug(3, "Passed password check: '%s'\n", buf);
01232             return 0;
01233          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01234             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01235             return 0;
01236          } else {
01237             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01238             return 1;
01239          }
01240       }
01241    }
01242    return 0;
01243 }
01244 
01245 /*! 
01246  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01247  * \param vmu The voicemail user to change the password for.
01248  * \param password The new value to be set to the password for this user.
01249  * 
01250  * This only works if there is a realtime engine configured.
01251  * This is called from the (top level) vm_change_password.
01252  *
01253  * \return zero on success, -1 on error.
01254  */
01255 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01256 {
01257    int res = -1;
01258    if (!strcmp(vmu->password, password)) {
01259       /* No change (but an update would return 0 rows updated, so we opt out here) */
01260       return 0;
01261    }
01262 
01263    if (strlen(password) > 10) {
01264       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01265    }
01266    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01267       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01268       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01269       res = 0;
01270    }
01271    return res;
01272 }
01273 
01274 /*!
01275  * \brief Destructively Parse options and apply.
01276  */
01277 static void apply_options(struct ast_vm_user *vmu, const char *options)
01278 {  
01279    char *stringp;
01280    char *s;
01281    char *var, *value;
01282    stringp = ast_strdupa(options);
01283    while ((s = strsep(&stringp, "|"))) {
01284       value = s;
01285       if ((var = strsep(&value, "=")) && value) {
01286          apply_option(vmu, var, value);
01287       }
01288    }  
01289 }
01290 
01291 /*!
01292  * \brief Loads the options specific to a voicemail user.
01293  * 
01294  * This is called when a vm_user structure is being set up, such as from load_options.
01295  */
01296 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01297 {
01298    for (; var; var = var->next) {
01299       if (!strcasecmp(var->name, "vmsecret")) {
01300          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01301       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01302          if (ast_strlen_zero(retval->password)) {
01303             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01304                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01305                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01306             } else {
01307                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01308             }
01309          }
01310       } else if (!strcasecmp(var->name, "uniqueid")) {
01311          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01312       } else if (!strcasecmp(var->name, "pager")) {
01313          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01314       } else if (!strcasecmp(var->name, "email")) {
01315          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01316       } else if (!strcasecmp(var->name, "fullname")) {
01317          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01318       } else if (!strcasecmp(var->name, "context")) {
01319          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01320       } else if (!strcasecmp(var->name, "emailsubject")) {
01321          ast_free(retval->emailsubject);
01322          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01323       } else if (!strcasecmp(var->name, "emailbody")) {
01324          ast_free(retval->emailbody);
01325          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01326 #ifdef IMAP_STORAGE
01327       } else if (!strcasecmp(var->name, "imapuser")) {
01328          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01329          retval->imapversion = imapversion;
01330       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01331          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01332          retval->imapversion = imapversion;
01333       } else if (!strcasecmp(var->name, "imapfolder")) {
01334          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01335       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01336          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01337          retval->imapversion = imapversion;
01338 #endif
01339       } else
01340          apply_option(retval, var->name, var->value);
01341    }
01342 }
01343 
01344 /*!
01345  * \brief Determines if a DTMF key entered is valid.
01346  * \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.
01347  *
01348  * Tests the character entered against the set of valid DTMF characters. 
01349  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01350  */
01351 static int is_valid_dtmf(const char *key)
01352 {
01353    int i;
01354    char *local_key = ast_strdupa(key);
01355 
01356    for (i = 0; i < strlen(key); ++i) {
01357       if (!strchr(VALID_DTMF, *local_key)) {
01358          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01359          return 0;
01360       }
01361       local_key++;
01362    }
01363    return 1;
01364 }
01365 
01366 /*!
01367  * \brief Finds a voicemail user from the realtime engine.
01368  * \param ivm
01369  * \param context
01370  * \param mailbox
01371  *
01372  * 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.
01373  *
01374  * \return The ast_vm_user structure for the user that was found.
01375  */
01376 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01377 {
01378    struct ast_variable *var;
01379    struct ast_vm_user *retval;
01380 
01381    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01382       if (ivm) {
01383          memset(retval, 0, sizeof(*retval));
01384       }
01385       populate_defaults(retval);
01386       if (!ivm) {
01387          ast_set_flag(retval, VM_ALLOCED);
01388       }
01389       if (mailbox) {
01390          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01391       }
01392       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01393          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01394       } else {
01395          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01396       }
01397       if (var) {
01398          apply_options_full(retval, var);
01399          ast_variables_destroy(var);
01400       } else { 
01401          if (!ivm) 
01402             free_user(retval);
01403          retval = NULL;
01404       }  
01405    } 
01406    return retval;
01407 }
01408 
01409 /*!
01410  * \brief Finds a voicemail user from the users file or the realtime engine.
01411  * \param ivm
01412  * \param context
01413  * \param mailbox
01414  * 
01415  * \return The ast_vm_user structure for the user that was found.
01416  */
01417 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01418 {
01419    /* This function could be made to generate one from a database, too */
01420    struct ast_vm_user *vmu = NULL, *cur;
01421    AST_LIST_LOCK(&users);
01422 
01423    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01424       context = "default";
01425 
01426    AST_LIST_TRAVERSE(&users, cur, list) {
01427 #ifdef IMAP_STORAGE
01428       if (cur->imapversion != imapversion) {
01429          continue;
01430       }
01431 #endif
01432       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01433          break;
01434       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01435          break;
01436    }
01437    if (cur) {
01438       /* Make a copy, so that on a reload, we have no race */
01439       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01440          *vmu = *cur;
01441          if (!ivm) {
01442             vmu->emailbody = ast_strdup(cur->emailbody);
01443             vmu->emailsubject = ast_strdup(cur->emailsubject);
01444          }
01445          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01446          AST_LIST_NEXT(vmu, list) = NULL;
01447       }
01448    } else
01449       vmu = find_user_realtime(ivm, context, mailbox);
01450    AST_LIST_UNLOCK(&users);
01451    return vmu;
01452 }
01453 
01454 /*!
01455  * \brief Resets a user password to a specified password.
01456  * \param context
01457  * \param mailbox
01458  * \param newpass
01459  *
01460  * This does the actual change password work, called by the vm_change_password() function.
01461  *
01462  * \return zero on success, -1 on error.
01463  */
01464 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01465 {
01466    /* This function could be made to generate one from a database, too */
01467    struct ast_vm_user *cur;
01468    int res = -1;
01469    AST_LIST_LOCK(&users);
01470    AST_LIST_TRAVERSE(&users, cur, list) {
01471       if ((!context || !strcasecmp(context, cur->context)) &&
01472          (!strcasecmp(mailbox, cur->mailbox)))
01473             break;
01474    }
01475    if (cur) {
01476       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01477       res = 0;
01478    }
01479    AST_LIST_UNLOCK(&users);
01480    return res;
01481 }
01482 
01483 /*! 
01484  * \brief The handler for the change password option.
01485  * \param vmu The voicemail user to work with.
01486  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01487  * 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.
01488  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01489  */
01490 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01491 {
01492    struct ast_config   *cfg = NULL;
01493    struct ast_variable *var = NULL;
01494    struct ast_category *cat = NULL;
01495    char *category = NULL, *value = NULL, *new = NULL;
01496    const char *tmp = NULL;
01497    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01498    char secretfn[PATH_MAX] = "";
01499    int found = 0;
01500 
01501    if (!change_password_realtime(vmu, newpassword))
01502       return;
01503 
01504    /* check if we should store the secret in the spool directory next to the messages */
01505    switch (vmu->passwordlocation) {
01506    case OPT_PWLOC_SPOOLDIR:
01507       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01508       if (write_password_to_file(secretfn, newpassword) == 0) {
01509          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01510          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01511          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01512          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01513          break;
01514       } else {
01515          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01516       }
01517       /* Fall-through */
01518    case OPT_PWLOC_VOICEMAILCONF:
01519       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01520          while ((category = ast_category_browse(cfg, category))) {
01521             if (!strcasecmp(category, vmu->context)) {
01522                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01523                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01524                   break;
01525                }
01526                value = strstr(tmp, ",");
01527                if (!value) {
01528                   new = ast_alloca(strlen(newpassword)+1);
01529                   sprintf(new, "%s", newpassword);
01530                } else {
01531                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01532                   sprintf(new, "%s%s", newpassword, value);
01533                }
01534                if (!(cat = ast_category_get(cfg, category))) {
01535                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01536                   break;
01537                }
01538                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01539                found = 1;
01540             }
01541          }
01542          /* save the results */
01543          if (found) {
01544             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01545             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01546             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01547             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01548             break;
01549          }
01550       }
01551       /* Fall-through */
01552    case OPT_PWLOC_USERSCONF:
01553       /* check users.conf and update the password stored for the mailbox */
01554       /* if no vmsecret entry exists create one. */
01555       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01556          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01557          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01558             ast_debug(4, "users.conf: %s\n", category);
01559             if (!strcasecmp(category, vmu->mailbox)) {
01560                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01561                   ast_debug(3, "looks like we need to make vmsecret!\n");
01562                   var = ast_variable_new("vmsecret", newpassword, "");
01563                } else {
01564                   var = NULL;
01565                }
01566                new = ast_alloca(strlen(newpassword) + 1);
01567                sprintf(new, "%s", newpassword);
01568                if (!(cat = ast_category_get(cfg, category))) {
01569                   ast_debug(4, "failed to get category!\n");
01570                   ast_free(var);
01571                   break;
01572                }
01573                if (!var) {
01574                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01575                } else {
01576                   ast_variable_append(cat, var);
01577                }
01578                found = 1;
01579                break;
01580             }
01581          }
01582          /* save the results and clean things up */
01583          if (found) {
01584             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01585             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01586             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01587             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01588          }
01589       }
01590    }
01591 }
01592 
01593 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01594 {
01595    char buf[255];
01596    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01597    ast_debug(1, "External password: %s\n",buf);
01598    if (!ast_safe_system(buf)) {
01599       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01600       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01601       /* Reset the password in memory, too */
01602       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01603    }
01604 }
01605 
01606 /*! 
01607  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01608  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01609  * \param len The length of the path string that was written out.
01610  * \param context
01611  * \param ext 
01612  * \param folder 
01613  * 
01614  * The path is constructed as 
01615  *    VM_SPOOL_DIRcontext/ext/folder
01616  *
01617  * \return zero on success, -1 on error.
01618  */
01619 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01620 {
01621    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01622 }
01623 
01624 /*! 
01625  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01626  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01627  * \param len The length of the path string that was written out.
01628  * \param dir 
01629  * \param num 
01630  * 
01631  * The path is constructed as 
01632  *    VM_SPOOL_DIRcontext/ext/folder
01633  *
01634  * \return zero on success, -1 on error.
01635  */
01636 static int make_file(char *dest, const int len, const char *dir, const int num)
01637 {
01638    return snprintf(dest, len, "%s/msg%04d", dir, num);
01639 }
01640 
01641 /* same as mkstemp, but return a FILE * */
01642 static FILE *vm_mkftemp(char *template)
01643 {
01644    FILE *p = NULL;
01645    int pfd = mkstemp(template);
01646    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01647    if (pfd > -1) {
01648       p = fdopen(pfd, "w+");
01649       if (!p) {
01650          close(pfd);
01651          pfd = -1;
01652       }
01653    }
01654    return p;
01655 }
01656 
01657 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01658  * \param dest    String. base directory.
01659  * \param len     Length of dest.
01660  * \param context String. Ignored if is null or empty string.
01661  * \param ext     String. Ignored if is null or empty string.
01662  * \param folder  String. Ignored if is null or empty string. 
01663  * \return -1 on failure, 0 on success.
01664  */
01665 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01666 {
01667    mode_t   mode = VOICEMAIL_DIR_MODE;
01668    int res;
01669 
01670    make_dir(dest, len, context, ext, folder);
01671    if ((res = ast_mkdir(dest, mode))) {
01672       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01673       return -1;
01674    }
01675    return 0;
01676 }
01677 
01678 static const char * const mailbox_folders[] = {
01679 #ifdef IMAP_STORAGE
01680    imapfolder,
01681 #else
01682    "INBOX",
01683 #endif
01684    "Old",
01685    "Work",
01686    "Family",
01687    "Friends",
01688    "Cust1",
01689    "Cust2",
01690    "Cust3",
01691    "Cust4",
01692    "Cust5",
01693    "Deleted",
01694    "Urgent",
01695 };
01696 
01697 static const char *mbox(struct ast_vm_user *vmu, int id)
01698 {
01699 #ifdef IMAP_STORAGE
01700    if (vmu && id == 0) {
01701       return vmu->imapfolder;
01702    }
01703 #endif
01704    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01705 }
01706 
01707 static int get_folder_by_name(const char *name)
01708 {
01709    size_t i;
01710 
01711    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01712       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01713          return i;
01714       }
01715    }
01716 
01717    return -1;
01718 }
01719 
01720 static void free_user(struct ast_vm_user *vmu)
01721 {
01722    if (ast_test_flag(vmu, VM_ALLOCED)) {
01723 
01724       ast_free(vmu->emailbody);
01725       vmu->emailbody = NULL;
01726 
01727       ast_free(vmu->emailsubject);
01728       vmu->emailsubject = NULL;
01729 
01730       ast_free(vmu);
01731    }
01732 }
01733 
01734 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01735 
01736    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01737 
01738    /* remove old allocation */
01739    if (vms->deleted) {
01740       ast_free(vms->deleted);
01741       vms->deleted = NULL;
01742    }
01743    if (vms->heard) {
01744       ast_free(vms->heard);
01745       vms->heard = NULL;
01746    }
01747    vms->dh_arraysize = 0;
01748 
01749    if (arraysize > 0) {
01750       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01751          return -1;
01752       }
01753       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01754          ast_free(vms->deleted);
01755          vms->deleted = NULL;
01756          return -1;
01757       }
01758       vms->dh_arraysize = arraysize;
01759    }
01760 
01761    return 0;
01762 }
01763 
01764 /* All IMAP-specific functions should go in this block. This
01765  * keeps them from being spread out all over the code */
01766 #ifdef IMAP_STORAGE
01767 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01768 {
01769    char arg[10];
01770    struct vm_state *vms;
01771    unsigned long messageNum;
01772 
01773    /* If greetings aren't stored in IMAP, just delete the file */
01774    if (msgnum < 0 && !imapgreetings) {
01775       ast_filedelete(file, NULL);
01776       return;
01777    }
01778 
01779    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01780       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);
01781       return;
01782    }
01783 
01784    if (msgnum < 0) {
01785       imap_delete_old_greeting(file, vms);
01786       return;
01787    }
01788 
01789    /* find real message number based on msgnum */
01790    /* this may be an index into vms->msgArray based on the msgnum. */
01791    messageNum = vms->msgArray[msgnum];
01792    if (messageNum == 0) {
01793       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01794       return;
01795    }
01796    if (option_debug > 2)
01797       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01798    /* delete message */
01799    snprintf (arg, sizeof(arg), "%lu", messageNum);
01800    ast_mutex_lock(&vms->lock);
01801    mail_setflag (vms->mailstream, arg, "\\DELETED");
01802    mail_expunge(vms->mailstream);
01803    ast_mutex_unlock(&vms->lock);
01804 }
01805 
01806 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01807 {
01808    struct vm_state *vms_p;
01809    char *file, *filename;
01810    char *attachment;
01811    int i;
01812    BODY *body;
01813 
01814    /* This function is only used for retrieval of IMAP greetings
01815     * regular messages are not retrieved this way, nor are greetings
01816     * if they are stored locally*/
01817    if (msgnum > -1 || !imapgreetings) {
01818       return 0;
01819    } else {
01820       file = strrchr(ast_strdupa(dir), '/');
01821       if (file)
01822          *file++ = '\0';
01823       else {
01824          ast_debug (1, "Failed to procure file name from directory passed.\n");
01825          return -1;
01826       }
01827    }
01828 
01829    /* check if someone is accessing this box right now... */
01830    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01831       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01832       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01833       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01834       * that's all we need to do.
01835       */
01836       if (!(vms_p = create_vm_state_from_user(vmu))) {
01837          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01838          return -1;
01839       }
01840    }
01841 
01842    /* Greetings will never have a prepended message */
01843    *vms_p->introfn = '\0';
01844 
01845    ast_mutex_lock(&vms_p->lock);
01846    init_mailstream(vms_p, GREETINGS_FOLDER);
01847    if (!vms_p->mailstream) {
01848       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01849       ast_mutex_unlock(&vms_p->lock);
01850       return -1;
01851    }
01852 
01853    /*XXX Yuck, this could probably be done a lot better */
01854    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01855       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01856       /* We have the body, now we extract the file name of the first attachment. */
01857       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01858          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01859       } else {
01860          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01861          ast_mutex_unlock(&vms_p->lock);
01862          return -1;
01863       }
01864       filename = strsep(&attachment, ".");
01865       if (!strcmp(filename, file)) {
01866          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01867          vms_p->msgArray[vms_p->curmsg] = i + 1;
01868          save_body(body, vms_p, "2", attachment, 0);
01869          ast_mutex_unlock(&vms_p->lock);
01870          return 0;
01871       }
01872    }
01873    ast_mutex_unlock(&vms_p->lock);
01874 
01875    return -1;
01876 }
01877 
01878 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01879 {
01880    BODY *body;
01881    char *header_content;
01882    char *attachedfilefmt;
01883    char buf[80];
01884    struct vm_state *vms;
01885    char text_file[PATH_MAX];
01886    FILE *text_file_ptr;
01887    int res = 0;
01888    struct ast_vm_user *vmu;
01889 
01890    if (!(vmu = find_user(NULL, context, mailbox))) {
01891       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01892       return -1;
01893    }
01894    
01895    if (msgnum < 0) {
01896       if (imapgreetings) {
01897          res = imap_retrieve_greeting(dir, msgnum, vmu);
01898          goto exit;
01899       } else {
01900          res = 0;
01901          goto exit;
01902       }
01903    }
01904 
01905    /* Before anything can happen, we need a vm_state so that we can
01906     * actually access the imap server through the vms->mailstream
01907     */
01908    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01909       /* This should not happen. If it does, then I guess we'd
01910        * need to create the vm_state, extract which mailbox to
01911        * open, and then set up the msgArray so that the correct
01912        * IMAP message could be accessed. If I have seen correctly
01913        * though, the vms should be obtainable from the vmstates list
01914        * and should have its msgArray properly set up.
01915        */
01916       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01917       res = -1;
01918       goto exit;
01919    }
01920    
01921    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01922    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01923 
01924    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01925    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01926       res = 0;
01927       goto exit;
01928    }
01929 
01930    if (option_debug > 2)
01931       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01932    if (vms->msgArray[msgnum] == 0) {
01933       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01934       res = -1;
01935       goto exit;
01936    }
01937 
01938    /* This will only work for new messages... */
01939    ast_mutex_lock(&vms->lock);
01940    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01941    ast_mutex_unlock(&vms->lock);
01942    /* empty string means no valid header */
01943    if (ast_strlen_zero(header_content)) {
01944       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01945       res = -1;
01946       goto exit;
01947    }
01948 
01949    ast_mutex_lock(&vms->lock);
01950    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01951    ast_mutex_unlock(&vms->lock);
01952 
01953    /* We have the body, now we extract the file name of the first attachment. */
01954    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01955       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01956    } else {
01957       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01958       res = -1;
01959       goto exit;
01960    }
01961    
01962    /* Find the format of the attached file */
01963 
01964    strsep(&attachedfilefmt, ".");
01965    if (!attachedfilefmt) {
01966       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01967       res = -1;
01968       goto exit;
01969    }
01970    
01971    save_body(body, vms, "2", attachedfilefmt, 0);
01972    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01973       *vms->introfn = '\0';
01974    }
01975 
01976    /* Get info from headers!! */
01977    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01978 
01979    if (!(text_file_ptr = fopen(text_file, "w"))) {
01980       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01981    }
01982 
01983    fprintf(text_file_ptr, "%s\n", "[message]");
01984 
01985    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01986    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01987    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01988    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01989    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01990    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01991    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01992    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01993    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01994    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01995    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01996    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01997    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01998    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01999    fclose(text_file_ptr);
02000 
02001 exit:
02002    free_user(vmu);
02003    return res;
02004 }
02005 
02006 static int folder_int(const char *folder)
02007 {
02008    /*assume a NULL folder means INBOX*/
02009    if (!folder) {
02010       return 0;
02011    }
02012    if (!strcasecmp(folder, imapfolder)) {
02013       return 0;
02014    } else if (!strcasecmp(folder, "Old")) {
02015       return 1;
02016    } else if (!strcasecmp(folder, "Work")) {
02017       return 2;
02018    } else if (!strcasecmp(folder, "Family")) {
02019       return 3;
02020    } else if (!strcasecmp(folder, "Friends")) {
02021       return 4;
02022    } else if (!strcasecmp(folder, "Cust1")) {
02023       return 5;
02024    } else if (!strcasecmp(folder, "Cust2")) {
02025       return 6;
02026    } else if (!strcasecmp(folder, "Cust3")) {
02027       return 7;
02028    } else if (!strcasecmp(folder, "Cust4")) {
02029       return 8;
02030    } else if (!strcasecmp(folder, "Cust5")) {
02031       return 9;
02032    } else if (!strcasecmp(folder, "Urgent")) {
02033       return 11;
02034    } else { /*assume they meant INBOX if folder is not found otherwise*/
02035       return 0;
02036    }
02037 }
02038 
02039 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02040 {
02041    SEARCHPGM *pgm;
02042    SEARCHHEADER *hdr;
02043 
02044    struct ast_vm_user *vmu, vmus;
02045    struct vm_state *vms_p;
02046    int ret = 0;
02047    int fold = folder_int(folder);
02048    int urgent = 0;
02049    
02050    /* If URGENT, then look at INBOX */
02051    if (fold == 11) {
02052       fold = NEW_FOLDER;
02053       urgent = 1;
02054    }
02055 
02056    if (ast_strlen_zero(mailbox))
02057       return 0;
02058 
02059    /* We have to get the user before we can open the stream! */
02060    vmu = find_user(&vmus, context, mailbox);
02061    if (!vmu) {
02062       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02063       return -1;
02064    } else {
02065       /* No IMAP account available */
02066       if (vmu->imapuser[0] == '\0') {
02067          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02068          return -1;
02069       }
02070    }
02071    
02072    /* No IMAP account available */
02073    if (vmu->imapuser[0] == '\0') {
02074       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02075       free_user(vmu);
02076       return -1;
02077    }
02078 
02079    /* check if someone is accessing this box right now... */
02080    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02081    if (!vms_p) {
02082       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02083    }
02084    if (vms_p) {
02085       ast_debug(3, "Returning before search - user is logged in\n");
02086       if (fold == 0) { /* INBOX */
02087          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02088       }
02089       if (fold == 1) { /* Old messages */
02090          return vms_p->oldmessages;
02091       }
02092    }
02093 
02094    /* add one if not there... */
02095    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02096    if (!vms_p) {
02097       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02098    }
02099 
02100    if (!vms_p) {
02101       vms_p = create_vm_state_from_user(vmu);
02102    }
02103    ret = init_mailstream(vms_p, fold);
02104    if (!vms_p->mailstream) {
02105       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02106       return -1;
02107    }
02108    if (ret == 0) {
02109       ast_mutex_lock(&vms_p->lock);
02110       pgm = mail_newsearchpgm ();
02111       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02112       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02113       pgm->header = hdr;
02114       if (fold != OLD_FOLDER) {
02115          pgm->unseen = 1;
02116          pgm->seen = 0;
02117       }
02118       /* In the special case where fold is 1 (old messages) we have to do things a bit
02119        * differently. Old messages are stored in the INBOX but are marked as "seen"
02120        */
02121       else {
02122          pgm->unseen = 0;
02123          pgm->seen = 1;
02124       }
02125       /* look for urgent messages */
02126       if (fold == NEW_FOLDER) {
02127          if (urgent) {
02128             pgm->flagged = 1;
02129             pgm->unflagged = 0;
02130          } else {
02131             pgm->flagged = 0;
02132             pgm->unflagged = 1;
02133          }
02134       }
02135       pgm->undeleted = 1;
02136       pgm->deleted = 0;
02137 
02138       vms_p->vmArrayIndex = 0;
02139       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02140       if (fold == 0 && urgent == 0)
02141          vms_p->newmessages = vms_p->vmArrayIndex;
02142       if (fold == 1)
02143          vms_p->oldmessages = vms_p->vmArrayIndex;
02144       if (fold == 0 && urgent == 1)
02145          vms_p->urgentmessages = vms_p->vmArrayIndex;
02146       /*Freeing the searchpgm also frees the searchhdr*/
02147       mail_free_searchpgm(&pgm);
02148       ast_mutex_unlock(&vms_p->lock);
02149       vms_p->updated = 0;
02150       return vms_p->vmArrayIndex;
02151    } else {
02152       ast_mutex_lock(&vms_p->lock);
02153       mail_ping(vms_p->mailstream);
02154       ast_mutex_unlock(&vms_p->lock);
02155    }
02156    return 0;
02157 }
02158 
02159 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02160 {
02161    /* Check if mailbox is full */
02162    check_quota(vms, vmu->imapfolder);
02163    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02164       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02165       ast_play_and_wait(chan, "vm-mailboxfull");
02166       return -1;
02167    }
02168    
02169    /* Check if we have exceeded maxmsg */
02170    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));
02171    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02172       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02173       ast_play_and_wait(chan, "vm-mailboxfull");
02174       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02175       return -1;
02176    }
02177 
02178    return 0;
02179 }
02180 
02181 /*!
02182  * \brief Gets the number of messages that exist in a mailbox folder.
02183  * \param context
02184  * \param mailbox
02185  * \param folder
02186  * 
02187  * This method is used when IMAP backend is used.
02188  * \return The number of messages in this mailbox folder (zero or more).
02189  */
02190 static int messagecount(const char *context, const char *mailbox, const char *folder)
02191 {
02192    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02193       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02194    } else {
02195       return __messagecount(context, mailbox, folder);
02196    }
02197 }
02198 
02199 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)
02200 {
02201    char *myserveremail = serveremail;
02202    char fn[PATH_MAX];
02203    char introfn[PATH_MAX];
02204    char mailbox[256];
02205    char *stringp;
02206    FILE *p = NULL;
02207    char tmp[80] = "/tmp/astmail-XXXXXX";
02208    long len;
02209    void *buf;
02210    int tempcopy = 0;
02211    STRING str;
02212    int ret; /* for better error checking */
02213    char *imap_flags = NIL;
02214    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02215    int box = NEW_FOLDER;
02216 
02217    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02218    if (msgnum < 0) {
02219       if(!imapgreetings) {
02220          return 0;
02221       } else {
02222          box = GREETINGS_FOLDER;
02223       }
02224    }
02225    
02226    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02227       return -1;
02228    }
02229 
02230    /* Set urgent flag for IMAP message */
02231    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02232       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02233       imap_flags = "\\FLAGGED";
02234    }
02235    
02236    /* Attach only the first format */
02237    fmt = ast_strdupa(fmt);
02238    stringp = fmt;
02239    strsep(&stringp, "|");
02240 
02241    if (!ast_strlen_zero(vmu->serveremail))
02242       myserveremail = vmu->serveremail;
02243 
02244    if (msgnum > -1)
02245       make_file(fn, sizeof(fn), dir, msgnum);
02246    else
02247       ast_copy_string (fn, dir, sizeof(fn));
02248 
02249    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02250    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02251       *introfn = '\0';
02252    }
02253    
02254    if (ast_strlen_zero(vmu->email)) {
02255       /* We need the vmu->email to be set when we call make_email_file, but
02256        * if we keep it set, a duplicate e-mail will be created. So at the end
02257        * of this function, we will revert back to an empty string if tempcopy
02258        * is 1.
02259        */
02260       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02261       tempcopy = 1;
02262    }
02263 
02264    if (!strcmp(fmt, "wav49"))
02265       fmt = "WAV";
02266    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02267 
02268    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02269       command hangs. */
02270    if (!(p = vm_mkftemp(tmp))) {
02271       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02272       if (tempcopy)
02273          *(vmu->email) = '\0';
02274       return -1;
02275    }
02276 
02277    if (msgnum < 0 && imapgreetings) {
02278       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02279          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02280          return -1;
02281       }
02282       imap_delete_old_greeting(fn, vms);
02283    }
02284 
02285    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02286       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02287       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02288       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02289    /* read mail file to memory */
02290    len = ftell(p);
02291    rewind(p);
02292    if (!(buf = ast_malloc(len + 1))) {
02293       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02294       fclose(p);
02295       if (tempcopy)
02296          *(vmu->email) = '\0';
02297       return -1;
02298    }
02299    if (fread(buf, len, 1, p) < len) {
02300       if (ferror(p)) {
02301          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02302          return -1;
02303       }
02304    }
02305    ((char *) buf)[len] = '\0';
02306    INIT(&str, mail_string, buf, len);
02307    ret = init_mailstream(vms, box);
02308    if (ret == 0) {
02309       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02310       ast_mutex_lock(&vms->lock);
02311       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02312          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02313       ast_mutex_unlock(&vms->lock);
02314       fclose(p);
02315       unlink(tmp);
02316       ast_free(buf);
02317    } else {
02318       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02319       fclose(p);
02320       unlink(tmp);
02321       ast_free(buf);
02322       return -1;
02323    }
02324    ast_debug(3, "%s stored\n", fn);
02325    
02326    if (tempcopy)
02327       *(vmu->email) = '\0';
02328    inprocess_count(vmu->mailbox, vmu->context, -1);
02329    return 0;
02330 
02331 }
02332 
02333 /*!
02334  * \brief Gets the number of messages that exist in the inbox folder.
02335  * \param mailbox_context
02336  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02337  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02338  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02339  * 
02340  * This method is used when IMAP backend is used.
02341  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02342  *
02343  * \return zero on success, -1 on error.
02344  */
02345 
02346 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02347 {
02348    char tmp[PATH_MAX] = "";
02349    char *mailboxnc;
02350    char *context;
02351    char *mb;
02352    char *cur;
02353    if (newmsgs)
02354       *newmsgs = 0;
02355    if (oldmsgs)
02356       *oldmsgs = 0;
02357    if (urgentmsgs)
02358       *urgentmsgs = 0;
02359 
02360    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02361    /* If no mailbox, return immediately */
02362    if (ast_strlen_zero(mailbox_context))
02363       return 0;
02364    
02365    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02366    context = strchr(tmp, '@');
02367    if (strchr(mailbox_context, ',')) {
02368       int tmpnew, tmpold, tmpurgent;
02369       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02370       mb = tmp;
02371       while ((cur = strsep(&mb, ", "))) {
02372          if (!ast_strlen_zero(cur)) {
02373             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02374                return -1;
02375             else {
02376                if (newmsgs)
02377                   *newmsgs += tmpnew; 
02378                if (oldmsgs)
02379                   *oldmsgs += tmpold;
02380                if (urgentmsgs)
02381                   *urgentmsgs += tmpurgent;
02382             }
02383          }
02384       }
02385       return 0;
02386    }
02387    if (context) {
02388       *context = '\0';
02389       mailboxnc = tmp;
02390       context++;
02391    } else {
02392       context = "default";
02393       mailboxnc = (char *) mailbox_context;
02394    }
02395 
02396    if (newmsgs) {
02397       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02398       if (!vmu) {
02399          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02400          return -1;
02401       }
02402       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02403          free_user(vmu);
02404          return -1;
02405       }
02406       free_user(vmu);
02407    }
02408    if (oldmsgs) {
02409       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02410          return -1;
02411       }
02412    }
02413    if (urgentmsgs) {
02414       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02415          return -1;
02416       }
02417    }
02418    return 0;
02419 }
02420 
02421 /** 
02422  * \brief Determines if the given folder has messages.
02423  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02424  * \param folder the folder to look in
02425  *
02426  * This function is used when the mailbox is stored in an IMAP back end.
02427  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02428  * \return 1 if the folder has one or more messages. zero otherwise.
02429  */
02430 
02431 static int has_voicemail(const char *mailbox, const char *folder)
02432 {
02433    char tmp[256], *tmp2, *box, *context;
02434    ast_copy_string(tmp, mailbox, sizeof(tmp));
02435    tmp2 = tmp;
02436    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02437       while ((box = strsep(&tmp2, ",&"))) {
02438          if (!ast_strlen_zero(box)) {
02439             if (has_voicemail(box, folder)) {
02440                return 1;
02441             }
02442          }
02443       }
02444    }
02445    if ((context = strchr(tmp, '@'))) {
02446       *context++ = '\0';
02447    } else {
02448       context = "default";
02449    }
02450    return __messagecount(context, tmp, folder) ? 1 : 0;
02451 }
02452 
02453 /*!
02454  * \brief Copies a message from one mailbox to another.
02455  * \param chan
02456  * \param vmu
02457  * \param imbox
02458  * \param msgnum
02459  * \param duration
02460  * \param recip
02461  * \param fmt
02462  * \param dir
02463  *
02464  * This works with IMAP storage based mailboxes.
02465  *
02466  * \return zero on success, -1 on error.
02467  */
02468 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)
02469 {
02470    struct vm_state *sendvms = NULL, *destvms = NULL;
02471    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02472    if (msgnum >= recip->maxmsg) {
02473       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02474       return -1;
02475    }
02476    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02477       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02478       return -1;
02479    }
02480    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02481       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02482       return -1;
02483    }
02484    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02485    ast_mutex_lock(&sendvms->lock);
02486    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02487       ast_mutex_unlock(&sendvms->lock);
02488       return 0;
02489    }
02490    ast_mutex_unlock(&sendvms->lock);
02491    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02492    return -1;
02493 }
02494 
02495 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02496 {
02497    char tmp[256], *t = tmp;
02498    size_t left = sizeof(tmp);
02499    
02500    if (box == OLD_FOLDER) {
02501       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02502    } else {
02503       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02504    }
02505 
02506    if (box == NEW_FOLDER) {
02507       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02508    } else {
02509       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02510    }
02511 
02512    /* Build up server information */
02513    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02514 
02515    /* Add authentication user if present */
02516    if (!ast_strlen_zero(authuser))
02517       ast_build_string(&t, &left, "/authuser=%s", authuser);
02518 
02519    /* Add flags if present */
02520    if (!ast_strlen_zero(imapflags))
02521       ast_build_string(&t, &left, "/%s", imapflags);
02522 
02523    /* End with username */
02524 #if 1
02525    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02526 #else
02527    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02528 #endif
02529    if (box == NEW_FOLDER || box == OLD_FOLDER)
02530       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02531    else if (box == GREETINGS_FOLDER)
02532       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02533    else {   /* Other folders such as Friends, Family, etc... */
02534       if (!ast_strlen_zero(imapparentfolder)) {
02535          /* imapparentfolder would typically be set to INBOX */
02536          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02537       } else {
02538          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02539       }
02540    }
02541 }
02542 
02543 static int init_mailstream(struct vm_state *vms, int box)
02544 {
02545    MAILSTREAM *stream = NIL;
02546    long debug;
02547    char tmp[256];
02548    
02549    if (!vms) {
02550       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02551       return -1;
02552    }
02553    if (option_debug > 2)
02554       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02555    if (vms->mailstream == NIL || !vms->mailstream) {
02556       if (option_debug)
02557          ast_log(LOG_DEBUG, "mailstream not set.\n");
02558    } else {
02559       stream = vms->mailstream;
02560    }
02561    /* debug = T;  user wants protocol telemetry? */
02562    debug = NIL;  /* NO protocol telemetry? */
02563 
02564    if (delimiter == '\0') {      /* did not probe the server yet */
02565       char *cp;
02566 #ifdef USE_SYSTEM_IMAP
02567 #include <imap/linkage.c>
02568 #elif defined(USE_SYSTEM_CCLIENT)
02569 #include <c-client/linkage.c>
02570 #else
02571 #include "linkage.c"
02572 #endif
02573       /* Connect to INBOX first to get folders delimiter */
02574       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02575       ast_mutex_lock(&vms->lock);
02576       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02577       ast_mutex_unlock(&vms->lock);
02578       if (stream == NIL) {
02579          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02580          return -1;
02581       }
02582       get_mailbox_delimiter(stream);
02583       /* update delimiter in imapfolder */
02584       for (cp = vms->imapfolder; *cp; cp++)
02585          if (*cp == '/')
02586             *cp = delimiter;
02587    }
02588    /* Now connect to the target folder */
02589    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02590    if (option_debug > 2)
02591       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02592    ast_mutex_lock(&vms->lock);
02593    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02594    ast_mutex_unlock(&vms->lock);
02595    if (vms->mailstream == NIL) {
02596       return -1;
02597    } else {
02598       return 0;
02599    }
02600 }
02601 
02602 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02603 {
02604    SEARCHPGM *pgm;
02605    SEARCHHEADER *hdr;
02606    int ret, urgent = 0;
02607 
02608    /* If Urgent, then look at INBOX */
02609    if (box == 11) {
02610       box = NEW_FOLDER;
02611       urgent = 1;
02612    }
02613 
02614    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02615    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02616    vms->imapversion = vmu->imapversion;
02617    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02618 
02619    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02620       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02621       return -1;
02622    }
02623    
02624    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02625    
02626    /* Check Quota */
02627    if  (box == 0)  {
02628       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02629       check_quota(vms, (char *) mbox(vmu, box));
02630    }
02631 
02632    ast_mutex_lock(&vms->lock);
02633    pgm = mail_newsearchpgm();
02634 
02635    /* Check IMAP folder for Asterisk messages only... */
02636    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02637    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02638    pgm->header = hdr;
02639    pgm->deleted = 0;
02640    pgm->undeleted = 1;
02641 
02642    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02643    if (box == NEW_FOLDER && urgent == 1) {
02644       pgm->unseen = 1;
02645       pgm->seen = 0;
02646       pgm->flagged = 1;
02647       pgm->unflagged = 0;
02648    } else if (box == NEW_FOLDER && urgent == 0) {
02649       pgm->unseen = 1;
02650       pgm->seen = 0;
02651       pgm->flagged = 0;
02652       pgm->unflagged = 1;
02653    } else if (box == OLD_FOLDER) {
02654       pgm->seen = 1;
02655       pgm->unseen = 0;
02656    }
02657 
02658    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02659 
02660    vms->vmArrayIndex = 0;
02661    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02662    vms->lastmsg = vms->vmArrayIndex - 1;
02663    mail_free_searchpgm(&pgm);
02664    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02665     * ensure to allocate enough space to account for all of them. Warn if old messages
02666     * have not been checked first as that is required.
02667     */
02668    if (box == 0 && !vms->dh_arraysize) {
02669       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02670    }
02671    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02672       ast_mutex_unlock(&vms->lock);
02673       return -1;
02674    }
02675 
02676    ast_mutex_unlock(&vms->lock);
02677    return 0;
02678 }
02679 
02680 static void write_file(char *filename, char *buffer, unsigned long len)
02681 {
02682    FILE *output;
02683 
02684    output = fopen (filename, "w");
02685    if (fwrite(buffer, len, 1, output) != 1) {
02686       if (ferror(output)) {
02687          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02688       }
02689    }
02690    fclose (output);
02691 }
02692 
02693 static void update_messages_by_imapuser(const char *user, unsigned long number)
02694 {
02695    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02696 
02697    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02698       return;
02699    }
02700 
02701    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02702    vms->msgArray[vms->vmArrayIndex++] = number;
02703 }
02704 
02705 void mm_searched(MAILSTREAM *stream, unsigned long number)
02706 {
02707    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02708 
02709    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02710       return;
02711 
02712    update_messages_by_imapuser(user, number);
02713 }
02714 
02715 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02716 {
02717    struct ast_variable *var;
02718    struct ast_vm_user *vmu;
02719 
02720    vmu = ast_calloc(1, sizeof *vmu);
02721    if (!vmu)
02722       return NULL;
02723 
02724    populate_defaults(vmu);
02725    ast_set_flag(vmu, VM_ALLOCED);
02726 
02727    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02728    if (var) {
02729       apply_options_full(vmu, var);
02730       ast_variables_destroy(var);
02731       return vmu;
02732    } else {
02733       ast_free(vmu);
02734       return NULL;
02735    }
02736 }
02737 
02738 /* Interfaces to C-client */
02739 
02740 void mm_exists(MAILSTREAM * stream, unsigned long number)
02741 {
02742    /* mail_ping will callback here if new mail! */
02743    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02744    if (number == 0) return;
02745    set_update(stream);
02746 }
02747 
02748 
02749 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02750 {
02751    /* mail_ping will callback here if expunged mail! */
02752    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02753    if (number == 0) return;
02754    set_update(stream);
02755 }
02756 
02757 
02758 void mm_flags(MAILSTREAM * stream, unsigned long number)
02759 {
02760    /* mail_ping will callback here if read mail! */
02761    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02762    if (number == 0) return;
02763    set_update(stream);
02764 }
02765 
02766 
02767 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02768 {
02769    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02770    mm_log (string, errflg);
02771 }
02772 
02773 
02774 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02775 {
02776    if (delimiter == '\0') {
02777       delimiter = delim;
02778    }
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_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02793 {
02794    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02795    if (attributes & LATT_NOINFERIORS)
02796       ast_debug(5, "no inferiors\n");
02797    if (attributes & LATT_NOSELECT)
02798       ast_debug(5, "no select\n");
02799    if (attributes & LATT_MARKED)
02800       ast_debug(5, "marked\n");
02801    if (attributes & LATT_UNMARKED)
02802       ast_debug(5, "unmarked\n");
02803 }
02804 
02805 
02806 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02807 {
02808    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02809    if (status->flags & SA_MESSAGES)
02810       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02811    if (status->flags & SA_RECENT)
02812       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02813    if (status->flags & SA_UNSEEN)
02814       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02815    if (status->flags & SA_UIDVALIDITY)
02816       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02817    if (status->flags & SA_UIDNEXT)
02818       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02819    ast_log(AST_LOG_NOTICE, "\n");
02820 }
02821 
02822 
02823 void mm_log(char *string, long errflg)
02824 {
02825    switch ((short) errflg) {
02826       case NIL:
02827          ast_debug(1, "IMAP Info: %s\n", string);
02828          break;
02829       case PARSE:
02830       case WARN:
02831          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02832          break;
02833       case ERROR:
02834          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02835          break;
02836    }
02837 }
02838 
02839 
02840 void mm_dlog(char *string)
02841 {
02842    ast_log(AST_LOG_NOTICE, "%s\n", string);
02843 }
02844 
02845 
02846 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02847 {
02848    struct ast_vm_user *vmu;
02849 
02850    ast_debug(4, "Entering callback mm_login\n");
02851 
02852    ast_copy_string(user, mb->user, MAILTMPLEN);
02853 
02854    /* We should only do this when necessary */
02855    if (!ast_strlen_zero(authpassword)) {
02856       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02857    } else {
02858       AST_LIST_TRAVERSE(&users, vmu, list) {
02859          if (!strcasecmp(mb->user, vmu->imapuser)) {
02860             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02861             break;
02862          }
02863       }
02864       if (!vmu) {
02865          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02866             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02867             free_user(vmu);
02868          }
02869       }
02870    }
02871 }
02872 
02873 
02874 void mm_critical(MAILSTREAM * stream)
02875 {
02876 }
02877 
02878 
02879 void mm_nocritical(MAILSTREAM * stream)
02880 {
02881 }
02882 
02883 
02884 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02885 {
02886    kill (getpid (), SIGSTOP);
02887    return NIL;
02888 }
02889 
02890 
02891 void mm_fatal(char *string)
02892 {
02893    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02894 }
02895 
02896 /* C-client callback to handle quota */
02897 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02898 {
02899    struct vm_state *vms;
02900    char *mailbox = stream->mailbox, *user;
02901    char buf[1024] = "";
02902    unsigned long usage = 0, limit = 0;
02903    
02904    while (pquota) {
02905       usage = pquota->usage;
02906       limit = pquota->limit;
02907       pquota = pquota->next;
02908    }
02909    
02910    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)))) {
02911       ast_log(AST_LOG_ERROR, "No state found.\n");
02912       return;
02913    }
02914 
02915    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02916 
02917    vms->quota_usage = usage;
02918    vms->quota_limit = limit;
02919 }
02920 
02921 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02922 {
02923    char *start, *eol_pnt;
02924    int taglen;
02925 
02926    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02927       return NULL;
02928 
02929    taglen = strlen(tag) + 1;
02930    if (taglen < 1)
02931       return NULL;
02932 
02933    if (!(start = strstr(header, tag)))
02934       return NULL;
02935 
02936    /* Since we can be called multiple times we should clear our buffer */
02937    memset(buf, 0, len);
02938 
02939    ast_copy_string(buf, start+taglen, len);
02940    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02941       *eol_pnt = '\0';
02942    return buf;
02943 }
02944 
02945 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02946 {
02947    char *start, *quote, *eol_pnt;
02948 
02949    if (ast_strlen_zero(mailbox))
02950       return NULL;
02951 
02952    if (!(start = strstr(mailbox, "/user=")))
02953       return NULL;
02954 
02955    ast_copy_string(buf, start+6, len);
02956 
02957    if (!(quote = strchr(buf, '\"'))) {
02958       if (!(eol_pnt = strchr(buf, '/')))
02959          eol_pnt = strchr(buf,'}');
02960       *eol_pnt = '\0';
02961       return buf;
02962    } else {
02963       eol_pnt = strchr(buf+1,'\"');
02964       *eol_pnt = '\0';
02965       return buf+1;
02966    }
02967 }
02968 
02969 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02970 {
02971    struct vm_state *vms_p;
02972 
02973    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02974    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02975       return vms_p;
02976    }
02977    if (option_debug > 4)
02978       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02979    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02980       return NULL;
02981    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02982    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02983    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02984    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02985    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02986    vms_p->imapversion = vmu->imapversion;
02987    if (option_debug > 4)
02988       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02989    vms_p->updated = 1;
02990    /* set mailbox to INBOX! */
02991    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02992    init_vm_state(vms_p);
02993    vmstate_insert(vms_p);
02994    return vms_p;
02995 }
02996 
02997 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02998 {
02999    struct vmstate *vlist = NULL;
03000 
03001    if (interactive) {
03002       struct vm_state *vms;
03003       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03004       vms = pthread_getspecific(ts_vmstate.key);
03005       return vms;
03006    }
03007 
03008    AST_LIST_LOCK(&vmstates);
03009    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03010       if (!vlist->vms) {
03011          ast_debug(3, "error: vms is NULL for %s\n", user);
03012          continue;
03013       }
03014       if (vlist->vms->imapversion != imapversion) {
03015          continue;
03016       }
03017       if (!vlist->vms->imapuser) {
03018          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03019          continue;
03020       }
03021 
03022       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03023          AST_LIST_UNLOCK(&vmstates);
03024          return vlist->vms;
03025       }
03026    }
03027    AST_LIST_UNLOCK(&vmstates);
03028 
03029    ast_debug(3, "%s not found in vmstates\n", user);
03030 
03031    return NULL;
03032 }
03033 
03034 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03035 {
03036 
03037    struct vmstate *vlist = NULL;
03038    const char *local_context = S_OR(context, "default");
03039 
03040    if (interactive) {
03041       struct vm_state *vms;
03042       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03043       vms = pthread_getspecific(ts_vmstate.key);
03044       return vms;
03045    }
03046 
03047    AST_LIST_LOCK(&vmstates);
03048    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03049       if (!vlist->vms) {
03050          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03051          continue;
03052       }
03053       if (vlist->vms->imapversion != imapversion) {
03054          continue;
03055       }
03056       if (!vlist->vms->username || !vlist->vms->context) {
03057          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03058          continue;
03059       }
03060 
03061       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);
03062       
03063       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03064          ast_debug(3, "Found it!\n");
03065          AST_LIST_UNLOCK(&vmstates);
03066          return vlist->vms;
03067       }
03068    }
03069    AST_LIST_UNLOCK(&vmstates);
03070 
03071    ast_debug(3, "%s not found in vmstates\n", mailbox);
03072 
03073    return NULL;
03074 }
03075 
03076 static void vmstate_insert(struct vm_state *vms) 
03077 {
03078    struct vmstate *v;
03079    struct vm_state *altvms;
03080 
03081    /* If interactive, it probably already exists, and we should
03082       use the one we already have since it is more up to date.
03083       We can compare the username to find the duplicate */
03084    if (vms->interactive == 1) {
03085       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03086       if (altvms) {  
03087          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03088          vms->newmessages = altvms->newmessages;
03089          vms->oldmessages = altvms->oldmessages;
03090          vms->vmArrayIndex = altvms->vmArrayIndex;
03091          vms->lastmsg = altvms->lastmsg;
03092          vms->curmsg = altvms->curmsg;
03093          /* get a pointer to the persistent store */
03094          vms->persist_vms = altvms;
03095          /* Reuse the mailstream? */
03096 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03097          vms->mailstream = altvms->mailstream;
03098 #else
03099          vms->mailstream = NIL;
03100 #endif
03101       }
03102       return;
03103    }
03104 
03105    if (!(v = ast_calloc(1, sizeof(*v))))
03106       return;
03107    
03108    v->vms = vms;
03109 
03110    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03111 
03112    AST_LIST_LOCK(&vmstates);
03113    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03114    AST_LIST_UNLOCK(&vmstates);
03115 }
03116 
03117 static void vmstate_delete(struct vm_state *vms) 
03118 {
03119    struct vmstate *vc = NULL;
03120    struct vm_state *altvms = NULL;
03121 
03122    /* If interactive, we should copy pertinent info
03123       back to the persistent state (to make update immediate) */
03124    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03125       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03126       altvms->newmessages = vms->newmessages;
03127       altvms->oldmessages = vms->oldmessages;
03128       altvms->updated = 1;
03129       vms->mailstream = mail_close(vms->mailstream);
03130 
03131       /* Interactive states are not stored within the persistent list */
03132       return;
03133    }
03134    
03135    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03136    
03137    AST_LIST_LOCK(&vmstates);
03138    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03139       if (vc->vms == vms) {
03140          AST_LIST_REMOVE_CURRENT(list);
03141          break;
03142       }
03143    }
03144    AST_LIST_TRAVERSE_SAFE_END
03145    AST_LIST_UNLOCK(&vmstates);
03146    
03147    if (vc) {
03148       ast_mutex_destroy(&vc->vms->lock);
03149       ast_free(vc);
03150    }
03151    else
03152       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03153 }
03154 
03155 static void set_update(MAILSTREAM * stream) 
03156 {
03157    struct vm_state *vms;
03158    char *mailbox = stream->mailbox, *user;
03159    char buf[1024] = "";
03160 
03161    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03162       if (user && option_debug > 2)
03163          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03164       return;
03165    }
03166 
03167    ast_debug(3, "User %s mailbox set for update.\n", user);
03168 
03169    vms->updated = 1; /* Set updated flag since mailbox changed */
03170 }
03171 
03172 static void init_vm_state(struct vm_state *vms) 
03173 {
03174    int x;
03175    vms->vmArrayIndex = 0;
03176    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03177       vms->msgArray[x] = 0;
03178    }
03179    ast_mutex_init(&vms->lock);
03180 }
03181 
03182 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03183 {
03184    char *body_content;
03185    char *body_decoded;
03186    char *fn = is_intro ? vms->introfn : vms->fn;
03187    unsigned long len;
03188    unsigned long newlen;
03189    char filename[256];
03190    
03191    if (!body || body == NIL)
03192       return -1;
03193 
03194    ast_mutex_lock(&vms->lock);
03195    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03196    ast_mutex_unlock(&vms->lock);
03197    if (body_content != NIL) {
03198       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03199       /* ast_debug(1,body_content); */
03200       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03201       /* If the body of the file is empty, return an error */
03202       if (!newlen) {
03203          return -1;
03204       }
03205       write_file(filename, (char *) body_decoded, newlen);
03206    } else {
03207       ast_debug(5, "Body of message is NULL.\n");
03208       return -1;
03209    }
03210    return 0;
03211 }
03212 
03213 /*! 
03214  * \brief Get delimiter via mm_list callback 
03215  * \param stream
03216  *
03217  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03218  */
03219 /* MUTEX should already be held */
03220 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03221    char tmp[50];
03222    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03223    mail_list(stream, tmp, "*");
03224 }
03225 
03226 /*! 
03227  * \brief Check Quota for user 
03228  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03229  * \param mailbox the mailbox to check the quota for.
03230  *
03231  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03232  */
03233 static void check_quota(struct vm_state *vms, char *mailbox) {
03234    ast_mutex_lock(&vms->lock);
03235    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03236    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03237    if (vms && vms->mailstream != NULL) {
03238       imap_getquotaroot(vms->mailstream, mailbox);
03239    } else {
03240       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03241    }
03242    ast_mutex_unlock(&vms->lock);
03243 }
03244 
03245 #endif /* IMAP_STORAGE */
03246 
03247 /*! \brief Lock file path
03248  * only return failure if ast_lock_path returns 'timeout',
03249  * not if the path does not exist or any other reason
03250  */
03251 static int vm_lock_path(const char *path)
03252 {
03253    switch (ast_lock_path(path)) {
03254    case AST_LOCK_TIMEOUT:
03255       return -1;
03256    default:
03257       return 0;
03258    }
03259 }
03260 
03261 
03262 #ifdef ODBC_STORAGE
03263 struct generic_prepare_struct {
03264    char *sql;
03265    int argc;
03266    char **argv;
03267 };
03268 
03269 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03270 {
03271    struct generic_prepare_struct *gps = data;
03272    int res, i;
03273    SQLHSTMT stmt;
03274 
03275    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03276    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03277       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03278       return NULL;
03279    }
03280    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03281    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03282       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03283       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03284       return NULL;
03285    }
03286    for (i = 0; i < gps->argc; i++)
03287       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03288 
03289    return stmt;
03290 }
03291 
03292 /*!
03293  * \brief Retrieves a file from an ODBC data store.
03294  * \param dir the path to the file to be retreived.
03295  * \param msgnum the message number, such as within a mailbox folder.
03296  * 
03297  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03298  * 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.
03299  *
03300  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03301  * The output is the message information file with the name msgnum and the extension .txt
03302  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03303  * 
03304  * \return 0 on success, -1 on error.
03305  */
03306 static int retrieve_file(char *dir, int msgnum)
03307 {
03308    int x = 0;
03309    int res;
03310    int fd = -1;
03311    size_t fdlen = 0;
03312    void *fdm = MAP_FAILED;
03313    SQLSMALLINT colcount = 0;
03314    SQLHSTMT stmt;
03315    char sql[PATH_MAX];
03316    char fmt[80]="";
03317    char *c;
03318    char coltitle[256];
03319    SQLSMALLINT collen;
03320    SQLSMALLINT datatype;
03321    SQLSMALLINT decimaldigits;
03322    SQLSMALLINT nullable;
03323    SQLULEN colsize;
03324    SQLLEN colsize2;
03325    FILE *f = NULL;
03326    char rowdata[80];
03327    char fn[PATH_MAX];
03328    char full_fn[PATH_MAX];
03329    char msgnums[80];
03330    char *argv[] = { dir, msgnums };
03331    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03332 
03333    struct odbc_obj *obj;
03334    obj = ast_odbc_request_obj(odbc_database, 0);
03335    if (obj) {
03336       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03337       c = strchr(fmt, '|');
03338       if (c)
03339          *c = '\0';
03340       if (!strcasecmp(fmt, "wav49"))
03341          strcpy(fmt, "WAV");
03342       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03343       if (msgnum > -1)
03344          make_file(fn, sizeof(fn), dir, msgnum);
03345       else
03346          ast_copy_string(fn, dir, sizeof(fn));
03347 
03348       /* Create the information file */
03349       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03350       
03351       if (!(f = fopen(full_fn, "w+"))) {
03352          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03353          goto yuck;
03354       }
03355       
03356       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03357       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03358       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03359       if (!stmt) {
03360          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03361          ast_odbc_release_obj(obj);
03362          goto yuck;
03363       }
03364       res = SQLFetch(stmt);
03365       if (res == SQL_NO_DATA) {
03366          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03367          ast_odbc_release_obj(obj);
03368          goto yuck;
03369       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03370          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03371          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03372          ast_odbc_release_obj(obj);
03373          goto yuck;
03374       }
03375       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03376       if (fd < 0) {
03377          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03378          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03379          ast_odbc_release_obj(obj);
03380          goto yuck;
03381       }
03382       res = SQLNumResultCols(stmt, &colcount);
03383       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03384          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03385          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03386          ast_odbc_release_obj(obj);
03387          goto yuck;
03388       }
03389       if (f) 
03390          fprintf(f, "[message]\n");
03391       for (x = 0; x < colcount; x++) {
03392          rowdata[0] = '\0';
03393          colsize = 0;
03394          collen = sizeof(coltitle);
03395          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03396                   &datatype, &colsize, &decimaldigits, &nullable);
03397          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03398             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03399             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03400             ast_odbc_release_obj(obj);
03401             goto yuck;
03402          }
03403          if (!strcasecmp(coltitle, "recording")) {
03404             off_t offset;
03405             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03406             fdlen = colsize2;
03407             if (fd > -1) {
03408                char tmp[1]="";
03409                lseek(fd, fdlen - 1, SEEK_SET);
03410                if (write(fd, tmp, 1) != 1) {
03411                   close(fd);
03412                   fd = -1;
03413                   continue;
03414                }
03415                /* Read out in small chunks */
03416                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03417                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03418                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03419                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03420                      ast_odbc_release_obj(obj);
03421                      goto yuck;
03422                   } else {
03423                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03424                      munmap(fdm, CHUNKSIZE);
03425                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03426                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03427                         unlink(full_fn);
03428                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03429                         ast_odbc_release_obj(obj);
03430                         goto yuck;
03431                      }
03432                   }
03433                }
03434                if (truncate(full_fn, fdlen) < 0) {
03435                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03436                }
03437             }
03438          } else {
03439             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03440             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03441                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03442                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03443                ast_odbc_release_obj(obj);
03444                goto yuck;
03445             }
03446             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03447                fprintf(f, "%s=%s\n", coltitle, rowdata);
03448          }
03449       }
03450       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03451       ast_odbc_release_obj(obj);
03452    } else
03453       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03454 yuck:
03455    if (f)
03456       fclose(f);
03457    if (fd > -1)
03458       close(fd);
03459    return x - 1;
03460 }
03461 
03462 /*!
03463  * \brief Determines the highest message number in use for a given user and mailbox folder.
03464  * \param vmu 
03465  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03466  *
03467  * This method is used when mailboxes are stored in an ODBC back end.
03468  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03469  *
03470  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03471 
03472  */
03473 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03474 {
03475    int x = 0;
03476    int res;
03477    SQLHSTMT stmt;
03478    char sql[PATH_MAX];
03479    char rowdata[20];
03480    char *argv[] = { dir };
03481    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03482 
03483    struct odbc_obj *obj;
03484    obj = ast_odbc_request_obj(odbc_database, 0);
03485    if (obj) {
03486       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03487 
03488       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03489       if (!stmt) {
03490          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03491          ast_odbc_release_obj(obj);
03492          goto yuck;
03493       }
03494       res = SQLFetch(stmt);
03495       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03496          if (res == SQL_NO_DATA) {
03497             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03498          } else {
03499             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03500          }
03501 
03502          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03503          ast_odbc_release_obj(obj);
03504          goto yuck;
03505       }
03506       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03507       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03508          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03509          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03510          ast_odbc_release_obj(obj);
03511          goto yuck;
03512       }
03513       if (sscanf(rowdata, "%30d", &x) != 1)
03514          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03515       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03516       ast_odbc_release_obj(obj);
03517       return x;
03518    } else
03519       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03520 yuck:
03521    return x - 1;
03522 }
03523 
03524 /*!
03525  * \brief Determines if the specified message exists.
03526  * \param dir the folder the mailbox folder to look for messages. 
03527  * \param msgnum the message index to query for.
03528  *
03529  * This method is used when mailboxes are stored in an ODBC back end.
03530  *
03531  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03532  */
03533 static int message_exists(char *dir, int msgnum)
03534 {
03535    int x = 0;
03536    int res;
03537    SQLHSTMT stmt;
03538    char sql[PATH_MAX];
03539    char rowdata[20];
03540    char msgnums[20];
03541    char *argv[] = { dir, msgnums };
03542    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03543 
03544    struct odbc_obj *obj;
03545    obj = ast_odbc_request_obj(odbc_database, 0);
03546    if (obj) {
03547       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03548       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03549       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03550       if (!stmt) {
03551          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03552          ast_odbc_release_obj(obj);
03553          goto yuck;
03554       }
03555       res = SQLFetch(stmt);
03556       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03557          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03558          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03559          ast_odbc_release_obj(obj);
03560          goto yuck;
03561       }
03562       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03563       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03564          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03565          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03566          ast_odbc_release_obj(obj);
03567          goto yuck;
03568       }
03569       if (sscanf(rowdata, "%30d", &x) != 1)
03570          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03571       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03572       ast_odbc_release_obj(obj);
03573    } else
03574       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03575 yuck:
03576    return x;
03577 }
03578 
03579 /*!
03580  * \brief returns the number of messages found.
03581  * \param vmu
03582  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03583  *
03584  * This method is used when mailboxes are stored in an ODBC back end.
03585  *
03586  * \return The count of messages being zero or more, less than zero on error.
03587  */
03588 static int count_messages(struct ast_vm_user *vmu, char *dir)
03589 {
03590    int x = 0;
03591    int res;
03592    SQLHSTMT stmt;
03593    char sql[PATH_MAX];
03594    char rowdata[20];
03595    char *argv[] = { dir };
03596    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03597 
03598    struct odbc_obj *obj;
03599    obj = ast_odbc_request_obj(odbc_database, 0);
03600    if (obj) {
03601       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03602       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03603       if (!stmt) {
03604          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03605          ast_odbc_release_obj(obj);
03606          goto yuck;
03607       }
03608       res = SQLFetch(stmt);
03609       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03610          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03611          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03612          ast_odbc_release_obj(obj);
03613          goto yuck;
03614       }
03615       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03616       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03617          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03618          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03619          ast_odbc_release_obj(obj);
03620          goto yuck;
03621       }
03622       if (sscanf(rowdata, "%30d", &x) != 1)
03623          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03624       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03625       ast_odbc_release_obj(obj);
03626       return x;
03627    } else
03628       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03629 yuck:
03630    return x - 1;
03631 
03632 }
03633 
03634 /*!
03635  * \brief Deletes a message from the mailbox folder.
03636  * \param sdir The mailbox folder to work in.
03637  * \param smsg The message index to be deleted.
03638  *
03639  * This method is used when mailboxes are stored in an ODBC back end.
03640  * The specified message is directly deleted from the database 'voicemessages' table.
03641  * 
03642  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03643  */
03644 static void delete_file(const char *sdir, int smsg)
03645 {
03646    SQLHSTMT stmt;
03647    char sql[PATH_MAX];
03648    char msgnums[20];
03649    char *argv[] = { NULL, msgnums };
03650    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03651    struct odbc_obj *obj;
03652 
03653    argv[0] = ast_strdupa(sdir);
03654 
03655    obj = ast_odbc_request_obj(odbc_database, 0);
03656    if (obj) {
03657       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03658       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03659       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03660       if (!stmt)
03661          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03662       else
03663          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03664       ast_odbc_release_obj(obj);
03665    } else
03666       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03667    return;  
03668 }
03669 
03670 /*!
03671  * \brief Copies a voicemail from one mailbox to another.
03672  * \param sdir the folder for which to look for the message to be copied.
03673  * \param smsg the index of the message to be copied.
03674  * \param ddir the destination folder to copy the message into.
03675  * \param dmsg the index to be used for the copied message.
03676  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03677  * \param dmailboxcontext The context for the destination user.
03678  *
03679  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03680  */
03681 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03682 {
03683    SQLHSTMT stmt;
03684    char sql[512];
03685    char msgnums[20];
03686    char msgnumd[20];
03687    struct odbc_obj *obj;
03688    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03689    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03690 
03691    delete_file(ddir, dmsg);
03692    obj = ast_odbc_request_obj(odbc_database, 0);
03693    if (obj) {
03694       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03695       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03696       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);
03697       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03698       if (!stmt)
03699          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03700       else
03701          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03702       ast_odbc_release_obj(obj);
03703    } else
03704       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03705    return;  
03706 }
03707 
03708 struct insert_data {
03709    char *sql;
03710    const char *dir;
03711    const char *msgnums;
03712    void *data;
03713    SQLLEN datalen;
03714    SQLLEN indlen;
03715    const char *context;
03716    const char *macrocontext;
03717    const char *callerid;
03718    const char *origtime;
03719    const char *duration;
03720    const char *mailboxuser;
03721    const char *mailboxcontext;
03722    const char *category;
03723    const char *flag;
03724 };
03725 
03726 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03727 {
03728    struct insert_data *data = vdata;
03729    int res;
03730    SQLHSTMT stmt;
03731 
03732    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03733    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03734       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03735       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03736       return NULL;
03737    }
03738 
03739    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03740    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03741    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03742    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03743    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03744    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03745    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03746    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03747    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03748    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03749    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03750    if (!ast_strlen_zero(data->category)) {
03751       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03752    }
03753    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03754    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03755       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03756       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03757       return NULL;
03758    }
03759 
03760    return stmt;
03761 }
03762 
03763 /*!
03764  * \brief Stores a voicemail into the database.
03765  * \param dir the folder the mailbox folder to store the message.
03766  * \param mailboxuser the user owning the mailbox folder.
03767  * \param mailboxcontext
03768  * \param msgnum the message index for the message to be stored.
03769  *
03770  * This method is used when mailboxes are stored in an ODBC back end.
03771  * The message sound file and information file is looked up on the file system. 
03772  * A SQL query is invoked to store the message into the (MySQL) database.
03773  *
03774  * \return the zero on success -1 on error.
03775  */
03776 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03777 {
03778    int res = 0;
03779    int fd = -1;
03780    void *fdm = MAP_FAILED;
03781    off_t fdlen = -1;
03782    SQLHSTMT stmt;
03783    char sql[PATH_MAX];
03784    char msgnums[20];
03785    char fn[PATH_MAX];
03786    char full_fn[PATH_MAX];
03787    char fmt[80]="";
03788    char *c;
03789    struct ast_config *cfg = NULL;
03790    struct odbc_obj *obj;
03791    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03792       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03793    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03794 
03795    delete_file(dir, msgnum);
03796    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03797       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03798       return -1;
03799    }
03800 
03801    do {
03802       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03803       c = strchr(fmt, '|');
03804       if (c)
03805          *c = '\0';
03806       if (!strcasecmp(fmt, "wav49"))
03807          strcpy(fmt, "WAV");
03808       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03809       if (msgnum > -1)
03810          make_file(fn, sizeof(fn), dir, msgnum);
03811       else
03812          ast_copy_string(fn, dir, sizeof(fn));
03813       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03814       cfg = ast_config_load(full_fn, config_flags);
03815       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03816       fd = open(full_fn, O_RDWR);
03817       if (fd < 0) {
03818          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03819          res = -1;
03820          break;
03821       }
03822       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03823          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03824             idata.context = "";
03825          }
03826          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03827             idata.macrocontext = "";
03828          }
03829          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03830             idata.callerid = "";
03831          }
03832          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03833             idata.origtime = "";
03834          }
03835          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03836             idata.duration = "";
03837          }
03838          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03839             idata.category = "";
03840          }
03841          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03842             idata.flag = "";
03843          }
03844       }
03845       fdlen = lseek(fd, 0, SEEK_END);
03846       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03847          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03848          res = -1;
03849          break;
03850       }
03851       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03852       if (fdm == MAP_FAILED) {
03853          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03854          res = -1;
03855          break;
03856       } 
03857       idata.data = fdm;
03858       idata.datalen = idata.indlen = fdlen;
03859 
03860       if (!ast_strlen_zero(idata.category)) 
03861          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03862       else
03863          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03864 
03865       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03866          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03867       } else {
03868          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03869          res = -1;
03870       }
03871    } while (0);
03872    if (obj) {
03873       ast_odbc_release_obj(obj);
03874    }
03875    if (cfg)
03876       ast_config_destroy(cfg);
03877    if (fdm != MAP_FAILED)
03878       munmap(fdm, fdlen);
03879    if (fd > -1)
03880       close(fd);
03881    return res;
03882 }
03883 
03884 /*!
03885  * \brief Renames a message in a mailbox folder.
03886  * \param sdir The folder of the message to be renamed.
03887  * \param smsg The index of the message to be renamed.
03888  * \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.
03889  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03890  * \param ddir The destination folder for the message to be renamed into
03891  * \param dmsg The destination message for the message to be renamed.
03892  *
03893  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03894  * 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.
03895  * 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.
03896  */
03897 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03898 {
03899    SQLHSTMT stmt;
03900    char sql[PATH_MAX];
03901    char msgnums[20];
03902    char msgnumd[20];
03903    struct odbc_obj *obj;
03904    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03905    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03906 
03907    delete_file(ddir, dmsg);
03908    obj = ast_odbc_request_obj(odbc_database, 0);
03909    if (obj) {
03910       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03911       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03912       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03913       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03914       if (!stmt)
03915          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03916       else
03917          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03918       ast_odbc_release_obj(obj);
03919    } else
03920       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03921    return;  
03922 }
03923 
03924 /*!
03925  * \brief Removes a voicemail message file.
03926  * \param dir the path to the message file.
03927  * \param msgnum the unique number for the message within the mailbox.
03928  *
03929  * Removes the message content file and the information file.
03930  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03931  * Typical use is to clean up after a RETRIEVE operation. 
03932  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03933  * \return zero on success, -1 on error.
03934  */
03935 static int remove_file(char *dir, int msgnum)
03936 {
03937    char fn[PATH_MAX];
03938    char full_fn[PATH_MAX];
03939    char msgnums[80];
03940    
03941    if (msgnum > -1) {
03942       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03943       make_file(fn, sizeof(fn), dir, msgnum);
03944    } else
03945       ast_copy_string(fn, dir, sizeof(fn));
03946    ast_filedelete(fn, NULL);  
03947    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03948    unlink(full_fn);
03949    return 0;
03950 }
03951 #else
03952 #ifndef IMAP_STORAGE
03953 /*!
03954  * \brief Find all .txt files - even if they are not in sequence from 0000.
03955  * \param vmu
03956  * \param dir
03957  *
03958  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03959  *
03960  * \return the count of messages, zero or more.
03961  */
03962 static int count_messages(struct ast_vm_user *vmu, char *dir)
03963 {
03964 
03965    int vmcount = 0;
03966    DIR *vmdir = NULL;
03967    struct dirent *vment = NULL;
03968 
03969    if (vm_lock_path(dir))
03970       return ERROR_LOCK_PATH;
03971 
03972    if ((vmdir = opendir(dir))) {
03973       while ((vment = readdir(vmdir))) {
03974          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03975             vmcount++;
03976          }
03977       }
03978       closedir(vmdir);
03979    }
03980    ast_unlock_path(dir);
03981    
03982    return vmcount;
03983 }
03984 
03985 /*!
03986  * \brief Renames a message in a mailbox folder.
03987  * \param sfn The path to the mailbox information and data file to be renamed.
03988  * \param dfn The path for where the message data and information files will be renamed to.
03989  *
03990  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03991  */
03992 static void rename_file(char *sfn, char *dfn)
03993 {
03994    char stxt[PATH_MAX];
03995    char dtxt[PATH_MAX];
03996    ast_filerename(sfn, dfn, NULL);
03997    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03998    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03999    if (ast_check_realtime("voicemail_data")) {
04000       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04001    }
04002    rename(stxt, dtxt);
04003 }
04004 
04005 /*! 
04006  * \brief Determines the highest message number in use for a given user and mailbox folder.
04007  * \param vmu 
04008  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04009  *
04010  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04011  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04012  *
04013  * \note Should always be called with a lock already set on dir.
04014  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04015  */
04016 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04017 {
04018    int x;
04019    unsigned char map[MAXMSGLIMIT] = "";
04020    DIR *msgdir;
04021    struct dirent *msgdirent;
04022    int msgdirint;
04023    char extension[4];
04024    int stopcount = 0;
04025 
04026    /* Reading the entire directory into a file map scales better than
04027     * doing a stat repeatedly on a predicted sequence.  I suspect this
04028     * is partially due to stat(2) internally doing a readdir(2) itself to
04029     * find each file. */
04030    if (!(msgdir = opendir(dir))) {
04031       return -1;
04032    }
04033 
04034    while ((msgdirent = readdir(msgdir))) {
04035       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04036          map[msgdirint] = 1;
04037          stopcount++;
04038          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04039       }
04040    }
04041    closedir(msgdir);
04042 
04043    for (x = 0; x < vmu->maxmsg; x++) {
04044       if (map[x] == 1) {
04045          stopcount--;
04046       } else if (map[x] == 0 && !stopcount) {
04047          break;
04048       }
04049    }
04050 
04051    return x - 1;
04052 }
04053 
04054 #endif /* #ifndef IMAP_STORAGE */
04055 #endif /* #else of #ifdef ODBC_STORAGE */
04056 #ifndef IMAP_STORAGE
04057 /*!
04058  * \brief Utility function to copy a file.
04059  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04060  * \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.
04061  *
04062  * 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.
04063  * The copy operation copies up to 4096 bytes at once.
04064  *
04065  * \return zero on success, -1 on error.
04066  */
04067 static int copy(char *infile, char *outfile)
04068 {
04069    int ifd;
04070    int ofd;
04071    int res;
04072    int len;
04073    char buf[4096];
04074 
04075 #ifdef HARDLINK_WHEN_POSSIBLE
04076    /* Hard link if possible; saves disk space & is faster */
04077    if (link(infile, outfile)) {
04078 #endif
04079       if ((ifd = open(infile, O_RDONLY)) < 0) {
04080          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04081          return -1;
04082       }
04083       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04084          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04085          close(ifd);
04086          return -1;
04087       }
04088       do {
04089          len = read(ifd, buf, sizeof(buf));
04090          if (len < 0) {
04091             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04092             close(ifd);
04093             close(ofd);
04094             unlink(outfile);
04095          } else if (len) {
04096             res = write(ofd, buf, len);
04097             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04098                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04099                close(ifd);
04100                close(ofd);
04101                unlink(outfile);
04102             }
04103          }
04104       } while (len);
04105       close(ifd);
04106       close(ofd);
04107       return 0;
04108 #ifdef HARDLINK_WHEN_POSSIBLE
04109    } else {
04110       /* Hard link succeeded */
04111       return 0;
04112    }
04113 #endif
04114 }
04115 
04116 /*!
04117  * \brief Copies a voicemail information (envelope) file.
04118  * \param frompath
04119  * \param topath 
04120  *
04121  * Every voicemail has the data (.wav) file, and the information file.
04122  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04123  * This is used by the COPY macro when not using IMAP storage.
04124  */
04125 static void copy_plain_file(char *frompath, char *topath)
04126 {
04127    char frompath2[PATH_MAX], topath2[PATH_MAX];
04128    struct ast_variable *tmp,*var = NULL;
04129    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04130    ast_filecopy(frompath, topath, NULL);
04131    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04132    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04133    if (ast_check_realtime("voicemail_data")) {
04134       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04135       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04136       for (tmp = var; tmp; tmp = tmp->next) {
04137          if (!strcasecmp(tmp->name, "origmailbox")) {
04138             origmailbox = tmp->value;
04139          } else if (!strcasecmp(tmp->name, "context")) {
04140             context = tmp->value;
04141          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04142             macrocontext = tmp->value;
04143          } else if (!strcasecmp(tmp->name, "exten")) {
04144             exten = tmp->value;
04145          } else if (!strcasecmp(tmp->name, "priority")) {
04146             priority = tmp->value;
04147          } else if (!strcasecmp(tmp->name, "callerchan")) {
04148             callerchan = tmp->value;
04149          } else if (!strcasecmp(tmp->name, "callerid")) {
04150             callerid = tmp->value;
04151          } else if (!strcasecmp(tmp->name, "origdate")) {
04152             origdate = tmp->value;
04153          } else if (!strcasecmp(tmp->name, "origtime")) {
04154             origtime = tmp->value;
04155          } else if (!strcasecmp(tmp->name, "category")) {
04156             category = tmp->value;
04157          } else if (!strcasecmp(tmp->name, "duration")) {
04158             duration = tmp->value;
04159          }
04160       }
04161       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);
04162    }
04163    copy(frompath2, topath2);
04164    ast_variables_destroy(var);
04165 }
04166 #endif
04167 
04168 /*! 
04169  * \brief Removes the voicemail sound and information file.
04170  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04171  *
04172  * This is used by the DELETE macro when voicemails are stored on the file system.
04173  *
04174  * \return zero on success, -1 on error.
04175  */
04176 static int vm_delete(char *file)
04177 {
04178    char *txt;
04179    int txtsize = 0;
04180 
04181    txtsize = (strlen(file) + 5)*sizeof(char);
04182    txt = ast_alloca(txtsize);
04183    /* Sprintf here would safe because we alloca'd exactly the right length,
04184     * but trying to eliminate all sprintf's anyhow
04185     */
04186    if (ast_check_realtime("voicemail_data")) {
04187       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04188    }
04189    snprintf(txt, txtsize, "%s.txt", file);
04190    unlink(txt);
04191    return ast_filedelete(file, NULL);
04192 }
04193 
04194 /*!
04195  * \brief utility used by inchar(), for base_encode()
04196  */
04197 static int inbuf(struct baseio *bio, FILE *fi)
04198 {
04199    int l;
04200 
04201    if (bio->ateof)
04202       return 0;
04203 
04204    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04205       if (ferror(fi))
04206          return -1;
04207 
04208       bio->ateof = 1;
04209       return 0;
04210    }
04211 
04212    bio->iolen = l;
04213    bio->iocp = 0;
04214 
04215    return 1;
04216 }
04217 
04218 /*!
04219  * \brief utility used by base_encode()
04220  */
04221 static int inchar(struct baseio *bio, FILE *fi)
04222 {
04223    if (bio->iocp>=bio->iolen) {
04224       if (!inbuf(bio, fi))
04225          return EOF;
04226    }
04227 
04228    return bio->iobuf[bio->iocp++];
04229 }
04230 
04231 /*!
04232  * \brief utility used by base_encode()
04233  */
04234 static int ochar(struct baseio *bio, int c, FILE *so)
04235 {
04236    if (bio->linelength >= BASELINELEN) {
04237       if (fputs(ENDL, so) == EOF) {
04238          return -1;
04239       }
04240 
04241       bio->linelength = 0;
04242    }
04243 
04244    if (putc(((unsigned char) c), so) == EOF) {
04245       return -1;
04246    }
04247 
04248    bio->linelength++;
04249 
04250    return 1;
04251 }
04252 
04253 /*!
04254  * \brief Performs a base 64 encode algorithm on the contents of a File
04255  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04256  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04257  *
04258  * 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 ?
04259  *
04260  * \return zero on success, -1 on error.
04261  */
04262 static int base_encode(char *filename, FILE *so)
04263 {
04264    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04265       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04266       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04267       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04268    int i, hiteof = 0;
04269    FILE *fi;
04270    struct baseio bio;
04271 
04272    memset(&bio, 0, sizeof(bio));
04273    bio.iocp = BASEMAXINLINE;
04274 
04275    if (!(fi = fopen(filename, "rb"))) {
04276       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04277       return -1;
04278    }
04279 
04280    while (!hiteof){
04281       unsigned char igroup[3], ogroup[4];
04282       int c, n;
04283 
04284       memset(igroup, 0, sizeof(igroup));
04285 
04286       for (n = 0; n < 3; n++) {
04287          if ((c = inchar(&bio, fi)) == EOF) {
04288             hiteof = 1;
04289             break;
04290          }
04291 
04292          igroup[n] = (unsigned char) c;
04293       }
04294 
04295       if (n > 0) {
04296          ogroup[0]= dtable[igroup[0] >> 2];
04297          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04298          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04299          ogroup[3]= dtable[igroup[2] & 0x3F];
04300 
04301          if (n < 3) {
04302             ogroup[3] = '=';
04303 
04304             if (n < 2)
04305                ogroup[2] = '=';
04306          }
04307 
04308          for (i = 0; i < 4; i++)
04309             ochar(&bio, ogroup[i], so);
04310       }
04311    }
04312 
04313    fclose(fi);
04314    
04315    if (fputs(ENDL, so) == EOF) {
04316       return 0;
04317    }
04318 
04319    return 1;
04320 }
04321 
04322 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)
04323 {
04324    char callerid[256];
04325    char num[12];
04326    char fromdir[256], fromfile[256];
04327    struct ast_config *msg_cfg;
04328    const char *origcallerid, *origtime;
04329    char origcidname[80], origcidnum[80], origdate[80];
04330    int inttime;
04331    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04332 
04333    /* Prepare variables for substitution in email body and subject */
04334    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04335    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04336    snprintf(num, sizeof(num), "%d", msgnum);
04337    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04338    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04339    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04340    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04341       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04342    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04343    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04344    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04345    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04346    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04347 
04348    /* Retrieve info from VM attribute file */
04349    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04350    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04351    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04352       strcat(fromfile, ".txt");
04353    }
04354    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04355       if (option_debug > 0) {
04356          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04357       }
04358       return;
04359    }
04360 
04361    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04362       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04363       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04364       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04365       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04366    }
04367 
04368    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04369       struct timeval tv = { inttime, };
04370       struct ast_tm tm;
04371       ast_localtime(&tv, &tm, NULL);
04372       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04373       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04374    }
04375    ast_config_destroy(msg_cfg);
04376 }
04377 
04378 /*!
04379  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04380  * \param from The string to work with.
04381  * \param buf The buffer into which to write the modified quoted string.
04382  * \param maxlen Always zero, but see \see ast_str
04383  * 
04384  * \return The destination string with quotes wrapped on it (the to field).
04385  */
04386 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04387 {
04388    const char *ptr;
04389 
04390    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04391    ast_str_set(buf, maxlen, "\"");
04392    for (ptr = from; *ptr; ptr++) {
04393       if (*ptr == '"' || *ptr == '\\') {
04394          ast_str_append(buf, maxlen, "\\%c", *ptr);
04395       } else {
04396          ast_str_append(buf, maxlen, "%c", *ptr);
04397       }
04398    }
04399    ast_str_append(buf, maxlen, "\"");
04400 
04401    return ast_str_buffer(*buf);
04402 }
04403 
04404 /*! \brief
04405  * fill in *tm for current time according to the proper timezone, if any.
04406  * \return tm so it can be used as a function argument.
04407  */
04408 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04409 {
04410    const struct vm_zone *z = NULL;
04411    struct timeval t = ast_tvnow();
04412 
04413    /* Does this user have a timezone specified? */
04414    if (!ast_strlen_zero(vmu->zonetag)) {
04415       /* Find the zone in the list */
04416       AST_LIST_LOCK(&zones);
04417       AST_LIST_TRAVERSE(&zones, z, list) {
04418          if (!strcmp(z->name, vmu->zonetag))
04419             break;
04420       }
04421       AST_LIST_UNLOCK(&zones);
04422    }
04423    ast_localtime(&t, tm, z ? z->timezone : NULL);
04424    return tm;
04425 }
04426 
04427 /*!\brief Check if the string would need encoding within the MIME standard, to
04428  * avoid confusing certain mail software that expects messages to be 7-bit
04429  * clean.
04430  */
04431 static int check_mime(const char *str)
04432 {
04433    for (; *str; str++) {
04434       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04435          return 1;
04436       }
04437    }
04438    return 0;
04439 }
04440 
04441 /*!\brief Encode a string according to the MIME rules for encoding strings
04442  * that are not 7-bit clean or contain control characters.
04443  *
04444  * Additionally, if the encoded string would exceed the MIME limit of 76
04445  * characters per line, then the encoding will be broken up into multiple
04446  * sections, separated by a space character, in order to facilitate
04447  * breaking up the associated header across multiple lines.
04448  *
04449  * \param end An expandable buffer for holding the result
04450  * \param maxlen Always zero, but see \see ast_str
04451  * \param start A string to be encoded
04452  * \param preamble The length of the first line already used for this string,
04453  * to ensure that each line maintains a maximum length of 76 chars.
04454  * \param postamble the length of any additional characters appended to the
04455  * line, used to ensure proper field wrapping.
04456  * \retval The encoded string.
04457  */
04458 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04459 {
04460    struct ast_str *tmp = ast_str_alloca(80);
04461    int first_section = 1;
04462 
04463    ast_str_reset(*end);
04464    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04465    for (; *start; start++) {
04466       int need_encoding = 0;
04467       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04468          need_encoding = 1;
04469       }
04470       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04471          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04472          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04473          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04474          /* Start new line */
04475          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04476          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04477          first_section = 0;
04478       }
04479       if (need_encoding && *start == ' ') {
04480          ast_str_append(&tmp, -1, "_");
04481       } else if (need_encoding) {
04482          ast_str_append(&tmp, -1, "=%hhX", *start);
04483       } else {
04484          ast_str_append(&tmp, -1, "%c", *start);
04485       }
04486    }
04487    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04488    return ast_str_buffer(*end);
04489 }
04490 
04491 /*!
04492  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04493  * \param p The output file to generate the email contents into.
04494  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04495  * \param vmu The voicemail user who is sending the voicemail.
04496  * \param msgnum The message index in the mailbox folder.
04497  * \param context 
04498  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04499  * \param fromfolder
04500  * \param cidnum The caller ID number.
04501  * \param cidname The caller ID name.
04502  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04503  * \param attach2 
04504  * \param format The message sound file format. i.e. .wav
04505  * \param duration The time of the message content, in seconds.
04506  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04507  * \param chan
04508  * \param category
04509  * \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.
04510  * \param flag
04511  *
04512  * 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.
04513  */
04514 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)
04515 {
04516    char date[256];
04517    char host[MAXHOSTNAMELEN] = "";
04518    char who[256];
04519    char bound[256];
04520    char dur[256];
04521    struct ast_tm tm;
04522    char enc_cidnum[256] = "", enc_cidname[256] = "";
04523    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04524    char *greeting_attachment; 
04525    char filename[256];
04526 
04527    if (!str1 || !str2) {
04528       ast_free(str1);
04529       ast_free(str2);
04530       return;
04531    }
04532 
04533    if (cidnum) {
04534       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04535    }
04536    if (cidname) {
04537       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04538    }
04539    gethostname(host, sizeof(host) - 1);
04540 
04541    if (strchr(srcemail, '@')) {
04542       ast_copy_string(who, srcemail, sizeof(who));
04543    } else {
04544       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04545    }
04546 
04547    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04548    if (greeting_attachment) {
04549       *greeting_attachment++ = '\0';
04550    }
04551 
04552    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04553    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04554    fprintf(p, "Date: %s" ENDL, date);
04555 
04556    /* Set date format for voicemail mail */
04557    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04558 
04559    if (!ast_strlen_zero(fromstring)) {
04560       struct ast_channel *ast;
04561       if ((ast = ast_dummy_channel_alloc())) {
04562          char *ptr;
04563          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04564          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04565 
04566          if (check_mime(ast_str_buffer(str1))) {
04567             int first_line = 1;
04568             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04569             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04570                *ptr = '\0';
04571                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04572                first_line = 0;
04573                /* Substring is smaller, so this will never grow */
04574                ast_str_set(&str2, 0, "%s", ptr + 1);
04575             }
04576             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04577          } else {
04578             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04579          }
04580          ast = ast_channel_unref(ast);
04581       } else {
04582          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04583       }
04584    } else {
04585       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04586    }
04587 
04588    if (check_mime(vmu->fullname)) {
04589       int first_line = 1;
04590       char *ptr;
04591       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04592       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04593          *ptr = '\0';
04594          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04595          first_line = 0;
04596          /* Substring is smaller, so this will never grow */
04597          ast_str_set(&str2, 0, "%s", ptr + 1);
04598       }
04599       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04600    } else {
04601       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04602    }
04603 
04604    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04605       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04606       struct ast_channel *ast;
04607       if ((ast = ast_dummy_channel_alloc())) {
04608          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04609          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04610          if (check_mime(ast_str_buffer(str1))) {
04611             int first_line = 1;
04612             char *ptr;
04613             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04614             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04615                *ptr = '\0';
04616                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04617                first_line = 0;
04618                /* Substring is smaller, so this will never grow */
04619                ast_str_set(&str2, 0, "%s", ptr + 1);
04620             }
04621             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04622          } else {
04623             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04624          }
04625          ast = ast_channel_unref(ast);
04626       } else {
04627          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04628       }
04629    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04630       if (ast_strlen_zero(flag)) {
04631          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04632       } else {
04633          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04634       }
04635    } else {
04636       if (ast_strlen_zero(flag)) {
04637          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04638       } else {
04639          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04640       }
04641    }
04642 
04643    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04644       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04645    if (imap) {
04646       /* additional information needed for IMAP searching */
04647       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04648       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04649       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04650       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04651 #ifdef IMAP_STORAGE
04652       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04653 #else
04654       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04655 #endif
04656       /* flag added for Urgent */
04657       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04658       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04659       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04660       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04661       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04662       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04663       if (!ast_strlen_zero(category)) {
04664          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04665       } else {
04666          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04667       }
04668       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04669       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04670       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04671    }
04672    if (!ast_strlen_zero(cidnum)) {
04673       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04674    }
04675    if (!ast_strlen_zero(cidname)) {
04676       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04677    }
04678    fprintf(p, "MIME-Version: 1.0" ENDL);
04679    if (attach_user_voicemail) {
04680       /* Something unique. */
04681       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04682          (int) getpid(), (unsigned int) ast_random());
04683 
04684       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04685       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04686       fprintf(p, "--%s" ENDL, bound);
04687    }
04688    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04689    if (emailbody || vmu->emailbody) {
04690       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04691       struct ast_channel *ast;
04692       if ((ast = ast_dummy_channel_alloc())) {
04693          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04694          ast_str_substitute_variables(&str1, 0, ast, e_body);
04695 #ifdef IMAP_STORAGE
04696             {
04697                /* Convert body to native line terminators for IMAP backend */
04698                char *line = ast_str_buffer(str1), *next;
04699                do {
04700                   /* Terminate line before outputting it to the file */
04701                   if ((next = strchr(line, '\n'))) {
04702                      *next++ = '\0';
04703                   }
04704                   fprintf(p, "%s" ENDL, line);
04705                   line = next;
04706                } while (!ast_strlen_zero(line));
04707             }
04708 #else
04709          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04710 #endif
04711          ast = ast_channel_unref(ast);
04712       } else {
04713          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04714       }
04715    } else if (msgnum > -1) {
04716       if (strcmp(vmu->mailbox, mailbox)) {
04717          /* Forwarded type */
04718          struct ast_config *msg_cfg;
04719          const char *v;
04720          int inttime;
04721          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04722          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04723          /* Retrieve info from VM attribute file */
04724          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04725          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04726          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04727             strcat(fromfile, ".txt");
04728          }
04729          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04730             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04731                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04732             }
04733 
04734             /* You might be tempted to do origdate, except that a) it's in the wrong
04735              * format, and b) it's missing for IMAP recordings. */
04736             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04737                struct timeval tv = { inttime, };
04738                struct ast_tm tm;
04739                ast_localtime(&tv, &tm, NULL);
04740                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04741             }
04742             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04743                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04744                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04745                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04746                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04747                date, origcallerid, origdate);
04748             ast_config_destroy(msg_cfg);
04749          } else {
04750             goto plain_message;
04751          }
04752       } else {
04753 plain_message:
04754          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04755             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04756             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04757             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04758             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04759       }
04760    } else {
04761       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04762             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04763    }
04764 
04765    if (imap || attach_user_voicemail) {
04766       if (!ast_strlen_zero(attach2)) {
04767          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04768          ast_debug(5, "creating second attachment filename %s\n", filename);
04769          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04770          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04771          ast_debug(5, "creating attachment filename %s\n", filename);
04772          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04773       } else {
04774          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04775          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04776          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04777       }
04778    }
04779    ast_free(str1);
04780    ast_free(str2);
04781 }
04782 
04783 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)
04784 {
04785    char tmpdir[256], newtmp[256];
04786    char fname[256];
04787    char tmpcmd[256];
04788    int tmpfd = -1;
04789    int soxstatus = 0;
04790 
04791    /* Eww. We want formats to tell us their own MIME type */
04792    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04793 
04794    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04795       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04796       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04797       tmpfd = mkstemp(newtmp);
04798       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04799       ast_debug(3, "newtmp: %s\n", newtmp);
04800       if (tmpfd > -1) {
04801          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04802          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04803             attach = newtmp;
04804             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04805          } else {
04806             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04807                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04808             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04809          }
04810       }
04811    }
04812    fprintf(p, "--%s" ENDL, bound);
04813    if (msgnum > -1)
04814       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04815    else
04816       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04817    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04818    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04819    if (msgnum > -1)
04820       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04821    else
04822       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04823    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04824    base_encode(fname, p);
04825    if (last)
04826       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04827    if (tmpfd > -1) {
04828       if (soxstatus == 0) {
04829          unlink(fname);
04830       }
04831       close(tmpfd);
04832       unlink(newtmp);
04833    }
04834    return 0;
04835 }
04836 
04837 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)
04838 {
04839    FILE *p = NULL;
04840    char tmp[80] = "/tmp/astmail-XXXXXX";
04841    char tmp2[256];
04842    char *stringp;
04843 
04844    if (vmu && ast_strlen_zero(vmu->email)) {
04845       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04846       return(0);
04847    }
04848 
04849    /* Mail only the first format */
04850    format = ast_strdupa(format);
04851    stringp = format;
04852    strsep(&stringp, "|");
04853 
04854    if (!strcmp(format, "wav49"))
04855       format = "WAV";
04856    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));
04857    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04858       command hangs */
04859    if ((p = vm_mkftemp(tmp)) == NULL) {
04860       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04861       return -1;
04862    } else {
04863       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04864       fclose(p);
04865       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04866       ast_safe_system(tmp2);
04867       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04868    }
04869    return 0;
04870 }
04871 
04872 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)
04873 {
04874    char enc_cidnum[256], enc_cidname[256];
04875    char date[256];
04876    char host[MAXHOSTNAMELEN] = "";
04877    char who[256];
04878    char dur[PATH_MAX];
04879    char tmp[80] = "/tmp/astmail-XXXXXX";
04880    char tmp2[PATH_MAX];
04881    struct ast_tm tm;
04882    FILE *p;
04883    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04884 
04885    if (!str1 || !str2) {
04886       ast_free(str1);
04887       ast_free(str2);
04888       return -1;
04889    }
04890 
04891    if (cidnum) {
04892       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04893    }
04894    if (cidname) {
04895       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04896    }
04897 
04898    if ((p = vm_mkftemp(tmp)) == NULL) {
04899       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04900       ast_free(str1);
04901       ast_free(str2);
04902       return -1;
04903    }
04904    gethostname(host, sizeof(host)-1);
04905    if (strchr(srcemail, '@')) {
04906       ast_copy_string(who, srcemail, sizeof(who));
04907    } else {
04908       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04909    }
04910    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04911    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04912    fprintf(p, "Date: %s\n", date);
04913 
04914    /* Reformat for custom pager format */
04915    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04916 
04917    if (!ast_strlen_zero(pagerfromstring)) {
04918       struct ast_channel *ast;
04919       if ((ast = ast_dummy_channel_alloc())) {
04920          char *ptr;
04921          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04922          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04923 
04924          if (check_mime(ast_str_buffer(str1))) {
04925             int first_line = 1;
04926             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04927             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04928                *ptr = '\0';
04929                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04930                first_line = 0;
04931                /* Substring is smaller, so this will never grow */
04932                ast_str_set(&str2, 0, "%s", ptr + 1);
04933             }
04934             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04935          } else {
04936             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04937          }
04938          ast = ast_channel_unref(ast);
04939       } else {
04940          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04941       }
04942    } else {
04943       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04944    }
04945 
04946    if (check_mime(vmu->fullname)) {
04947       int first_line = 1;
04948       char *ptr;
04949       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04950       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04951          *ptr = '\0';
04952          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04953          first_line = 0;
04954          /* Substring is smaller, so this will never grow */
04955          ast_str_set(&str2, 0, "%s", ptr + 1);
04956       }
04957       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04958    } else {
04959       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04960    }
04961 
04962    if (!ast_strlen_zero(pagersubject)) {
04963       struct ast_channel *ast;
04964       if ((ast = ast_dummy_channel_alloc())) {
04965          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04966          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04967          if (check_mime(ast_str_buffer(str1))) {
04968             int first_line = 1;
04969             char *ptr;
04970             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04971             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04972                *ptr = '\0';
04973                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04974                first_line = 0;
04975                /* Substring is smaller, so this will never grow */
04976                ast_str_set(&str2, 0, "%s", ptr + 1);
04977             }
04978             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04979          } else {
04980             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04981          }
04982          ast = ast_channel_unref(ast);
04983       } else {
04984          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04985       }
04986    } else {
04987       if (ast_strlen_zero(flag)) {
04988          fprintf(p, "Subject: New VM\n\n");
04989       } else {
04990          fprintf(p, "Subject: New %s VM\n\n", flag);
04991       }
04992    }
04993 
04994    if (pagerbody) {
04995       struct ast_channel *ast;
04996       if ((ast = ast_dummy_channel_alloc())) {
04997          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04998          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04999          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05000          ast = ast_channel_unref(ast);
05001       } else {
05002          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05003       }
05004    } else {
05005       fprintf(p, "New %s long %s msg in box %s\n"
05006             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05007    }
05008 
05009    fclose(p);
05010    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05011    ast_safe_system(tmp2);
05012    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05013    ast_free(str1);
05014    ast_free(str2);
05015    return 0;
05016 }
05017 
05018 /*!
05019  * \brief Gets the current date and time, as formatted string.
05020  * \param s The buffer to hold the output formatted date.
05021  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05022  * 
05023  * The date format string used is "%a %b %e %r UTC %Y".
05024  * 
05025  * \return zero on success, -1 on error.
05026  */
05027 static int get_date(char *s, int len)
05028 {
05029    struct ast_tm tm;
05030    struct timeval t = ast_tvnow();
05031    
05032    ast_localtime(&t, &tm, "UTC");
05033 
05034    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05035 }
05036 
05037 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05038 {
05039    int res;
05040    char fn[PATH_MAX];
05041    char dest[PATH_MAX];
05042 
05043    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05044 
05045    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05046       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05047       return -1;
05048    }
05049 
05050    RETRIEVE(fn, -1, ext, context);
05051    if (ast_fileexists(fn, NULL, NULL) > 0) {
05052       res = ast_stream_and_wait(chan, fn, ecodes);
05053       if (res) {
05054          DISPOSE(fn, -1);
05055          return res;
05056       }
05057    } else {
05058       /* Dispose just in case */
05059       DISPOSE(fn, -1);
05060       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05061       if (res)
05062          return res;
05063       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05064       if (res)
05065          return res;
05066    }
05067    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05068    return res;
05069 }
05070 
05071 static void free_zone(struct vm_zone *z)
05072 {
05073    ast_free(z);
05074 }
05075 
05076 #ifdef ODBC_STORAGE
05077 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05078 {
05079    int x = -1;
05080    int res;
05081    SQLHSTMT stmt = NULL;
05082    char sql[PATH_MAX];
05083    char rowdata[20];
05084    char tmp[PATH_MAX] = "";
05085    struct odbc_obj *obj = NULL;
05086    char *context;
05087    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05088 
05089    if (newmsgs)
05090       *newmsgs = 0;
05091    if (oldmsgs)
05092       *oldmsgs = 0;
05093    if (urgentmsgs)
05094       *urgentmsgs = 0;
05095 
05096    /* If no mailbox, return immediately */
05097    if (ast_strlen_zero(mailbox))
05098       return 0;
05099 
05100    ast_copy_string(tmp, mailbox, sizeof(tmp));
05101 
05102    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05103       int u, n, o;
05104       char *next, *remaining = tmp;
05105       while ((next = strsep(&remaining, " ,"))) {
05106          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05107             return -1;
05108          }
05109          if (urgentmsgs) {
05110             *urgentmsgs += u;
05111          }
05112          if (newmsgs) {
05113             *newmsgs += n;
05114          }
05115          if (oldmsgs) {
05116             *oldmsgs += o;
05117          }
05118       }
05119       return 0;
05120    }
05121 
05122    context = strchr(tmp, '@');
05123    if (context) {
05124       *context = '\0';
05125       context++;
05126    } else
05127       context = "default";
05128 
05129    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05130       do {
05131          if (newmsgs) {
05132             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05133             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05134                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05135                break;
05136             }
05137             res = SQLFetch(stmt);
05138             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05139                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05140                break;
05141             }
05142             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05143             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05144                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05145                break;
05146             }
05147             *newmsgs = atoi(rowdata);
05148             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05149          }
05150 
05151          if (oldmsgs) {
05152             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05153             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05154                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05155                break;
05156             }
05157             res = SQLFetch(stmt);
05158             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05159                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05160                break;
05161             }
05162             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05163             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05164                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05165                break;
05166             }
05167             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05168             *oldmsgs = atoi(rowdata);
05169          }
05170 
05171          if (urgentmsgs) {
05172             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05173             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05174                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05175                break;
05176             }
05177             res = SQLFetch(stmt);
05178             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05179                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05180                break;
05181             }
05182             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05183             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05184                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05185                break;
05186             }
05187             *urgentmsgs = atoi(rowdata);
05188          }
05189 
05190          x = 0;
05191       } while (0);
05192    } else {
05193       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05194    }
05195 
05196    if (stmt) {
05197       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05198    }
05199    if (obj) {
05200       ast_odbc_release_obj(obj);
05201    }
05202    return x;
05203 }
05204 
05205 /*!
05206  * \brief Gets the number of messages that exist in a mailbox folder.
05207  * \param context
05208  * \param mailbox
05209  * \param folder
05210  * 
05211  * This method is used when ODBC backend is used.
05212  * \return The number of messages in this mailbox folder (zero or more).
05213  */
05214 static int messagecount(const char *context, const char *mailbox, const char *folder)
05215 {
05216    struct odbc_obj *obj = NULL;
05217    int nummsgs = 0;
05218    int res;
05219    SQLHSTMT stmt = NULL;
05220    char sql[PATH_MAX];
05221    char rowdata[20];
05222    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05223    if (!folder)
05224       folder = "INBOX";
05225    /* If no mailbox, return immediately */
05226    if (ast_strlen_zero(mailbox))
05227       return 0;
05228 
05229    obj = ast_odbc_request_obj(odbc_database, 0);
05230    if (obj) {
05231       if (!strcmp(folder, "INBOX")) {
05232          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);
05233       } else {
05234          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05235       }
05236       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05237       if (!stmt) {
05238          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05239          goto yuck;
05240       }
05241       res = SQLFetch(stmt);
05242       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05243          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05244          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05245          goto yuck;
05246       }
05247       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05248       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05249          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05250          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05251          goto yuck;
05252       }
05253       nummsgs = atoi(rowdata);
05254       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05255    } else
05256       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05257 
05258 yuck:
05259    if (obj)
05260       ast_odbc_release_obj(obj);
05261    return nummsgs;
05262 }
05263 
05264 /** 
05265  * \brief Determines if the given folder has messages.
05266  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05267  * 
05268  * This function is used when the mailbox is stored in an ODBC back end.
05269  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05270  * \return 1 if the folder has one or more messages. zero otherwise.
05271  */
05272 static int has_voicemail(const char *mailbox, const char *folder)
05273 {
05274    char tmp[256], *tmp2 = tmp, *box, *context;
05275    ast_copy_string(tmp, mailbox, sizeof(tmp));
05276    while ((context = box = strsep(&tmp2, ",&"))) {
05277       strsep(&context, "@");
05278       if (ast_strlen_zero(context))
05279          context = "default";
05280       if (messagecount(context, box, folder))
05281          return 1;
05282    }
05283    return 0;
05284 }
05285 #endif
05286 #ifndef IMAP_STORAGE
05287 /*! 
05288  * \brief Copies a message from one mailbox to another.
05289  * \param chan
05290  * \param vmu
05291  * \param imbox
05292  * \param msgnum
05293  * \param duration
05294  * \param recip
05295  * \param fmt
05296  * \param dir
05297  * \param flag
05298  *
05299  * This is only used by file storage based mailboxes.
05300  *
05301  * \return zero on success, -1 on error.
05302  */
05303 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)
05304 {
05305    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05306    const char *frombox = mbox(vmu, imbox);
05307    const char *userfolder;
05308    int recipmsgnum;
05309    int res = 0;
05310 
05311    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05312 
05313    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05314       userfolder = "Urgent";
05315    } else {
05316       userfolder = "INBOX";
05317    }
05318 
05319    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05320 
05321    if (!dir)
05322       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05323    else
05324       ast_copy_string(fromdir, dir, sizeof(fromdir));
05325 
05326    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05327    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05328 
05329    if (vm_lock_path(todir))
05330       return ERROR_LOCK_PATH;
05331 
05332    recipmsgnum = last_message_index(recip, todir) + 1;
05333    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05334       make_file(topath, sizeof(topath), todir, recipmsgnum);
05335 #ifndef ODBC_STORAGE
05336       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05337          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05338       } else {
05339 #endif
05340          /* If we are prepending a message for ODBC, then the message already
05341           * exists in the database, but we want to force copying from the
05342           * filesystem (since only the FS contains the prepend). */
05343          copy_plain_file(frompath, topath);
05344          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05345          vm_delete(topath);
05346 #ifndef ODBC_STORAGE
05347       }
05348 #endif
05349    } else {
05350       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05351       res = -1;
05352    }
05353    ast_unlock_path(todir);
05354    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05355       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05356       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05357       flag);
05358    
05359    return res;
05360 }
05361 #endif
05362 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05363 
05364 static int messagecount(const char *context, const char *mailbox, const char *folder)
05365 {
05366    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05367 }
05368 
05369 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05370 {
05371    DIR *dir;
05372    struct dirent *de;
05373    char fn[256];
05374    int ret = 0;
05375 
05376    /* If no mailbox, return immediately */
05377    if (ast_strlen_zero(mailbox))
05378       return 0;
05379 
05380    if (ast_strlen_zero(folder))
05381       folder = "INBOX";
05382    if (ast_strlen_zero(context))
05383       context = "default";
05384 
05385    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05386 
05387    if (!(dir = opendir(fn)))
05388       return 0;
05389 
05390    while ((de = readdir(dir))) {
05391       if (!strncasecmp(de->d_name, "msg", 3)) {
05392          if (shortcircuit) {
05393             ret = 1;
05394             break;
05395          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05396             ret++;
05397          }
05398       }
05399    }
05400 
05401    closedir(dir);
05402 
05403    return ret;
05404 }
05405 
05406 /** 
05407  * \brief Determines if the given folder has messages.
05408  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05409  * \param folder the folder to look in
05410  *
05411  * This function is used when the mailbox is stored in a filesystem back end.
05412  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05413  * \return 1 if the folder has one or more messages. zero otherwise.
05414  */
05415 static int has_voicemail(const char *mailbox, const char *folder)
05416 {
05417    char tmp[256], *tmp2 = tmp, *box, *context;
05418    ast_copy_string(tmp, mailbox, sizeof(tmp));
05419    if (ast_strlen_zero(folder)) {
05420       folder = "INBOX";
05421    }
05422    while ((box = strsep(&tmp2, ",&"))) {
05423       if ((context = strchr(box, '@')))
05424          *context++ = '\0';
05425       else
05426          context = "default";
05427       if (__has_voicemail(context, box, folder, 1))
05428          return 1;
05429       /* If we are checking INBOX, we should check Urgent as well */
05430       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05431          return 1;
05432       }
05433    }
05434    return 0;
05435 }
05436 
05437 
05438 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05439 {
05440    char tmp[256];
05441    char *context;
05442 
05443    /* If no mailbox, return immediately */
05444    if (ast_strlen_zero(mailbox))
05445       return 0;
05446 
05447    if (newmsgs)
05448       *newmsgs = 0;
05449    if (oldmsgs)
05450       *oldmsgs = 0;
05451    if (urgentmsgs)
05452       *urgentmsgs = 0;
05453 
05454    if (strchr(mailbox, ',')) {
05455       int tmpnew, tmpold, tmpurgent;
05456       char *mb, *cur;
05457 
05458       ast_copy_string(tmp, mailbox, sizeof(tmp));
05459       mb = tmp;
05460       while ((cur = strsep(&mb, ", "))) {
05461          if (!ast_strlen_zero(cur)) {
05462             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05463                return -1;
05464             else {
05465                if (newmsgs)
05466                   *newmsgs += tmpnew; 
05467                if (oldmsgs)
05468                   *oldmsgs += tmpold;
05469                if (urgentmsgs)
05470                   *urgentmsgs += tmpurgent;
05471             }
05472          }
05473       }
05474       return 0;
05475    }
05476 
05477    ast_copy_string(tmp, mailbox, sizeof(tmp));
05478    
05479    if ((context = strchr(tmp, '@')))
05480       *context++ = '\0';
05481    else
05482       context = "default";
05483 
05484    if (newmsgs)
05485       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05486    if (oldmsgs)
05487       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05488    if (urgentmsgs)
05489       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05490 
05491    return 0;
05492 }
05493 
05494 #endif
05495 
05496 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05497 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05498 {
05499    int urgentmsgs = 0;
05500    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05501    if (newmsgs) {
05502       *newmsgs += urgentmsgs;
05503    }
05504    return res;
05505 }
05506 
05507 static void run_externnotify(char *context, char *extension, const char *flag)
05508 {
05509    char arguments[255];
05510    char ext_context[256] = "";
05511    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05512    struct ast_smdi_mwi_message *mwi_msg;
05513 
05514    if (!ast_strlen_zero(context))
05515       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05516    else
05517       ast_copy_string(ext_context, extension, sizeof(ext_context));
05518 
05519    if (smdi_iface) {
05520       if (ast_app_has_voicemail(ext_context, NULL)) 
05521          ast_smdi_mwi_set(smdi_iface, extension);
05522       else
05523          ast_smdi_mwi_unset(smdi_iface, extension);
05524 
05525       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05526          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05527          if (!strncmp(mwi_msg->cause, "INV", 3))
05528             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05529          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05530             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05531          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05532          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05533       } else {
05534          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05535       }
05536    }
05537 
05538    if (!ast_strlen_zero(externnotify)) {
05539       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05540          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05541       } else {
05542          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05543          ast_debug(1, "Executing %s\n", arguments);
05544          ast_safe_system(arguments);
05545       }
05546    }
05547 }
05548 
05549 /*!
05550  * \brief Variables used for saving a voicemail.
05551  *
05552  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05553  */
05554 struct leave_vm_options {
05555    unsigned int flags;
05556    signed char record_gain;
05557    char *exitcontext;
05558 };
05559 
05560 /*!
05561  * \brief Prompts the user and records a voicemail to a mailbox.
05562  * \param chan
05563  * \param ext
05564  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05565  * 
05566  * 
05567  * 
05568  * \return zero on success, -1 on error.
05569  */
05570 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05571 {
05572 #ifdef IMAP_STORAGE
05573    int newmsgs, oldmsgs;
05574 #else
05575    char urgdir[PATH_MAX];
05576 #endif
05577    char txtfile[PATH_MAX];
05578    char tmptxtfile[PATH_MAX];
05579    struct vm_state *vms = NULL;
05580    char callerid[256];
05581    FILE *txt;
05582    char date[256];
05583    int txtdes;
05584    int res = 0;
05585    int msgnum;
05586    int duration = 0;
05587    int sound_duration = 0;
05588    int ausemacro = 0;
05589    int ousemacro = 0;
05590    int ouseexten = 0;
05591    char tmpdur[16];
05592    char priority[16];
05593    char origtime[16];
05594    char dir[PATH_MAX];
05595    char tmpdir[PATH_MAX];
05596    char fn[PATH_MAX];
05597    char prefile[PATH_MAX] = "";
05598    char tempfile[PATH_MAX] = "";
05599    char ext_context[256] = "";
05600    char fmt[80];
05601    char *context;
05602    char ecodes[17] = "#";
05603    struct ast_str *tmp = ast_str_create(16);
05604    char *tmpptr;
05605    struct ast_vm_user *vmu;
05606    struct ast_vm_user svm;
05607    const char *category = NULL;
05608    const char *code;
05609    const char *alldtmf = "0123456789ABCD*#";
05610    char flag[80];
05611 
05612    if (!tmp) {
05613       return -1;
05614    }
05615 
05616    ast_str_set(&tmp, 0, "%s", ext);
05617    ext = ast_str_buffer(tmp);
05618    if ((context = strchr(ext, '@'))) {
05619       *context++ = '\0';
05620       tmpptr = strchr(context, '&');
05621    } else {
05622       tmpptr = strchr(ext, '&');
05623    }
05624 
05625    if (tmpptr)
05626       *tmpptr++ = '\0';
05627 
05628    ast_channel_lock(chan);
05629    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05630       category = ast_strdupa(category);
05631    }
05632    ast_channel_unlock(chan);
05633 
05634    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05635       ast_copy_string(flag, "Urgent", sizeof(flag));
05636    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05637       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05638    } else {
05639       flag[0] = '\0';
05640    }
05641 
05642    ast_debug(3, "Before find_user\n");
05643    if (!(vmu = find_user(&svm, context, ext))) {
05644       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05645       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05646       ast_free(tmp);
05647       return res;
05648    }
05649    /* Setup pre-file if appropriate */
05650    if (strcmp(vmu->context, "default"))
05651       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05652    else
05653       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05654 
05655    /* Set the path to the prefile. Will be one of 
05656       VM_SPOOL_DIRcontext/ext/busy
05657       VM_SPOOL_DIRcontext/ext/unavail
05658       Depending on the flag set in options.
05659    */
05660    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05661       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05662    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05663       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05664    }
05665    /* Set the path to the tmpfile as
05666       VM_SPOOL_DIR/context/ext/temp
05667       and attempt to create the folder structure.
05668    */
05669    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05670    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05671       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05672       ast_free(tmp);
05673       return -1;
05674    }
05675    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05676    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05677       ast_copy_string(prefile, tempfile, sizeof(prefile));
05678 
05679    DISPOSE(tempfile, -1);
05680    /* It's easier just to try to make it than to check for its existence */
05681 #ifndef IMAP_STORAGE
05682    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05683 #else
05684    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05685    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05686       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05687    }
05688 #endif
05689 
05690    /* Check current or macro-calling context for special extensions */
05691    if (ast_test_flag(vmu, VM_OPERATOR)) {
05692       if (!ast_strlen_zero(vmu->exit)) {
05693          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05694             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05695             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05696             ouseexten = 1;
05697          }
05698       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05699          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05700          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05701          ouseexten = 1;
05702       } else if (!ast_strlen_zero(chan->macrocontext)
05703          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05704             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05705          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05706          ousemacro = 1;
05707       }
05708    }
05709 
05710    if (!ast_strlen_zero(vmu->exit)) {
05711       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05712          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05713          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05714       }
05715    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05716       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05717       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05718    } else if (!ast_strlen_zero(chan->macrocontext)
05719       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05720          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05721       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05722       ausemacro = 1;
05723    }
05724 
05725    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05726       for (code = alldtmf; *code; code++) {
05727          char e[2] = "";
05728          e[0] = *code;
05729          if (strchr(ecodes, e[0]) == NULL
05730             && ast_canmatch_extension(chan,
05731                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05732                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05733             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05734          }
05735       }
05736    }
05737 
05738    /* Play the beginning intro if desired */
05739    if (!ast_strlen_zero(prefile)) {
05740 #ifdef ODBC_STORAGE
05741       int success = 
05742 #endif
05743          RETRIEVE(prefile, -1, ext, context);
05744       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05745          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05746             res = ast_waitstream(chan, ecodes);
05747 #ifdef ODBC_STORAGE
05748          if (success == -1) {
05749             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05750             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05751             store_file(prefile, vmu->mailbox, vmu->context, -1);
05752          }
05753 #endif
05754       } else {
05755          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05756          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05757       }
05758       DISPOSE(prefile, -1);
05759       if (res < 0) {
05760          ast_debug(1, "Hang up during prefile playback\n");
05761          free_user(vmu);
05762          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05763          ast_free(tmp);
05764          return -1;
05765       }
05766    }
05767    if (res == '#') {
05768       /* On a '#' we skip the instructions */
05769       ast_set_flag(options, OPT_SILENT);
05770       res = 0;
05771    }
05772    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05773    if (vmu->maxmsg == 0) {
05774       if (option_debug > 2)
05775          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05776       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05777       goto leave_vm_out;
05778    }
05779    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05780       res = ast_stream_and_wait(chan, INTRO, ecodes);
05781       if (res == '#') {
05782          ast_set_flag(options, OPT_SILENT);
05783          res = 0;
05784       }
05785    }
05786    if (res > 0)
05787       ast_stopstream(chan);
05788    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05789     other than the operator -- an automated attendant or mailbox login for example */
05790    if (res == '*') {
05791       chan->exten[0] = 'a';
05792       chan->exten[1] = '\0';
05793       if (!ast_strlen_zero(vmu->exit)) {
05794          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05795       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05796          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05797       }
05798       chan->priority = 0;
05799       free_user(vmu);
05800       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05801       ast_free(tmp);
05802       return 0;
05803    }
05804 
05805    /* Check for a '0' here */
05806    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05807    transfer:
05808       if (ouseexten || ousemacro) {
05809          chan->exten[0] = 'o';
05810          chan->exten[1] = '\0';
05811          if (!ast_strlen_zero(vmu->exit)) {
05812             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05813          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05814             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05815          }
05816          ast_play_and_wait(chan, "transfer");
05817          chan->priority = 0;
05818          free_user(vmu);
05819          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05820       }
05821       ast_free(tmp);
05822       return OPERATOR_EXIT;
05823    }
05824 
05825    /* Allow all other digits to exit Voicemail and return to the dialplan */
05826    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05827       if (!ast_strlen_zero(options->exitcontext)) {
05828          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05829       }
05830       free_user(vmu);
05831       ast_free(tmp);
05832       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05833       return res;
05834    }
05835 
05836    if (res < 0) {
05837       free_user(vmu);
05838       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05839       ast_free(tmp);
05840       return -1;
05841    }
05842    /* The meat of recording the message...  All the announcements and beeps have been played*/
05843    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05844    if (!ast_strlen_zero(fmt)) {
05845       msgnum = 0;
05846 
05847 #ifdef IMAP_STORAGE
05848       /* Is ext a mailbox? */
05849       /* must open stream for this user to get info! */
05850       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05851       if (res < 0) {
05852          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05853          ast_free(tmp);
05854          return -1;
05855       }
05856       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05857       /* It is possible under certain circumstances that inboxcount did not
05858        * create a vm_state when it was needed. This is a catchall which will
05859        * rarely be used.
05860        */
05861          if (!(vms = create_vm_state_from_user(vmu))) {
05862             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05863             ast_free(tmp);
05864             return -1;
05865          }
05866       }
05867       vms->newmessages++;
05868       
05869       /* here is a big difference! We add one to it later */
05870       msgnum = newmsgs + oldmsgs;
05871       ast_debug(3, "Messagecount set to %d\n", msgnum);
05872       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05873       /* set variable for compatibility */
05874       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05875 
05876       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05877          goto leave_vm_out;
05878       }
05879 #else
05880       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05881          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05882          if (!res)
05883             res = ast_waitstream(chan, "");
05884          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05885          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05886          inprocess_count(vmu->mailbox, vmu->context, -1);
05887          goto leave_vm_out;
05888       }
05889 
05890 #endif
05891       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05892       txtdes = mkstemp(tmptxtfile);
05893       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05894       if (txtdes < 0) {
05895          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05896          if (!res)
05897             res = ast_waitstream(chan, "");
05898          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05899          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05900          inprocess_count(vmu->mailbox, vmu->context, -1);
05901          goto leave_vm_out;
05902       }
05903 
05904       /* Now play the beep once we have the message number for our next message. */
05905       if (res >= 0) {
05906          /* Unless we're *really* silent, try to send the beep */
05907          res = ast_stream_and_wait(chan, "beep", "");
05908       }
05909             
05910       /* Store information in real-time storage */
05911       if (ast_check_realtime("voicemail_data")) {
05912          snprintf(priority, sizeof(priority), "%d", chan->priority);
05913          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05914          get_date(date, sizeof(date));
05915          ast_callerid_merge(callerid, sizeof(callerid),
05916             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05917             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05918             "Unknown");
05919          ast_store_realtime("voicemail_data",
05920             "origmailbox", ext,
05921             "context", chan->context,
05922             "macrocontext", chan->macrocontext,
05923             "exten", chan->exten,
05924             "priority", priority,
05925             "callerchan", chan->name,
05926             "callerid", callerid,
05927             "origdate", date,
05928             "origtime", origtime,
05929             "category", S_OR(category, ""),
05930             "filename", tmptxtfile,
05931             SENTINEL);
05932       }
05933 
05934       /* Store information */
05935       txt = fdopen(txtdes, "w+");
05936       if (txt) {
05937          get_date(date, sizeof(date));
05938          ast_callerid_merge(callerid, sizeof(callerid),
05939             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05940             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05941             "Unknown");
05942          fprintf(txt, 
05943             ";\n"
05944             "; Message Information file\n"
05945             ";\n"
05946             "[message]\n"
05947             "origmailbox=%s\n"
05948             "context=%s\n"
05949             "macrocontext=%s\n"
05950             "exten=%s\n"
05951             "rdnis=%s\n"
05952             "priority=%d\n"
05953             "callerchan=%s\n"
05954             "callerid=%s\n"
05955             "origdate=%s\n"
05956             "origtime=%ld\n"
05957             "category=%s\n",
05958             ext,
05959             chan->context,
05960             chan->macrocontext, 
05961             chan->exten,
05962             S_COR(chan->redirecting.from.number.valid,
05963                chan->redirecting.from.number.str, "unknown"),
05964             chan->priority,
05965             chan->name,
05966             callerid,
05967             date, (long) time(NULL),
05968             category ? category : "");
05969       } else {
05970          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05971          inprocess_count(vmu->mailbox, vmu->context, -1);
05972          if (ast_check_realtime("voicemail_data")) {
05973             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05974          }
05975          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05976          goto leave_vm_out;
05977       }
05978       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05979 
05980       if (txt) {
05981          fprintf(txt, "flag=%s\n", flag);
05982          if (sound_duration < vmu->minsecs) {
05983             fclose(txt);
05984             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05985             ast_filedelete(tmptxtfile, NULL);
05986             unlink(tmptxtfile);
05987             if (ast_check_realtime("voicemail_data")) {
05988                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05989             }
05990             inprocess_count(vmu->mailbox, vmu->context, -1);
05991          } else {
05992             fprintf(txt, "duration=%d\n", duration);
05993             fclose(txt);
05994             if (vm_lock_path(dir)) {
05995                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05996                /* Delete files */
05997                ast_filedelete(tmptxtfile, NULL);
05998                unlink(tmptxtfile);
05999                inprocess_count(vmu->mailbox, vmu->context, -1);
06000             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06001                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06002                unlink(tmptxtfile);
06003                ast_unlock_path(dir);
06004                inprocess_count(vmu->mailbox, vmu->context, -1);
06005                if (ast_check_realtime("voicemail_data")) {
06006                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06007                }
06008             } else {
06009 #ifndef IMAP_STORAGE
06010                msgnum = last_message_index(vmu, dir) + 1;
06011 #endif
06012                make_file(fn, sizeof(fn), dir, msgnum);
06013 
06014                /* assign a variable with the name of the voicemail file */ 
06015 #ifndef IMAP_STORAGE
06016                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06017 #else
06018                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06019 #endif
06020 
06021                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06022                ast_filerename(tmptxtfile, fn, NULL);
06023                rename(tmptxtfile, txtfile);
06024                inprocess_count(vmu->mailbox, vmu->context, -1);
06025 
06026                /* Properly set permissions on voicemail text descriptor file.
06027                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06028                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06029                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06030 
06031                ast_unlock_path(dir);
06032                if (ast_check_realtime("voicemail_data")) {
06033                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06034                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06035                }
06036                /* We must store the file first, before copying the message, because
06037                 * ODBC storage does the entire copy with SQL.
06038                 */
06039                if (ast_fileexists(fn, NULL, NULL) > 0) {
06040                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06041                }
06042 
06043                /* Are there to be more recipients of this message? */
06044                while (tmpptr) {
06045                   struct ast_vm_user recipu, *recip;
06046                   char *exten, *cntx;
06047 
06048                   exten = strsep(&tmpptr, "&");
06049                   cntx = strchr(exten, '@');
06050                   if (cntx) {
06051                      *cntx = '\0';
06052                      cntx++;
06053                   }
06054                   if ((recip = find_user(&recipu, cntx, exten))) {
06055                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06056                      free_user(recip);
06057                   }
06058                }
06059 #ifndef IMAP_STORAGE
06060                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06061                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06062                   char sfn[PATH_MAX];
06063                   char dfn[PATH_MAX];
06064                   int x;
06065                   /* It's easier just to try to make it than to check for its existence */
06066                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06067                   x = last_message_index(vmu, urgdir) + 1;
06068                   make_file(sfn, sizeof(sfn), dir, msgnum);
06069                   make_file(dfn, sizeof(dfn), urgdir, x);
06070                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06071                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06072                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06073                   ast_copy_string(fn, dfn, sizeof(fn));
06074                   msgnum = x;
06075                }
06076 #endif
06077                /* Notification needs to happen after the copy, though. */
06078                if (ast_fileexists(fn, NULL, NULL)) {
06079 #ifdef IMAP_STORAGE
06080                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06081                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06082                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06083                      flag);
06084 #else
06085                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06086                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06087                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06088                      flag);
06089 #endif
06090                }
06091 
06092                /* Disposal needs to happen after the optional move and copy */
06093                if (ast_fileexists(fn, NULL, NULL)) {
06094                   DISPOSE(dir, msgnum);
06095                }
06096             }
06097          }
06098       } else {
06099          inprocess_count(vmu->mailbox, vmu->context, -1);
06100       }
06101       if (res == '0') {
06102          goto transfer;
06103       } else if (res > 0 && res != 't')
06104          res = 0;
06105 
06106       if (sound_duration < vmu->minsecs)
06107          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06108          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06109       else
06110          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06111    } else
06112       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06113 leave_vm_out:
06114    free_user(vmu);
06115 
06116 #ifdef IMAP_STORAGE
06117    /* expunge message - use UID Expunge if supported on IMAP server*/
06118    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06119    if (expungeonhangup == 1) {
06120       ast_mutex_lock(&vms->lock);
06121 #ifdef HAVE_IMAP_TK2006
06122       if (LEVELUIDPLUS (vms->mailstream)) {
06123          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06124       } else 
06125 #endif
06126          mail_expunge(vms->mailstream);
06127       ast_mutex_unlock(&vms->lock);
06128    }
06129 #endif
06130 
06131    ast_free(tmp);
06132    return res;
06133 }
06134 
06135 #if !defined(IMAP_STORAGE)
06136 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06137 {
06138    /* we know the actual number of messages, so stop process when number is hit */
06139 
06140    int x, dest;
06141    char sfn[PATH_MAX];
06142    char dfn[PATH_MAX];
06143 
06144    if (vm_lock_path(dir)) {
06145       return ERROR_LOCK_PATH;
06146    }
06147 
06148    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06149       make_file(sfn, sizeof(sfn), dir, x);
06150       if (EXISTS(dir, x, sfn, NULL)) {
06151 
06152          if (x != dest) {
06153             make_file(dfn, sizeof(dfn), dir, dest);
06154             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06155          }
06156 
06157          dest++;
06158       }
06159    }
06160    ast_unlock_path(dir);
06161 
06162    return dest;
06163 }
06164 #endif
06165 
06166 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06167 {
06168    int d;
06169    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06170    return d;
06171 }
06172 
06173 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06174 {
06175 #ifdef IMAP_STORAGE
06176    /* we must use mbox(x) folder names, and copy the message there */
06177    /* simple. huh? */
06178    char sequence[10];
06179    char mailbox[256];
06180    int res;
06181 
06182    /* get the real IMAP message number for this message */
06183    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06184    
06185    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06186    ast_mutex_lock(&vms->lock);
06187    /* if save to Old folder, put in INBOX as read */
06188    if (box == OLD_FOLDER) {
06189       mail_setflag(vms->mailstream, sequence, "\\Seen");
06190       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06191    } else if (box == NEW_FOLDER) {
06192       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06193       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06194    }
06195    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06196       ast_mutex_unlock(&vms->lock);
06197       return 0;
06198    }
06199    /* Create the folder if it don't exist */
06200    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06201    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06202    if (mail_create(vms->mailstream, mailbox) == NIL) 
06203       ast_debug(5, "Folder exists.\n");
06204    else
06205       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06206    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06207    ast_mutex_unlock(&vms->lock);
06208    return res;
06209 #else
06210    char *dir = vms->curdir;
06211    char *username = vms->username;
06212    char *context = vmu->context;
06213    char sfn[PATH_MAX];
06214    char dfn[PATH_MAX];
06215    char ddir[PATH_MAX];
06216    const char *dbox = mbox(vmu, box);
06217    int x, i;
06218    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06219 
06220    if (vm_lock_path(ddir))
06221       return ERROR_LOCK_PATH;
06222 
06223    x = last_message_index(vmu, ddir) + 1;
06224 
06225    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06226       x--;
06227       for (i = 1; i <= x; i++) {
06228          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06229          make_file(sfn, sizeof(sfn), ddir, i);
06230          make_file(dfn, sizeof(dfn), ddir, i - 1);
06231          if (EXISTS(ddir, i, sfn, NULL)) {
06232             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06233          } else
06234             break;
06235       }
06236    } else {
06237       if (x >= vmu->maxmsg) {
06238          ast_unlock_path(ddir);
06239          return -1;
06240       }
06241    }
06242    make_file(sfn, sizeof(sfn), dir, msg);
06243    make_file(dfn, sizeof(dfn), ddir, x);
06244    if (strcmp(sfn, dfn)) {
06245       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06246    }
06247    ast_unlock_path(ddir);
06248 #endif
06249    return 0;
06250 }
06251 
06252 static int adsi_logo(unsigned char *buf)
06253 {
06254    int bytes = 0;
06255    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06256    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06257    return bytes;
06258 }
06259 
06260 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06261 {
06262    unsigned char buf[256];
06263    int bytes = 0;
06264    int x;
06265    char num[5];
06266 
06267    *useadsi = 0;
06268    bytes += ast_adsi_data_mode(buf + bytes);
06269    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06270 
06271    bytes = 0;
06272    bytes += adsi_logo(buf);
06273    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06274 #ifdef DISPLAY
06275    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06276 #endif
06277    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06278    bytes += ast_adsi_data_mode(buf + bytes);
06279    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06280 
06281    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06282       bytes = 0;
06283       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06284       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06285       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06286       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06287       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06288       return 0;
06289    }
06290 
06291 #ifdef DISPLAY
06292    /* Add a dot */
06293    bytes = 0;
06294    bytes += ast_adsi_logo(buf);
06295    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06296    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
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    bytes = 0;
06301    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06302    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06303    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06304    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06305    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06306    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06307    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06308 
06309 #ifdef DISPLAY
06310    /* Add another dot */
06311    bytes = 0;
06312    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06313    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06314 
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    /* These buttons we load but don't use yet */
06321    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06322    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06323    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06324    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06325    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06326    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06327    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06328 
06329 #ifdef DISPLAY
06330    /* Add another dot */
06331    bytes = 0;
06332    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06333    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06334    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06335 #endif
06336 
06337    bytes = 0;
06338    for (x = 0; x < 5; x++) {
06339       snprintf(num, sizeof(num), "%d", x);
06340       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06341    }
06342    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06343    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06344 
06345 #ifdef DISPLAY
06346    /* Add another dot */
06347    bytes = 0;
06348    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06349    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06350    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06351 #endif
06352 
06353    if (ast_adsi_end_download(chan)) {
06354       bytes = 0;
06355       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06356       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06357       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06358       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06359       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06360       return 0;
06361    }
06362    bytes = 0;
06363    bytes += ast_adsi_download_disconnect(buf + bytes);
06364    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06365    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06366 
06367    ast_debug(1, "Done downloading scripts...\n");
06368 
06369 #ifdef DISPLAY
06370    /* Add last dot */
06371    bytes = 0;
06372    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06373    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06374 #endif
06375    ast_debug(1, "Restarting session...\n");
06376 
06377    bytes = 0;
06378    /* Load the session now */
06379    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06380       *useadsi = 1;
06381       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06382    } else
06383       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06384 
06385    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06386    return 0;
06387 }
06388 
06389 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06390 {
06391    int x;
06392    if (!ast_adsi_available(chan))
06393       return;
06394    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06395    if (x < 0)
06396       return;
06397    if (!x) {
06398       if (adsi_load_vmail(chan, useadsi)) {
06399          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06400          return;
06401       }
06402    } else
06403       *useadsi = 1;
06404 }
06405 
06406 static void adsi_login(struct ast_channel *chan)
06407 {
06408    unsigned char buf[256];
06409    int bytes = 0;
06410    unsigned char keys[8];
06411    int x;
06412    if (!ast_adsi_available(chan))
06413       return;
06414 
06415    for (x = 0; x < 8; x++)
06416       keys[x] = 0;
06417    /* Set one key for next */
06418    keys[3] = ADSI_KEY_APPS + 3;
06419 
06420    bytes += adsi_logo(buf + bytes);
06421    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06422    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06423    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06424    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06425    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06426    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06427    bytes += ast_adsi_set_keys(buf + bytes, keys);
06428    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06429    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06430 }
06431 
06432 static void adsi_password(struct ast_channel *chan)
06433 {
06434    unsigned char buf[256];
06435    int bytes = 0;
06436    unsigned char keys[8];
06437    int x;
06438    if (!ast_adsi_available(chan))
06439       return;
06440 
06441    for (x = 0; x < 8; x++)
06442       keys[x] = 0;
06443    /* Set one key for next */
06444    keys[3] = ADSI_KEY_APPS + 3;
06445 
06446    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06447    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06448    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06449    bytes += ast_adsi_set_keys(buf + bytes, keys);
06450    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06451    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06452 }
06453 
06454 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06455 {
06456    unsigned char buf[256];
06457    int bytes = 0;
06458    unsigned char keys[8];
06459    int x, y;
06460 
06461    if (!ast_adsi_available(chan))
06462       return;
06463 
06464    for (x = 0; x < 5; x++) {
06465       y = ADSI_KEY_APPS + 12 + start + x;
06466       if (y > ADSI_KEY_APPS + 12 + 4)
06467          y = 0;
06468       keys[x] = ADSI_KEY_SKT | y;
06469    }
06470    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06471    keys[6] = 0;
06472    keys[7] = 0;
06473 
06474    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06475    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06476    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06477    bytes += ast_adsi_set_keys(buf + bytes, keys);
06478    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06479 
06480    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06481 }
06482 
06483 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06484 {
06485    int bytes = 0;
06486    unsigned char buf[256]; 
06487    char buf1[256], buf2[256];
06488    char fn2[PATH_MAX];
06489 
06490    char cid[256] = "";
06491    char *val;
06492    char *name, *num;
06493    char datetime[21] = "";
06494    FILE *f;
06495 
06496    unsigned char keys[8];
06497 
06498    int x;
06499 
06500    if (!ast_adsi_available(chan))
06501       return;
06502 
06503    /* Retrieve important info */
06504    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06505    f = fopen(fn2, "r");
06506    if (f) {
06507       while (!feof(f)) {   
06508          if (!fgets((char *) buf, sizeof(buf), f)) {
06509             continue;
06510          }
06511          if (!feof(f)) {
06512             char *stringp = NULL;
06513             stringp = (char *) buf;
06514             strsep(&stringp, "=");
06515             val = strsep(&stringp, "=");
06516             if (!ast_strlen_zero(val)) {
06517                if (!strcmp((char *) buf, "callerid"))
06518                   ast_copy_string(cid, val, sizeof(cid));
06519                if (!strcmp((char *) buf, "origdate"))
06520                   ast_copy_string(datetime, val, sizeof(datetime));
06521             }
06522          }
06523       }
06524       fclose(f);
06525    }
06526    /* New meaning for keys */
06527    for (x = 0; x < 5; x++)
06528       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06529    keys[6] = 0x0;
06530    keys[7] = 0x0;
06531 
06532    if (!vms->curmsg) {
06533       /* No prev key, provide "Folder" instead */
06534       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06535    }
06536    if (vms->curmsg >= vms->lastmsg) {
06537       /* If last message ... */
06538       if (vms->curmsg) {
06539          /* but not only message, provide "Folder" instead */
06540          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06541          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06542 
06543       } else {
06544          /* Otherwise if only message, leave blank */
06545          keys[3] = 1;
06546       }
06547    }
06548 
06549    if (!ast_strlen_zero(cid)) {
06550       ast_callerid_parse(cid, &name, &num);
06551       if (!name)
06552          name = num;
06553    } else
06554       name = "Unknown Caller";
06555 
06556    /* If deleted, show "undeleted" */
06557 #ifdef IMAP_STORAGE
06558    ast_mutex_lock(&vms->lock);
06559 #endif
06560    if (vms->deleted[vms->curmsg]) {
06561       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06562    }
06563 #ifdef IMAP_STORAGE
06564    ast_mutex_unlock(&vms->lock);
06565 #endif
06566 
06567    /* Except "Exit" */
06568    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06569    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06570       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06571    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06572 
06573    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06574    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06575    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06576    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06577    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06578    bytes += ast_adsi_set_keys(buf + bytes, keys);
06579    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06580 
06581    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06582 }
06583 
06584 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06585 {
06586    int bytes = 0;
06587    unsigned char buf[256];
06588    unsigned char keys[8];
06589 
06590    int x;
06591 
06592    if (!ast_adsi_available(chan))
06593       return;
06594 
06595    /* New meaning for keys */
06596    for (x = 0; x < 5; x++)
06597       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06598 
06599    keys[6] = 0x0;
06600    keys[7] = 0x0;
06601 
06602    if (!vms->curmsg) {
06603       /* No prev key, provide "Folder" instead */
06604       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06605    }
06606    if (vms->curmsg >= vms->lastmsg) {
06607       /* If last message ... */
06608       if (vms->curmsg) {
06609          /* but not only message, provide "Folder" instead */
06610          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06611       } else {
06612          /* Otherwise if only message, leave blank */
06613          keys[3] = 1;
06614       }
06615    }
06616 
06617    /* If deleted, show "undeleted" */
06618 #ifdef IMAP_STORAGE
06619    ast_mutex_lock(&vms->lock);
06620 #endif
06621    if (vms->deleted[vms->curmsg]) {
06622       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06623    }
06624 #ifdef IMAP_STORAGE
06625    ast_mutex_unlock(&vms->lock);
06626 #endif
06627 
06628    /* Except "Exit" */
06629    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06630    bytes += ast_adsi_set_keys(buf + bytes, keys);
06631    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06632 
06633    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06634 }
06635 
06636 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06637 {
06638    unsigned char buf[256] = "";
06639    char buf1[256] = "", buf2[256] = "";
06640    int bytes = 0;
06641    unsigned char keys[8];
06642    int x;
06643 
06644    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06645    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06646    if (!ast_adsi_available(chan))
06647       return;
06648    if (vms->newmessages) {
06649       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06650       if (vms->oldmessages) {
06651          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06652          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06653       } else {
06654          snprintf(buf2, sizeof(buf2), "%s.", newm);
06655       }
06656    } else if (vms->oldmessages) {
06657       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06658       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06659    } else {
06660       strcpy(buf1, "You have no messages.");
06661       buf2[0] = ' ';
06662       buf2[1] = '\0';
06663    }
06664    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06665    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06666    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06667 
06668    for (x = 0; x < 6; x++)
06669       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06670    keys[6] = 0;
06671    keys[7] = 0;
06672 
06673    /* Don't let them listen if there are none */
06674    if (vms->lastmsg < 0)
06675       keys[0] = 1;
06676    bytes += ast_adsi_set_keys(buf + bytes, keys);
06677 
06678    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06679 
06680    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06681 }
06682 
06683 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06684 {
06685    unsigned char buf[256] = "";
06686    char buf1[256] = "", buf2[256] = "";
06687    int bytes = 0;
06688    unsigned char keys[8];
06689    int x;
06690 
06691    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06692 
06693    if (!ast_adsi_available(chan))
06694       return;
06695 
06696    /* Original command keys */
06697    for (x = 0; x < 6; x++)
06698       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06699 
06700    keys[6] = 0;
06701    keys[7] = 0;
06702 
06703    if ((vms->lastmsg + 1) < 1)
06704       keys[0] = 0;
06705 
06706    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06707       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06708 
06709    if (vms->lastmsg + 1)
06710       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06711    else
06712       strcpy(buf2, "no messages.");
06713    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06714    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06715    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06716    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06717    bytes += ast_adsi_set_keys(buf + bytes, keys);
06718 
06719    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06720 
06721    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06722    
06723 }
06724 
06725 /*
06726 static void adsi_clear(struct ast_channel *chan)
06727 {
06728    char buf[256];
06729    int bytes=0;
06730    if (!ast_adsi_available(chan))
06731       return;
06732    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06733    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06734 
06735    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06736 }
06737 */
06738 
06739 static void adsi_goodbye(struct ast_channel *chan)
06740 {
06741    unsigned char buf[256];
06742    int bytes = 0;
06743 
06744    if (!ast_adsi_available(chan))
06745       return;
06746    bytes += adsi_logo(buf + bytes);
06747    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06748    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06749    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06750    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06751 
06752    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06753 }
06754 
06755 /*!\brief get_folder: Folder menu
06756  * Plays "press 1 for INBOX messages" etc.
06757  * Should possibly be internationalized
06758  */
06759 static int get_folder(struct ast_channel *chan, int start)
06760 {
06761    int x;
06762    int d;
06763    char fn[PATH_MAX];
06764    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06765    if (d)
06766       return d;
06767    for (x = start; x < 5; x++) { /* For all folders */
06768       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06769          return d;
06770       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06771       if (d)
06772          return d;
06773       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06774 
06775       /* The inbox folder can have its name changed under certain conditions
06776        * so this checks if the sound file exists for the inbox folder name and
06777        * if it doesn't, plays the default name instead. */
06778       if (x == 0) {
06779          if (ast_fileexists(fn, NULL, NULL)) {
06780             d = vm_play_folder_name(chan, fn);
06781          } else {
06782             ast_verb(1, "failed to find %s\n", fn);
06783             d = vm_play_folder_name(chan, "vm-INBOX");
06784          }
06785       } else {
06786          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06787          d = vm_play_folder_name(chan, fn);
06788       }
06789 
06790       if (d)
06791          return d;
06792       d = ast_waitfordigit(chan, 500);
06793       if (d)
06794          return d;
06795    }
06796 
06797    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06798    if (d)
06799       return d;
06800    d = ast_waitfordigit(chan, 4000);
06801    return d;
06802 }
06803 
06804 /*!
06805  * \brief plays a prompt and waits for a keypress.
06806  * \param chan
06807  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06808  * \param start Does not appear to be used at this time.
06809  *
06810  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06811  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06812  * prompting for the number inputs that correspond to the available folders.
06813  * 
06814  * \return zero on success, or -1 on error.
06815  */
06816 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06817 {
06818    int res = 0;
06819    int loops = 0;
06820 
06821    res = ast_play_and_wait(chan, fn);  /* Folder name */
06822    while (((res < '0') || (res > '9')) &&
06823          (res != '#') && (res >= 0) &&
06824          loops < 4) {
06825       res = get_folder(chan, 0);
06826       loops++;
06827    }
06828    if (loops == 4) { /* give up */
06829       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06830       return '#';
06831    }
06832    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06833    return res;
06834 }
06835 
06836 /*!
06837  * \brief presents the option to prepend to an existing message when forwarding it.
06838  * \param chan
06839  * \param vmu
06840  * \param curdir
06841  * \param curmsg
06842  * \param vm_fmts
06843  * \param context
06844  * \param record_gain
06845  * \param duration
06846  * \param vms
06847  * \param flag 
06848  *
06849  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06850  *
06851  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06852  * \return zero on success, -1 on error.
06853  */
06854 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06855          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06856 {
06857    int cmd = 0;
06858    int retries = 0, prepend_duration = 0, already_recorded = 0;
06859    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06860    char textfile[PATH_MAX];
06861    struct ast_config *msg_cfg;
06862    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06863 #ifndef IMAP_STORAGE
06864    signed char zero_gain = 0;
06865 #endif
06866    const char *duration_str;
06867 
06868    /* Must always populate duration correctly */
06869    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06870    strcpy(textfile, msgfile);
06871    strcpy(backup, msgfile);
06872    strcpy(backup_textfile, msgfile);
06873    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06874    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06875    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06876 
06877    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06878       *duration = atoi(duration_str);
06879    } else {
06880       *duration = 0;
06881    }
06882 
06883    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06884       if (cmd)
06885          retries = 0;
06886       switch (cmd) {
06887       case '1': 
06888 
06889 #ifdef IMAP_STORAGE
06890          /* Record new intro file */
06891          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06892          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06893          ast_play_and_wait(chan, INTRO);
06894          ast_play_and_wait(chan, "beep");
06895          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06896          if (cmd == -1) {
06897             break;
06898          }
06899          cmd = 't';
06900 #else
06901 
06902          /* prepend a message to the current message, update the metadata and return */
06903 
06904          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06905          strcpy(textfile, msgfile);
06906          strncat(textfile, ".txt", sizeof(textfile) - 1);
06907          *duration = 0;
06908 
06909          /* if we can't read the message metadata, stop now */
06910          if (!msg_cfg) {
06911             cmd = 0;
06912             break;
06913          }
06914 
06915          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06916 #ifndef IMAP_STORAGE
06917          if (already_recorded) {
06918             ast_filecopy(backup, msgfile, NULL);
06919             copy(backup_textfile, textfile);
06920          }
06921          else {
06922             ast_filecopy(msgfile, backup, NULL);
06923             copy(textfile, backup_textfile);
06924          }
06925 #endif
06926          already_recorded = 1;
06927 
06928          if (record_gain)
06929             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06930 
06931          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06932 
06933          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06934             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06935             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06936             ast_filerename(backup, msgfile, NULL);
06937          }
06938 
06939          if (record_gain)
06940             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06941 
06942          
06943          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06944             *duration = atoi(duration_str);
06945 
06946          if (prepend_duration) {
06947             struct ast_category *msg_cat;
06948             /* need enough space for a maximum-length message duration */
06949             char duration_buf[12];
06950 
06951             *duration += prepend_duration;
06952             msg_cat = ast_category_get(msg_cfg, "message");
06953             snprintf(duration_buf, 11, "%ld", *duration);
06954             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06955                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06956             }
06957          }
06958 
06959 #endif
06960          break;
06961       case '2': 
06962          /* NULL out introfile so we know there is no intro! */
06963 #ifdef IMAP_STORAGE
06964          *vms->introfn = '\0';
06965 #endif
06966          cmd = 't';
06967          break;
06968       case '*':
06969          cmd = '*';
06970          break;
06971       default: 
06972          /* If time_out and return to menu, reset already_recorded */
06973          already_recorded = 0;
06974 
06975          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06976             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06977          if (!cmd) {
06978             cmd = ast_play_and_wait(chan, "vm-starmain");
06979             /* "press star to return to the main menu" */
06980          }
06981          if (!cmd) {
06982             cmd = ast_waitfordigit(chan, 6000);
06983          }
06984          if (!cmd) {
06985             retries++;
06986          }
06987          if (retries > 3) {
06988             cmd = '*'; /* Let's cancel this beast */
06989          }
06990          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06991       }
06992    }
06993 
06994    if (msg_cfg)
06995       ast_config_destroy(msg_cfg);
06996    if (prepend_duration)
06997       *duration = prepend_duration;
06998 
06999    if (already_recorded && cmd == -1) {
07000       /* restore original message if prepention cancelled */
07001       ast_filerename(backup, msgfile, NULL);
07002       rename(backup_textfile, textfile);
07003    }
07004 
07005    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07006       cmd = 0;
07007    return cmd;
07008 }
07009 
07010 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07011 {
07012    struct ast_event *event;
07013    char *mailbox, *context;
07014 
07015    /* Strip off @default */
07016    context = mailbox = ast_strdupa(box);
07017    strsep(&context, "@");
07018    if (ast_strlen_zero(context))
07019       context = "default";
07020 
07021    if (!(event = ast_event_new(AST_EVENT_MWI,
07022          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07023          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07024          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07025          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07026          AST_EVENT_IE_END))) {
07027       return;
07028    }
07029 
07030    ast_event_queue_and_cache(event);
07031 }
07032 
07033 /*!
07034  * \brief Sends email notification that a user has a new voicemail waiting for them.
07035  * \param chan
07036  * \param vmu
07037  * \param vms
07038  * \param msgnum
07039  * \param duration
07040  * \param fmt
07041  * \param cidnum The Caller ID phone number value.
07042  * \param cidname The Caller ID name value.
07043  * \param flag
07044  *
07045  * \return zero on success, -1 on error.
07046  */
07047 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)
07048 {
07049    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07050    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07051    const char *category;
07052    char *myserveremail = serveremail;
07053 
07054    ast_channel_lock(chan);
07055    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07056       category = ast_strdupa(category);
07057    }
07058    ast_channel_unlock(chan);
07059 
07060 #ifndef IMAP_STORAGE
07061    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07062 #else
07063    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07064 #endif
07065    make_file(fn, sizeof(fn), todir, msgnum);
07066    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07067 
07068    if (!ast_strlen_zero(vmu->attachfmt)) {
07069       if (strstr(fmt, vmu->attachfmt))
07070          fmt = vmu->attachfmt;
07071       else
07072          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);
07073    }
07074 
07075    /* Attach only the first format */
07076    fmt = ast_strdupa(fmt);
07077    stringp = fmt;
07078    strsep(&stringp, "|");
07079 
07080    if (!ast_strlen_zero(vmu->serveremail))
07081       myserveremail = vmu->serveremail;
07082 
07083    if (!ast_strlen_zero(vmu->email)) {
07084       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07085 
07086       if (attach_user_voicemail)
07087          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07088 
07089       /* XXX possible imap issue, should category be NULL XXX */
07090       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07091 
07092       if (attach_user_voicemail)
07093          DISPOSE(todir, msgnum);
07094    }
07095 
07096    if (!ast_strlen_zero(vmu->pager)) {
07097       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07098    }
07099 
07100    if (ast_test_flag(vmu, VM_DELETE))
07101       DELETE(todir, msgnum, fn, vmu);
07102 
07103    /* Leave voicemail for someone */
07104    if (ast_app_has_voicemail(ext_context, NULL)) 
07105       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07106 
07107    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07108 
07109    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);
07110    run_externnotify(vmu->context, vmu->mailbox, flag);
07111 
07112 #ifdef IMAP_STORAGE
07113    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07114    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07115       vm_imap_delete(NULL, vms->curmsg, vmu);
07116       vms->newmessages--;  /* Fix new message count */
07117    }
07118 #endif
07119 
07120    return 0;
07121 }
07122 
07123 /*!
07124  * \brief Sends a voicemail message to a mailbox recipient.
07125  * \param chan
07126  * \param context
07127  * \param vms
07128  * \param sender
07129  * \param fmt
07130  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07131  *             Will be 0 when called to forward an existing message (option 8)
07132  *             Will be 1 when called to leave a message (option 3->5)
07133  * \param record_gain 
07134  * \param urgent
07135  *
07136  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07137  * 
07138  * When in the leave message mode (is_new_message == 1):
07139  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07140  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07141  *
07142  * When in the forward message mode (is_new_message == 0):
07143  *   - retreives the current message to be forwarded
07144  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07145  *   - determines the target mailbox and folders
07146  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07147  *
07148  * \return zero on success, -1 on error.
07149  */
07150 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)
07151 {
07152 #ifdef IMAP_STORAGE
07153    int todircount = 0;
07154    struct vm_state *dstvms;
07155 #endif
07156    char username[70]="";
07157    char fn[PATH_MAX]; /* for playback of name greeting */
07158    char ecodes[16] = "#";
07159    int res = 0, cmd = 0;
07160    struct ast_vm_user *receiver = NULL, *vmtmp;
07161    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07162    char *stringp;
07163    const char *s;
07164    int saved_messages = 0;
07165    int valid_extensions = 0;
07166    char *dir;
07167    int curmsg;
07168    char urgent_str[7] = "";
07169    int prompt_played = 0;
07170 #ifndef IMAP_STORAGE
07171    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07172 #endif
07173    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07174       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07175    }
07176 
07177    if (vms == NULL) return -1;
07178    dir = vms->curdir;
07179    curmsg = vms->curmsg;
07180 
07181    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07182    while (!res && !valid_extensions) {
07183       int use_directory = 0;
07184       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07185          int done = 0;
07186          int retries = 0;
07187          cmd = 0;
07188          while ((cmd >= 0) && !done ){
07189             if (cmd)
07190                retries = 0;
07191             switch (cmd) {
07192             case '1': 
07193                use_directory = 0;
07194                done = 1;
07195                break;
07196             case '2': 
07197                use_directory = 1;
07198                done = 1;
07199                break;
07200             case '*': 
07201                cmd = 't';
07202                done = 1;
07203                break;
07204             default: 
07205                /* Press 1 to enter an extension press 2 to use the directory */
07206                cmd = ast_play_and_wait(chan, "vm-forward");
07207                if (!cmd) {
07208                   cmd = ast_waitfordigit(chan, 3000);
07209                }
07210                if (!cmd) {
07211                   retries++;
07212                }
07213                if (retries > 3) {
07214                   cmd = 't';
07215                   done = 1;
07216                }
07217                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07218             }
07219          }
07220          if (cmd < 0 || cmd == 't')
07221             break;
07222       }
07223       
07224       if (use_directory) {
07225          /* use app_directory */
07226          
07227          char old_context[sizeof(chan->context)];
07228          char old_exten[sizeof(chan->exten)];
07229          int old_priority;
07230          struct ast_app* directory_app;
07231 
07232          directory_app = pbx_findapp("Directory");
07233          if (directory_app) {
07234             char vmcontext[256];
07235             /* make backup copies */
07236             memcpy(old_context, chan->context, sizeof(chan->context));
07237             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07238             old_priority = chan->priority;
07239             
07240             /* call the the Directory, changes the channel */
07241             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07242             res = pbx_exec(chan, directory_app, vmcontext);
07243             
07244             ast_copy_string(username, chan->exten, sizeof(username));
07245             
07246             /* restore the old context, exten, and priority */
07247             memcpy(chan->context, old_context, sizeof(chan->context));
07248             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07249             chan->priority = old_priority;
07250          } else {
07251             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07252             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07253          }
07254       } else {
07255          /* Ask for an extension */
07256          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07257          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07258          prompt_played++;
07259          if (res || prompt_played > 4)
07260             break;
07261          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07262             break;
07263       }
07264       
07265       /* start all over if no username */
07266       if (ast_strlen_zero(username))
07267          continue;
07268       stringp = username;
07269       s = strsep(&stringp, "*");
07270       /* start optimistic */
07271       valid_extensions = 1;
07272       while (s) {
07273          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07274             int oldmsgs;
07275             int newmsgs;
07276             int capacity;
07277             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07278                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07279                /* Shouldn't happen, but allow trying another extension if it does */
07280                res = ast_play_and_wait(chan, "pbx-invalid");
07281                valid_extensions = 0;
07282                break;
07283             }
07284             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07285             if ((newmsgs + oldmsgs) >= capacity) {
07286                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07287                res = ast_play_and_wait(chan, "vm-mailboxfull");
07288                valid_extensions = 0;
07289                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07290                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07291                   free_user(vmtmp);
07292                }
07293                inprocess_count(receiver->mailbox, receiver->context, -1);
07294                break;
07295             }
07296             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07297          } else {
07298             /* XXX Optimization for the future.  When we encounter a single bad extension,
07299              * bailing out on all of the extensions may not be the way to go.  We should
07300              * probably just bail on that single extension, then allow the user to enter
07301              * several more. XXX
07302              */
07303             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07304                free_user(receiver);
07305             }
07306             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07307             /* "I am sorry, that's not a valid extension.  Please try again." */
07308             res = ast_play_and_wait(chan, "pbx-invalid");
07309             valid_extensions = 0;
07310             break;
07311          }
07312 
07313          /* play name if available, else play extension number */
07314          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07315          RETRIEVE(fn, -1, s, receiver->context);
07316          if (ast_fileexists(fn, NULL, NULL) > 0) {
07317             res = ast_stream_and_wait(chan, fn, ecodes);
07318             if (res) {
07319                DISPOSE(fn, -1);
07320                return res;
07321             }
07322          } else {
07323             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07324          }
07325          DISPOSE(fn, -1);
07326 
07327          s = strsep(&stringp, "*");
07328       }
07329       /* break from the loop of reading the extensions */
07330       if (valid_extensions)
07331          break;
07332    }
07333    /* check if we're clear to proceed */
07334    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07335       return res;
07336    if (is_new_message == 1) {
07337       struct leave_vm_options leave_options;
07338       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07339       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07340 
07341       /* Send VoiceMail */
07342       memset(&leave_options, 0, sizeof(leave_options));
07343       leave_options.record_gain = record_gain;
07344       cmd = leave_voicemail(chan, mailbox, &leave_options);
07345    } else {
07346       /* Forward VoiceMail */
07347       long duration = 0;
07348       struct vm_state vmstmp;
07349       int copy_msg_result = 0;
07350       memcpy(&vmstmp, vms, sizeof(vmstmp));
07351 
07352       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07353 
07354       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07355       if (!cmd) {
07356          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07357 #ifdef IMAP_STORAGE
07358             int attach_user_voicemail;
07359             char *myserveremail = serveremail;
07360             
07361             /* get destination mailbox */
07362             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07363             if (!dstvms) {
07364                dstvms = create_vm_state_from_user(vmtmp);
07365             }
07366             if (dstvms) {
07367                init_mailstream(dstvms, 0);
07368                if (!dstvms->mailstream) {
07369                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07370                } else {
07371                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07372                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07373                }
07374             } else {
07375                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07376             }
07377             if (!ast_strlen_zero(vmtmp->serveremail))
07378                myserveremail = vmtmp->serveremail;
07379             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07380             /* NULL category for IMAP storage */
07381             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07382                dstvms->curbox,
07383                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07384                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07385                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07386                NULL, urgent_str);
07387 #else
07388             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07389 #endif
07390             saved_messages++;
07391             AST_LIST_REMOVE_CURRENT(list);
07392             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07393             free_user(vmtmp);
07394             if (res)
07395                break;
07396          }
07397          AST_LIST_TRAVERSE_SAFE_END;
07398          if (saved_messages > 0 && !copy_msg_result) {
07399             /* give confirmation that the message was saved */
07400             /* commented out since we can't forward batches yet
07401             if (saved_messages == 1)
07402                res = ast_play_and_wait(chan, "vm-message");
07403             else
07404                res = ast_play_and_wait(chan, "vm-messages");
07405             if (!res)
07406                res = ast_play_and_wait(chan, "vm-saved"); */
07407 #ifdef IMAP_STORAGE
07408             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07409             if (ast_strlen_zero(vmstmp.introfn))
07410 #endif
07411             res = ast_play_and_wait(chan, "vm-msgsaved");
07412          }
07413 #ifndef IMAP_STORAGE
07414          else {
07415             /* with IMAP, mailbox full warning played by imap_check_limits */
07416             res = ast_play_and_wait(chan, "vm-mailboxfull");
07417          }
07418          /* Restore original message without prepended message if backup exists */
07419          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07420          strcpy(textfile, msgfile);
07421          strcpy(backup, msgfile);
07422          strcpy(backup_textfile, msgfile);
07423          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07424          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07425          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07426          if (ast_fileexists(backup, NULL, NULL) > 0) {
07427             ast_filerename(backup, msgfile, NULL);
07428             rename(backup_textfile, textfile);
07429          }
07430 #endif
07431       }
07432       DISPOSE(dir, curmsg);
07433 #ifndef IMAP_STORAGE
07434       if (cmd) { /* assuming hangup, cleanup backup file */
07435          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07436          strcpy(textfile, msgfile);
07437          strcpy(backup_textfile, msgfile);
07438          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07439          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07440          rename(backup_textfile, textfile);
07441       }
07442 #endif
07443    }
07444 
07445    /* If anything failed above, we still have this list to free */
07446    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07447       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07448       free_user(vmtmp);
07449    }
07450    return res ? res : cmd;
07451 }
07452 
07453 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07454 {
07455    int res;
07456    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07457       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07458    return res;
07459 }
07460 
07461 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07462 {
07463    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07464    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);
07465 }
07466 
07467 static int play_message_category(struct ast_channel *chan, const char *category)
07468 {
07469    int res = 0;
07470 
07471    if (!ast_strlen_zero(category))
07472       res = ast_play_and_wait(chan, category);
07473 
07474    if (res) {
07475       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07476       res = 0;
07477    }
07478 
07479    return res;
07480 }
07481 
07482 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07483 {
07484    int res = 0;
07485    struct vm_zone *the_zone = NULL;
07486    time_t t;
07487 
07488    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07489       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07490       return 0;
07491    }
07492 
07493    /* Does this user have a timezone specified? */
07494    if (!ast_strlen_zero(vmu->zonetag)) {
07495       /* Find the zone in the list */
07496       struct vm_zone *z;
07497       AST_LIST_LOCK(&zones);
07498       AST_LIST_TRAVERSE(&zones, z, list) {
07499          if (!strcmp(z->name, vmu->zonetag)) {
07500             the_zone = z;
07501             break;
07502          }
07503       }
07504       AST_LIST_UNLOCK(&zones);
07505    }
07506 
07507 /* No internal variable parsing for now, so we'll comment it out for the time being */
07508 #if 0
07509    /* Set the DIFF_* variables */
07510    ast_localtime(&t, &time_now, NULL);
07511    tv_now = ast_tvnow();
07512    ast_localtime(&tv_now, &time_then, NULL);
07513 
07514    /* Day difference */
07515    if (time_now.tm_year == time_then.tm_year)
07516       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07517    else
07518       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07519    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07520 
07521    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07522 #endif
07523    if (the_zone) {
07524       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07525    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07526       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07527    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07528       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07529    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07530       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);
07531    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07532       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07533    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07534       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07535    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07536       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07537    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07538       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);
07539    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07540       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07541    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07542       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07543    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07544       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);
07545    } else {
07546       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07547    }
07548 #if 0
07549    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07550 #endif
07551    return res;
07552 }
07553 
07554 
07555 
07556 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07557 {
07558    int res = 0;
07559    int i;
07560    char *callerid, *name;
07561    char prefile[PATH_MAX] = "";
07562    
07563 
07564    /* If voicemail cid is not enabled, or we didn't get cid or context from
07565     * the attribute file, leave now.
07566     *
07567     * TODO Still need to change this so that if this function is called by the
07568     * message envelope (and someone is explicitly requesting to hear the CID),
07569     * it does not check to see if CID is enabled in the config file.
07570     */
07571    if ((cid == NULL)||(context == NULL))
07572       return res;
07573 
07574    /* Strip off caller ID number from name */
07575    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07576    ast_callerid_parse(cid, &name, &callerid);
07577    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07578       /* Check for internal contexts and only */
07579       /* say extension when the call didn't come from an internal context in the list */
07580       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07581          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07582          if ((strcmp(cidinternalcontexts[i], context) == 0))
07583             break;
07584       }
07585       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07586          if (!res) {
07587             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07588             if (!ast_strlen_zero(prefile)) {
07589             /* See if we can find a recorded name for this person instead of their extension number */
07590                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07591                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07592                   if (!callback)
07593                      res = wait_file2(chan, vms, "vm-from");
07594                   res = ast_stream_and_wait(chan, prefile, "");
07595                } else {
07596                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07597                   /* Say "from extension" as one saying to sound smoother */
07598                   if (!callback)
07599                      res = wait_file2(chan, vms, "vm-from-extension");
07600                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07601                }
07602             }
07603          }
07604       } else if (!res) {
07605          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07606          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07607          if (!callback)
07608             res = wait_file2(chan, vms, "vm-from-phonenumber");
07609          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07610       }
07611    } else {
07612       /* Number unknown */
07613       ast_debug(1, "VM-CID: From an unknown number\n");
07614       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07615       res = wait_file2(chan, vms, "vm-unknown-caller");
07616    }
07617    return res;
07618 }
07619 
07620 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07621 {
07622    int res = 0;
07623    int durationm;
07624    int durations;
07625    /* Verify that we have a duration for the message */
07626    if (duration == NULL)
07627       return res;
07628 
07629    /* Convert from seconds to minutes */
07630    durations = atoi(duration);
07631    durationm = (durations / 60);
07632 
07633    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07634 
07635    if ((!res) && (durationm >= minduration)) {
07636       res = wait_file2(chan, vms, "vm-duration");
07637 
07638       /* POLISH syntax */
07639       if (!strncasecmp(chan->language, "pl", 2)) {
07640          div_t num = div(durationm, 10);
07641 
07642          if (durationm == 1) {
07643             res = ast_play_and_wait(chan, "digits/1z");
07644             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07645          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07646             if (num.rem == 2) {
07647                if (!num.quot) {
07648                   res = ast_play_and_wait(chan, "digits/2-ie");
07649                } else {
07650                   res = say_and_wait(chan, durationm - 2 , chan->language);
07651                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07652                }
07653             } else {
07654                res = say_and_wait(chan, durationm, chan->language);
07655             }
07656             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07657          } else {
07658             res = say_and_wait(chan, durationm, chan->language);
07659             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07660          }
07661       /* DEFAULT syntax */
07662       } else {
07663          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07664          res = wait_file2(chan, vms, "vm-minutes");
07665       }
07666    }
07667    return res;
07668 }
07669 
07670 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07671 {
07672    int res = 0;
07673    char filename[256], *cid;
07674    const char *origtime, *context, *category, *duration, *flag;
07675    struct ast_config *msg_cfg;
07676    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07677 
07678    vms->starting = 0;
07679    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07680    adsi_message(chan, vms);
07681    if (!vms->curmsg) {
07682       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07683    } else if (vms->curmsg == vms->lastmsg) {
07684       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07685    }
07686 
07687    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07688    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07689    msg_cfg = ast_config_load(filename, config_flags);
07690    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07691       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07692       return 0;
07693    }
07694    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07695 
07696    /* Play the word urgent if we are listening to urgent messages */
07697    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07698       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07699    }
07700 
07701    if (!res) {
07702       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07703       /* POLISH syntax */
07704       if (!strncasecmp(chan->language, "pl", 2)) {
07705          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07706             int ten, one;
07707             char nextmsg[256];
07708             ten = (vms->curmsg + 1) / 10;
07709             one = (vms->curmsg + 1) % 10;
07710 
07711             if (vms->curmsg < 20) {
07712                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07713                res = wait_file2(chan, vms, nextmsg);
07714             } else {
07715                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07716                res = wait_file2(chan, vms, nextmsg);
07717                if (one > 0) {
07718                   if (!res) {
07719                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07720                      res = wait_file2(chan, vms, nextmsg);
07721                   }
07722                }
07723             }
07724          }
07725          if (!res)
07726             res = wait_file2(chan, vms, "vm-message");
07727       /* HEBREW syntax */
07728       } else if (!strncasecmp(chan->language, "he", 2)) {
07729          if (!vms->curmsg) {
07730             res = wait_file2(chan, vms, "vm-message");
07731             res = wait_file2(chan, vms, "vm-first");
07732          } else if (vms->curmsg == vms->lastmsg) {
07733             res = wait_file2(chan, vms, "vm-message");
07734             res = wait_file2(chan, vms, "vm-last");
07735          } else {
07736             res = wait_file2(chan, vms, "vm-message");
07737             res = wait_file2(chan, vms, "vm-number");
07738             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07739          }
07740       /* VIETNAMESE syntax */
07741       } else if (!strncasecmp(chan->language, "vi", 2)) {
07742          if (!vms->curmsg) {
07743             res = wait_file2(chan, vms, "vm-message");
07744             res = wait_file2(chan, vms, "vm-first");
07745          } else if (vms->curmsg == vms->lastmsg) {
07746             res = wait_file2(chan, vms, "vm-message");
07747             res = wait_file2(chan, vms, "vm-last");
07748          } else {
07749             res = wait_file2(chan, vms, "vm-message");
07750             res = wait_file2(chan, vms, "vm-number");
07751             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07752          }
07753       } else {
07754          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07755             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07756          } else { /* DEFAULT syntax */
07757             res = wait_file2(chan, vms, "vm-message");
07758          }
07759          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07760             if (!res) {
07761                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07762                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07763             }
07764          }
07765       }
07766    }
07767 
07768    if (!msg_cfg) {
07769       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07770       return 0;
07771    }
07772 
07773    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07774       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07775       DISPOSE(vms->curdir, vms->curmsg);
07776       ast_config_destroy(msg_cfg);
07777       return 0;
07778    }
07779 
07780    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07781    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07782    category = ast_variable_retrieve(msg_cfg, "message", "category");
07783 
07784    context = ast_variable_retrieve(msg_cfg, "message", "context");
07785    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07786       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07787    if (!res) {
07788       res = play_message_category(chan, category);
07789    }
07790    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07791       res = play_message_datetime(chan, vmu, origtime, filename);
07792    }
07793    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07794       res = play_message_callerid(chan, vms, cid, context, 0);
07795    }
07796    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07797       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07798    }
07799    /* Allow pressing '1' to skip envelope / callerid */
07800    if (res == '1') {
07801       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07802       res = 0;
07803    }
07804    ast_config_destroy(msg_cfg);
07805 
07806    if (!res) {
07807       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07808 #ifdef IMAP_STORAGE
07809       ast_mutex_lock(&vms->lock);
07810 #endif
07811       vms->heard[vms->curmsg] = 1;
07812 #ifdef IMAP_STORAGE
07813       ast_mutex_unlock(&vms->lock);
07814       /*IMAP storage stores any prepended message from a forward
07815        * as a separate file from the rest of the message
07816        */
07817       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07818          wait_file(chan, vms, vms->introfn);
07819       }
07820 #endif
07821       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07822          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07823          res = 0;
07824       }
07825       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07826    }
07827    DISPOSE(vms->curdir, vms->curmsg);
07828    return res;
07829 }
07830 
07831 #ifdef IMAP_STORAGE
07832 static int imap_remove_file(char *dir, int msgnum)
07833 {
07834    char fn[PATH_MAX];
07835    char full_fn[PATH_MAX];
07836    char intro[PATH_MAX] = {0,};
07837    
07838    if (msgnum > -1) {
07839       make_file(fn, sizeof(fn), dir, msgnum);
07840       snprintf(intro, sizeof(intro), "%sintro", fn);
07841    } else
07842       ast_copy_string(fn, dir, sizeof(fn));
07843    
07844    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07845       ast_filedelete(fn, NULL);
07846       if (!ast_strlen_zero(intro)) {
07847          ast_filedelete(intro, NULL);
07848       }
07849       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07850       unlink(full_fn);
07851    }
07852    return 0;
07853 }
07854 
07855 
07856 
07857 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07858 {
07859    char *file, *filename;
07860    char *attachment;
07861    char arg[10];
07862    int i;
07863    BODY* body;
07864 
07865    file = strrchr(ast_strdupa(dir), '/');
07866    if (file) {
07867       *file++ = '\0';
07868    } else {
07869       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07870       return -1;
07871    }
07872 
07873    ast_mutex_lock(&vms->lock);
07874    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07875       mail_fetchstructure(vms->mailstream, i + 1, &body);
07876       /* We have the body, now we extract the file name of the first attachment. */
07877       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07878          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07879       } else {
07880          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07881          ast_mutex_unlock(&vms->lock);
07882          return -1;
07883       }
07884       filename = strsep(&attachment, ".");
07885       if (!strcmp(filename, file)) {
07886          sprintf(arg, "%d", i + 1);
07887          mail_setflag(vms->mailstream, arg, "\\DELETED");
07888       }
07889    }
07890    mail_expunge(vms->mailstream);
07891    ast_mutex_unlock(&vms->lock);
07892    return 0;
07893 }
07894 
07895 #elif !defined(IMAP_STORAGE)
07896 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07897 {
07898    int count_msg, last_msg;
07899 
07900    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07901 
07902    /* Rename the member vmbox HERE so that we don't try to return before
07903     * we know what's going on.
07904     */
07905    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07906 
07907    /* Faster to make the directory than to check if it exists. */
07908    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07909 
07910    /* traverses directory using readdir (or select query for ODBC) */
07911    count_msg = count_messages(vmu, vms->curdir);
07912    if (count_msg < 0) {
07913       return count_msg;
07914    } else {
07915       vms->lastmsg = count_msg - 1;
07916    }
07917 
07918    if (vm_allocate_dh(vms, vmu, count_msg)) {
07919       return -1;
07920    }
07921 
07922    /*
07923    The following test is needed in case sequencing gets messed up.
07924    There appears to be more than one way to mess up sequence, so
07925    we will not try to find all of the root causes--just fix it when
07926    detected.
07927    */
07928 
07929    if (vm_lock_path(vms->curdir)) {
07930       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07931       return ERROR_LOCK_PATH;
07932    }
07933 
07934    /* for local storage, checks directory for messages up to maxmsg limit */
07935    last_msg = last_message_index(vmu, vms->curdir);
07936    ast_unlock_path(vms->curdir);
07937 
07938    if (last_msg < -1) {
07939       return last_msg;
07940    } else if (vms->lastmsg != last_msg) {
07941       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);
07942       resequence_mailbox(vmu, vms->curdir, count_msg);
07943    }
07944 
07945    return 0;
07946 }
07947 #endif
07948 
07949 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07950 {
07951    int x = 0;
07952    int last_msg_idx = 0;
07953 
07954 #ifndef IMAP_STORAGE
07955    int res = 0, nummsg;
07956    char fn2[PATH_MAX];
07957 #endif
07958 
07959    if (vms->lastmsg <= -1) {
07960       goto done;
07961    }
07962 
07963    vms->curmsg = -1;
07964 #ifndef IMAP_STORAGE
07965    /* Get the deleted messages fixed */
07966    if (vm_lock_path(vms->curdir)) {
07967       return ERROR_LOCK_PATH;
07968    }
07969 
07970    /* update count as message may have arrived while we've got mailbox open */
07971    last_msg_idx = last_message_index(vmu, vms->curdir);
07972    if (last_msg_idx != vms->lastmsg) {
07973       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07974    }
07975 
07976    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07977    for (x = 0; x < last_msg_idx + 1; x++) {
07978       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07979          /* Save this message.  It's not in INBOX or hasn't been heard */
07980          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07981          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07982             break;
07983          }
07984          vms->curmsg++;
07985          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07986          if (strcmp(vms->fn, fn2)) {
07987             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07988          }
07989       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07990          /* Move to old folder before deleting */
07991          res = save_to_folder(vmu, vms, x, 1);
07992          if (res == ERROR_LOCK_PATH) {
07993             /* If save failed do not delete the message */
07994             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07995             vms->deleted[x] = 0;
07996             vms->heard[x] = 0;
07997             --x;
07998          }
07999       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08000          /* Move to deleted folder */
08001          res = save_to_folder(vmu, vms, x, 10);
08002          if (res == ERROR_LOCK_PATH) {
08003             /* If save failed do not delete the message */
08004             vms->deleted[x] = 0;
08005             vms->heard[x] = 0;
08006             --x;
08007          }
08008       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08009          /* If realtime storage enabled - we should explicitly delete this message,
08010          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08011          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08012          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08013             DELETE(vms->curdir, x, vms->fn, vmu);
08014          }
08015       }
08016    }
08017 
08018    /* Delete ALL remaining messages */
08019    nummsg = x - 1;
08020    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08021       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08022       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08023          DELETE(vms->curdir, x, vms->fn, vmu);
08024       }
08025    }
08026    ast_unlock_path(vms->curdir);
08027 #else /* defined(IMAP_STORAGE) */
08028    ast_mutex_lock(&vms->lock);
08029    if (vms->deleted) {
08030       /* Since we now expunge after each delete, deleting in reverse order
08031        * ensures that no reordering occurs between each step. */
08032       last_msg_idx = vms->dh_arraysize;
08033       for (x = last_msg_idx - 1; x >= 0; x--) {
08034          if (vms->deleted[x]) {
08035             ast_debug(3, "IMAP delete of %d\n", x);
08036             DELETE(vms->curdir, x, vms->fn, vmu);
08037          }
08038       }
08039    }
08040 #endif
08041 
08042 done:
08043    if (vms->deleted) {
08044       ast_free(vms->deleted);
08045       vms->deleted = NULL;
08046    }
08047    if (vms->heard) {
08048       ast_free(vms->heard);
08049       vms->heard = NULL;
08050    }
08051    vms->dh_arraysize = 0;
08052 #ifdef IMAP_STORAGE
08053    ast_mutex_unlock(&vms->lock);
08054 #endif
08055 
08056    return 0;
08057 }
08058 
08059 /* In Greek even though we CAN use a syntax like "friends messages"
08060  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08061  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08062  * syntax for the above three categories which is more elegant.
08063  */
08064 
08065 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08066 {
08067    int cmd;
08068    char *buf;
08069 
08070    buf = ast_alloca(strlen(box) + 2);
08071    strcpy(buf, box);
08072    strcat(buf, "s");
08073 
08074    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08075       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08076       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08077    } else {
08078       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08079       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08080    }
08081 }
08082 
08083 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08084 {
08085    int cmd;
08086 
08087    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08088       if (!strcasecmp(box, "vm-INBOX"))
08089          cmd = ast_play_and_wait(chan, "vm-new-e");
08090       else
08091          cmd = ast_play_and_wait(chan, "vm-old-e");
08092       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08093    } else {
08094       cmd = ast_play_and_wait(chan, "vm-messages");
08095       return cmd ? cmd : ast_play_and_wait(chan, box);
08096    }
08097 }
08098 
08099 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08100 {
08101    int cmd;
08102 
08103    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08104       cmd = ast_play_and_wait(chan, "vm-messages");
08105       return cmd ? cmd : ast_play_and_wait(chan, box);
08106    } else {
08107       cmd = ast_play_and_wait(chan, box);
08108       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08109    }
08110 }
08111 
08112 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08113 {
08114    int cmd;
08115 
08116    if (  !strncasecmp(chan->language, "it", 2) ||
08117         !strncasecmp(chan->language, "es", 2) ||
08118         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08119       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08120       return cmd ? cmd : ast_play_and_wait(chan, box);
08121    } else if (!strncasecmp(chan->language, "gr", 2)) {
08122       return vm_play_folder_name_gr(chan, box);
08123    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08124       return ast_play_and_wait(chan, box);
08125    } else if (!strncasecmp(chan->language, "pl", 2)) {
08126       return vm_play_folder_name_pl(chan, box);
08127    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08128       return vm_play_folder_name_ua(chan, box);
08129    } else if (!strncasecmp(chan->language, "vi", 2)) {
08130       return ast_play_and_wait(chan, box);
08131    } else {  /* Default English */
08132       cmd = ast_play_and_wait(chan, box);
08133       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08134    }
08135 }
08136 
08137 /* GREEK SYNTAX
08138    In greek the plural for old/new is
08139    different so we need the following files
08140    We also need vm-denExeteMynhmata because
08141    this syntax is different.
08142 
08143    -> vm-Olds.wav : "Palia"
08144    -> vm-INBOXs.wav : "Nea"
08145    -> vm-denExeteMynhmata : "den exete mynhmata"
08146 */
08147 
08148 
08149 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08150 {
08151    int res = 0;
08152 
08153    if (vms->newmessages) {
08154       res = ast_play_and_wait(chan, "vm-youhave");
08155       if (!res) 
08156          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08157       if (!res) {
08158          if ((vms->newmessages == 1)) {
08159             res = ast_play_and_wait(chan, "vm-INBOX");
08160             if (!res)
08161                res = ast_play_and_wait(chan, "vm-message");
08162          } else {
08163             res = ast_play_and_wait(chan, "vm-INBOXs");
08164             if (!res)
08165                res = ast_play_and_wait(chan, "vm-messages");
08166          }
08167       }
08168    } else if (vms->oldmessages){
08169       res = ast_play_and_wait(chan, "vm-youhave");
08170       if (!res)
08171          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08172       if ((vms->oldmessages == 1)){
08173          res = ast_play_and_wait(chan, "vm-Old");
08174          if (!res)
08175             res = ast_play_and_wait(chan, "vm-message");
08176       } else {
08177          res = ast_play_and_wait(chan, "vm-Olds");
08178          if (!res)
08179             res = ast_play_and_wait(chan, "vm-messages");
08180       }
08181    } else if (!vms->oldmessages && !vms->newmessages) 
08182       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08183    return res;
08184 }
08185 
08186 /* Version of vm_intro() designed to work for many languages.
08187  *
08188  * It is hoped that this function can prevent the proliferation of 
08189  * language-specific vm_intro() functions and in time replace the language-
08190  * specific functions which already exist.  An examination of the language-
08191  * specific functions revealed that they all corrected the same deficiencies
08192  * in vm_intro_en() (which was the default function). Namely:
08193  *
08194  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08195  *     wording of the voicemail greeting hides this problem.  For example,
08196  *     vm-INBOX contains only the word "new".  This means that both of these
08197  *     sequences produce valid utterances:
08198  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08199  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08200  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08201  *     in many languages) the first utterance becomes "you have 1 the new message".
08202  *  2) The function contains hardcoded rules for pluralizing the word "message".
08203  *     These rules are correct for English, but not for many other languages.
08204  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08205  *     required in many languages.
08206  *  4) The gender of the word for "message" is not specified. This is a problem
08207  *     because in many languages the gender of the number in phrases such
08208  *     as "you have one new message" must match the gender of the word
08209  *     meaning "message".
08210  *
08211  * Fixing these problems for each new language has meant duplication of effort.
08212  * This new function solves the problems in the following general ways:
08213  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08214  *     and vm-Old respectively for those languages where it makes sense.
08215  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08216  *     on vm-message.
08217  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08218  *     prefix on vm-new and vm-old (none for English).
08219  *  4) Pass the gender of the language's word for "message" as an agument to
08220  *     this function which is can in turn pass on to the functions which 
08221  *     say numbers and put endings on nounds and adjectives.
08222  *
08223  * All languages require these messages:
08224  *  vm-youhave    "You have..."
08225  *  vm-and     "and"
08226  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08227  *
08228  * To use it for English, you will need these additional sound files:
08229  *  vm-new     "new"
08230  *  vm-message    "message", singular
08231  *  vm-messages      "messages", plural
08232  *
08233  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08234  *
08235  *  vm-newn    "novoye" (singular, neuter)
08236  *  vm-newx    "novikh" (counting plural form, genative plural)
08237  *  vm-message    "sobsheniye" (singular form)
08238  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08239  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08240  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08241  *  digits/2n     "dva" (neuter singular)
08242  */
08243 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08244 {
08245    int res;
08246    int lastnum = 0;
08247 
08248    res = ast_play_and_wait(chan, "vm-youhave");
08249 
08250    if (!res && vms->newmessages) {
08251       lastnum = vms->newmessages;
08252 
08253       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08254          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08255       }
08256 
08257       if (!res && vms->oldmessages) {
08258          res = ast_play_and_wait(chan, "vm-and");
08259       }
08260    }
08261 
08262    if (!res && vms->oldmessages) {
08263       lastnum = vms->oldmessages;
08264 
08265       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08266          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08267       }
08268    }
08269 
08270    if (!res) {
08271       if (lastnum == 0) {
08272          res = ast_play_and_wait(chan, "vm-no");
08273       }
08274       if (!res) {
08275          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08276       }
08277    }
08278 
08279    return res;
08280 }
08281 
08282 /* Default Hebrew syntax */
08283 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08284 {
08285    int res = 0;
08286 
08287    /* Introduce messages they have */
08288    if (!res) {
08289       if ((vms->newmessages) || (vms->oldmessages)) {
08290          res = ast_play_and_wait(chan, "vm-youhave");
08291       }
08292       /*
08293        * The word "shtei" refers to the number 2 in hebrew when performing a count
08294        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08295        * an element, this is one of them.
08296        */
08297       if (vms->newmessages) {
08298          if (!res) {
08299             if (vms->newmessages == 1) {
08300                res = ast_play_and_wait(chan, "vm-INBOX1");
08301             } else {
08302                if (vms->newmessages == 2) {
08303                   res = ast_play_and_wait(chan, "vm-shtei");
08304                } else {
08305                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08306                }
08307                res = ast_play_and_wait(chan, "vm-INBOX");
08308             }
08309          }
08310          if (vms->oldmessages && !res) {
08311             res = ast_play_and_wait(chan, "vm-and");
08312             if (vms->oldmessages == 1) {
08313                res = ast_play_and_wait(chan, "vm-Old1");
08314             } else {
08315                if (vms->oldmessages == 2) {
08316                   res = ast_play_and_wait(chan, "vm-shtei");
08317                } else {
08318                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08319                }
08320                res = ast_play_and_wait(chan, "vm-Old");
08321             }
08322          }
08323       }
08324       if (!res && vms->oldmessages && !vms->newmessages) {
08325          if (!res) {
08326             if (vms->oldmessages == 1) {
08327                res = ast_play_and_wait(chan, "vm-Old1");
08328             } else {
08329                if (vms->oldmessages == 2) {
08330                   res = ast_play_and_wait(chan, "vm-shtei");
08331                } else {
08332                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08333                }
08334                res = ast_play_and_wait(chan, "vm-Old");
08335             }
08336          }
08337       }
08338       if (!res) {
08339          if (!vms->oldmessages && !vms->newmessages) {
08340             if (!res) {
08341                res = ast_play_and_wait(chan, "vm-nomessages");
08342             }
08343          }
08344       }
08345    }
08346    return res;
08347 }
08348    
08349 /* Default English syntax */
08350 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08351 {
08352    int res;
08353 
08354    /* Introduce messages they have */
08355    res = ast_play_and_wait(chan, "vm-youhave");
08356    if (!res) {
08357       if (vms->urgentmessages) {
08358          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08359          if (!res)
08360             res = ast_play_and_wait(chan, "vm-Urgent");
08361          if ((vms->oldmessages || vms->newmessages) && !res) {
08362             res = ast_play_and_wait(chan, "vm-and");
08363          } else if (!res) {
08364             if ((vms->urgentmessages == 1))
08365                res = ast_play_and_wait(chan, "vm-message");
08366             else
08367                res = ast_play_and_wait(chan, "vm-messages");
08368          }
08369       }
08370       if (vms->newmessages) {
08371          res = say_and_wait(chan, vms->newmessages, chan->language);
08372          if (!res)
08373             res = ast_play_and_wait(chan, "vm-INBOX");
08374          if (vms->oldmessages && !res)
08375             res = ast_play_and_wait(chan, "vm-and");
08376          else if (!res) {
08377             if ((vms->newmessages == 1))
08378                res = ast_play_and_wait(chan, "vm-message");
08379             else
08380                res = ast_play_and_wait(chan, "vm-messages");
08381          }
08382             
08383       }
08384       if (!res && vms->oldmessages) {
08385          res = say_and_wait(chan, vms->oldmessages, chan->language);
08386          if (!res)
08387             res = ast_play_and_wait(chan, "vm-Old");
08388          if (!res) {
08389             if (vms->oldmessages == 1)
08390                res = ast_play_and_wait(chan, "vm-message");
08391             else
08392                res = ast_play_and_wait(chan, "vm-messages");
08393          }
08394       }
08395       if (!res) {
08396          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08397             res = ast_play_and_wait(chan, "vm-no");
08398             if (!res)
08399                res = ast_play_and_wait(chan, "vm-messages");
08400          }
08401       }
08402    }
08403    return res;
08404 }
08405 
08406 /* ITALIAN syntax */
08407 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08408 {
08409    /* Introduce messages they have */
08410    int res;
08411    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08412       res = ast_play_and_wait(chan, "vm-no") ||
08413          ast_play_and_wait(chan, "vm-message");
08414    else
08415       res = ast_play_and_wait(chan, "vm-youhave");
08416    if (!res && vms->newmessages) {
08417       res = (vms->newmessages == 1) ?
08418          ast_play_and_wait(chan, "digits/un") ||
08419          ast_play_and_wait(chan, "vm-nuovo") ||
08420          ast_play_and_wait(chan, "vm-message") :
08421          /* 2 or more new messages */
08422          say_and_wait(chan, vms->newmessages, chan->language) ||
08423          ast_play_and_wait(chan, "vm-nuovi") ||
08424          ast_play_and_wait(chan, "vm-messages");
08425       if (!res && vms->oldmessages)
08426          res = ast_play_and_wait(chan, "vm-and");
08427    }
08428    if (!res && vms->oldmessages) {
08429       res = (vms->oldmessages == 1) ?
08430          ast_play_and_wait(chan, "digits/un") ||
08431          ast_play_and_wait(chan, "vm-vecchio") ||
08432          ast_play_and_wait(chan, "vm-message") :
08433          /* 2 or more old messages */
08434          say_and_wait(chan, vms->oldmessages, chan->language) ||
08435          ast_play_and_wait(chan, "vm-vecchi") ||
08436          ast_play_and_wait(chan, "vm-messages");
08437    }
08438    return res;
08439 }
08440 
08441 /* POLISH syntax */
08442 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08443 {
08444    /* Introduce messages they have */
08445    int res;
08446    div_t num;
08447 
08448    if (!vms->oldmessages && !vms->newmessages) {
08449       res = ast_play_and_wait(chan, "vm-no");
08450       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08451       return res;
08452    } else {
08453       res = ast_play_and_wait(chan, "vm-youhave");
08454    }
08455 
08456    if (vms->newmessages) {
08457       num = div(vms->newmessages, 10);
08458       if (vms->newmessages == 1) {
08459          res = ast_play_and_wait(chan, "digits/1-a");
08460          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08461          res = res ? res : ast_play_and_wait(chan, "vm-message");
08462       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08463          if (num.rem == 2) {
08464             if (!num.quot) {
08465                res = ast_play_and_wait(chan, "digits/2-ie");
08466             } else {
08467                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08468                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08469             }
08470          } else {
08471             res = say_and_wait(chan, vms->newmessages, chan->language);
08472          }
08473          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08474          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08475       } else {
08476          res = say_and_wait(chan, vms->newmessages, chan->language);
08477          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08478          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08479       }
08480       if (!res && vms->oldmessages)
08481          res = ast_play_and_wait(chan, "vm-and");
08482    }
08483    if (!res && vms->oldmessages) {
08484       num = div(vms->oldmessages, 10);
08485       if (vms->oldmessages == 1) {
08486          res = ast_play_and_wait(chan, "digits/1-a");
08487          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08488          res = res ? res : ast_play_and_wait(chan, "vm-message");
08489       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08490          if (num.rem == 2) {
08491             if (!num.quot) {
08492                res = ast_play_and_wait(chan, "digits/2-ie");
08493             } else {
08494                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08495                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08496             }
08497          } else {
08498             res = say_and_wait(chan, vms->oldmessages, chan->language);
08499          }
08500          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08501          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08502       } else {
08503          res = say_and_wait(chan, vms->oldmessages, chan->language);
08504          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08505          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08506       }
08507    }
08508 
08509    return res;
08510 }
08511 
08512 /* SWEDISH syntax */
08513 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08514 {
08515    /* Introduce messages they have */
08516    int res;
08517 
08518    res = ast_play_and_wait(chan, "vm-youhave");
08519    if (res)
08520       return res;
08521 
08522    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08523       res = ast_play_and_wait(chan, "vm-no");
08524       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08525       return res;
08526    }
08527 
08528    if (vms->newmessages) {
08529       if ((vms->newmessages == 1)) {
08530          res = ast_play_and_wait(chan, "digits/ett");
08531          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08532          res = res ? res : ast_play_and_wait(chan, "vm-message");
08533       } else {
08534          res = say_and_wait(chan, vms->newmessages, chan->language);
08535          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08536          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08537       }
08538       if (!res && vms->oldmessages)
08539          res = ast_play_and_wait(chan, "vm-and");
08540    }
08541    if (!res && vms->oldmessages) {
08542       if (vms->oldmessages == 1) {
08543          res = ast_play_and_wait(chan, "digits/ett");
08544          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08545          res = res ? res : ast_play_and_wait(chan, "vm-message");
08546       } else {
08547          res = say_and_wait(chan, vms->oldmessages, chan->language);
08548          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08549          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08550       }
08551    }
08552 
08553    return res;
08554 }
08555 
08556 /* NORWEGIAN syntax */
08557 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08558 {
08559    /* Introduce messages they have */
08560    int res;
08561 
08562    res = ast_play_and_wait(chan, "vm-youhave");
08563    if (res)
08564       return res;
08565 
08566    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08567       res = ast_play_and_wait(chan, "vm-no");
08568       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08569       return res;
08570    }
08571 
08572    if (vms->newmessages) {
08573       if ((vms->newmessages == 1)) {
08574          res = ast_play_and_wait(chan, "digits/1");
08575          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08576          res = res ? res : ast_play_and_wait(chan, "vm-message");
08577       } else {
08578          res = say_and_wait(chan, vms->newmessages, chan->language);
08579          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08580          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08581       }
08582       if (!res && vms->oldmessages)
08583          res = ast_play_and_wait(chan, "vm-and");
08584    }
08585    if (!res && vms->oldmessages) {
08586       if (vms->oldmessages == 1) {
08587          res = ast_play_and_wait(chan, "digits/1");
08588          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08589          res = res ? res : ast_play_and_wait(chan, "vm-message");
08590       } else {
08591          res = say_and_wait(chan, vms->oldmessages, chan->language);
08592          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08593          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08594       }
08595    }
08596 
08597    return res;
08598 }
08599 
08600 /* GERMAN syntax */
08601 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08602 {
08603    /* Introduce messages they have */
08604    int res;
08605    res = ast_play_and_wait(chan, "vm-youhave");
08606    if (!res) {
08607       if (vms->newmessages) {
08608          if ((vms->newmessages == 1))
08609             res = ast_play_and_wait(chan, "digits/1F");
08610          else
08611             res = say_and_wait(chan, vms->newmessages, chan->language);
08612          if (!res)
08613             res = ast_play_and_wait(chan, "vm-INBOX");
08614          if (vms->oldmessages && !res)
08615             res = ast_play_and_wait(chan, "vm-and");
08616          else if (!res) {
08617             if ((vms->newmessages == 1))
08618                res = ast_play_and_wait(chan, "vm-message");
08619             else
08620                res = ast_play_and_wait(chan, "vm-messages");
08621          }
08622             
08623       }
08624       if (!res && vms->oldmessages) {
08625          if (vms->oldmessages == 1)
08626             res = ast_play_and_wait(chan, "digits/1F");
08627          else
08628             res = say_and_wait(chan, vms->oldmessages, chan->language);
08629          if (!res)
08630             res = ast_play_and_wait(chan, "vm-Old");
08631          if (!res) {
08632             if (vms->oldmessages == 1)
08633                res = ast_play_and_wait(chan, "vm-message");
08634             else
08635                res = ast_play_and_wait(chan, "vm-messages");
08636          }
08637       }
08638       if (!res) {
08639          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08640             res = ast_play_and_wait(chan, "vm-no");
08641             if (!res)
08642                res = ast_play_and_wait(chan, "vm-messages");
08643          }
08644       }
08645    }
08646    return res;
08647 }
08648 
08649 /* SPANISH syntax */
08650 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08651 {
08652    /* Introduce messages they have */
08653    int res;
08654    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08655       res = ast_play_and_wait(chan, "vm-youhaveno");
08656       if (!res)
08657          res = ast_play_and_wait(chan, "vm-messages");
08658    } else {
08659       res = ast_play_and_wait(chan, "vm-youhave");
08660    }
08661    if (!res) {
08662       if (vms->newmessages) {
08663          if (!res) {
08664             if ((vms->newmessages == 1)) {
08665                res = ast_play_and_wait(chan, "digits/1M");
08666                if (!res)
08667                   res = ast_play_and_wait(chan, "vm-message");
08668                if (!res)
08669                   res = ast_play_and_wait(chan, "vm-INBOXs");
08670             } else {
08671                res = say_and_wait(chan, vms->newmessages, chan->language);
08672                if (!res)
08673                   res = ast_play_and_wait(chan, "vm-messages");
08674                if (!res)
08675                   res = ast_play_and_wait(chan, "vm-INBOX");
08676             }
08677          }
08678          if (vms->oldmessages && !res)
08679             res = ast_play_and_wait(chan, "vm-and");
08680       }
08681       if (vms->oldmessages) {
08682          if (!res) {
08683             if (vms->oldmessages == 1) {
08684                res = ast_play_and_wait(chan, "digits/1M");
08685                if (!res)
08686                   res = ast_play_and_wait(chan, "vm-message");
08687                if (!res)
08688                   res = ast_play_and_wait(chan, "vm-Olds");
08689             } else {
08690                res = say_and_wait(chan, vms->oldmessages, chan->language);
08691                if (!res)
08692                   res = ast_play_and_wait(chan, "vm-messages");
08693                if (!res)
08694                   res = ast_play_and_wait(chan, "vm-Old");
08695             }
08696          }
08697       }
08698    }
08699 return res;
08700 }
08701 
08702 /* BRAZILIAN PORTUGUESE syntax */
08703 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08704    /* Introduce messages they have */
08705    int res;
08706    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08707       res = ast_play_and_wait(chan, "vm-nomessages");
08708       return res;
08709    } else {
08710       res = ast_play_and_wait(chan, "vm-youhave");
08711    }
08712    if (vms->newmessages) {
08713       if (!res)
08714          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08715       if ((vms->newmessages == 1)) {
08716          if (!res)
08717             res = ast_play_and_wait(chan, "vm-message");
08718          if (!res)
08719             res = ast_play_and_wait(chan, "vm-INBOXs");
08720       } else {
08721          if (!res)
08722             res = ast_play_and_wait(chan, "vm-messages");
08723          if (!res)
08724             res = ast_play_and_wait(chan, "vm-INBOX");
08725       }
08726       if (vms->oldmessages && !res)
08727          res = ast_play_and_wait(chan, "vm-and");
08728    }
08729    if (vms->oldmessages) {
08730       if (!res)
08731          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08732       if (vms->oldmessages == 1) {
08733          if (!res)
08734             res = ast_play_and_wait(chan, "vm-message");
08735          if (!res)
08736             res = ast_play_and_wait(chan, "vm-Olds");
08737       } else {
08738          if (!res)
08739             res = ast_play_and_wait(chan, "vm-messages");
08740          if (!res)
08741             res = ast_play_and_wait(chan, "vm-Old");
08742       }
08743    }
08744    return res;
08745 }
08746 
08747 /* FRENCH syntax */
08748 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08749 {
08750    /* Introduce messages they have */
08751    int res;
08752    res = ast_play_and_wait(chan, "vm-youhave");
08753    if (!res) {
08754       if (vms->newmessages) {
08755          res = say_and_wait(chan, vms->newmessages, chan->language);
08756          if (!res)
08757             res = ast_play_and_wait(chan, "vm-INBOX");
08758          if (vms->oldmessages && !res)
08759             res = ast_play_and_wait(chan, "vm-and");
08760          else if (!res) {
08761             if ((vms->newmessages == 1))
08762                res = ast_play_and_wait(chan, "vm-message");
08763             else
08764                res = ast_play_and_wait(chan, "vm-messages");
08765          }
08766             
08767       }
08768       if (!res && vms->oldmessages) {
08769          res = say_and_wait(chan, vms->oldmessages, chan->language);
08770          if (!res)
08771             res = ast_play_and_wait(chan, "vm-Old");
08772          if (!res) {
08773             if (vms->oldmessages == 1)
08774                res = ast_play_and_wait(chan, "vm-message");
08775             else
08776                res = ast_play_and_wait(chan, "vm-messages");
08777          }
08778       }
08779       if (!res) {
08780          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08781             res = ast_play_and_wait(chan, "vm-no");
08782             if (!res)
08783                res = ast_play_and_wait(chan, "vm-messages");
08784          }
08785       }
08786    }
08787    return res;
08788 }
08789 
08790 /* DUTCH syntax */
08791 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08792 {
08793    /* Introduce messages they have */
08794    int res;
08795    res = ast_play_and_wait(chan, "vm-youhave");
08796    if (!res) {
08797       if (vms->newmessages) {
08798          res = say_and_wait(chan, vms->newmessages, chan->language);
08799          if (!res) {
08800             if (vms->newmessages == 1)
08801                res = ast_play_and_wait(chan, "vm-INBOXs");
08802             else
08803                res = ast_play_and_wait(chan, "vm-INBOX");
08804          }
08805          if (vms->oldmessages && !res)
08806             res = ast_play_and_wait(chan, "vm-and");
08807          else if (!res) {
08808             if ((vms->newmessages == 1))
08809                res = ast_play_and_wait(chan, "vm-message");
08810             else
08811                res = ast_play_and_wait(chan, "vm-messages");
08812          }
08813             
08814       }
08815       if (!res && vms->oldmessages) {
08816          res = say_and_wait(chan, vms->oldmessages, chan->language);
08817          if (!res) {
08818             if (vms->oldmessages == 1)
08819                res = ast_play_and_wait(chan, "vm-Olds");
08820             else
08821                res = ast_play_and_wait(chan, "vm-Old");
08822          }
08823          if (!res) {
08824             if (vms->oldmessages == 1)
08825                res = ast_play_and_wait(chan, "vm-message");
08826             else
08827                res = ast_play_and_wait(chan, "vm-messages");
08828          }
08829       }
08830       if (!res) {
08831          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08832             res = ast_play_and_wait(chan, "vm-no");
08833             if (!res)
08834                res = ast_play_and_wait(chan, "vm-messages");
08835          }
08836       }
08837    }
08838    return res;
08839 }
08840 
08841 /* PORTUGUESE syntax */
08842 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08843 {
08844    /* Introduce messages they have */
08845    int res;
08846    res = ast_play_and_wait(chan, "vm-youhave");
08847    if (!res) {
08848       if (vms->newmessages) {
08849          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08850          if (!res) {
08851             if ((vms->newmessages == 1)) {
08852                res = ast_play_and_wait(chan, "vm-message");
08853                if (!res)
08854                   res = ast_play_and_wait(chan, "vm-INBOXs");
08855             } else {
08856                res = ast_play_and_wait(chan, "vm-messages");
08857                if (!res)
08858                   res = ast_play_and_wait(chan, "vm-INBOX");
08859             }
08860          }
08861          if (vms->oldmessages && !res)
08862             res = ast_play_and_wait(chan, "vm-and");
08863       }
08864       if (!res && vms->oldmessages) {
08865          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08866          if (!res) {
08867             if (vms->oldmessages == 1) {
08868                res = ast_play_and_wait(chan, "vm-message");
08869                if (!res)
08870                   res = ast_play_and_wait(chan, "vm-Olds");
08871             } else {
08872                res = ast_play_and_wait(chan, "vm-messages");
08873                if (!res)
08874                   res = ast_play_and_wait(chan, "vm-Old");
08875             }
08876          }
08877       }
08878       if (!res) {
08879          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08880             res = ast_play_and_wait(chan, "vm-no");
08881             if (!res)
08882                res = ast_play_and_wait(chan, "vm-messages");
08883          }
08884       }
08885    }
08886    return res;
08887 }
08888 
08889 
08890 /* CZECH syntax */
08891 /* in czech there must be declension of word new and message
08892  * czech        : english        : czech      : english
08893  * --------------------------------------------------------
08894  * vm-youhave   : you have 
08895  * vm-novou     : one new        : vm-zpravu  : message
08896  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08897  * vm-novych    : 5-infinite new : vm-zprav   : messages
08898  * vm-starou   : one old
08899  * vm-stare     : 2-4 old 
08900  * vm-starych   : 5-infinite old
08901  * jednu        : one   - falling 4. 
08902  * vm-no        : no  ( no messages )
08903  */
08904 
08905 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08906 {
08907    int res;
08908    res = ast_play_and_wait(chan, "vm-youhave");
08909    if (!res) {
08910       if (vms->newmessages) {
08911          if (vms->newmessages == 1) {
08912             res = ast_play_and_wait(chan, "digits/jednu");
08913          } else {
08914             res = say_and_wait(chan, vms->newmessages, chan->language);
08915          }
08916          if (!res) {
08917             if ((vms->newmessages == 1))
08918                res = ast_play_and_wait(chan, "vm-novou");
08919             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08920                res = ast_play_and_wait(chan, "vm-nove");
08921             if (vms->newmessages > 4)
08922                res = ast_play_and_wait(chan, "vm-novych");
08923          }
08924          if (vms->oldmessages && !res)
08925             res = ast_play_and_wait(chan, "vm-and");
08926          else if (!res) {
08927             if ((vms->newmessages == 1))
08928                res = ast_play_and_wait(chan, "vm-zpravu");
08929             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08930                res = ast_play_and_wait(chan, "vm-zpravy");
08931             if (vms->newmessages > 4)
08932                res = ast_play_and_wait(chan, "vm-zprav");
08933          }
08934       }
08935       if (!res && vms->oldmessages) {
08936          res = say_and_wait(chan, vms->oldmessages, chan->language);
08937          if (!res) {
08938             if ((vms->oldmessages == 1))
08939                res = ast_play_and_wait(chan, "vm-starou");
08940             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08941                res = ast_play_and_wait(chan, "vm-stare");
08942             if (vms->oldmessages > 4)
08943                res = ast_play_and_wait(chan, "vm-starych");
08944          }
08945          if (!res) {
08946             if ((vms->oldmessages == 1))
08947                res = ast_play_and_wait(chan, "vm-zpravu");
08948             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08949                res = ast_play_and_wait(chan, "vm-zpravy");
08950             if (vms->oldmessages > 4)
08951                res = ast_play_and_wait(chan, "vm-zprav");
08952          }
08953       }
08954       if (!res) {
08955          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08956             res = ast_play_and_wait(chan, "vm-no");
08957             if (!res)
08958                res = ast_play_and_wait(chan, "vm-zpravy");
08959          }
08960       }
08961    }
08962    return res;
08963 }
08964 
08965 /* CHINESE (Taiwan) syntax */
08966 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08967 {
08968    int res;
08969    /* Introduce messages they have */
08970    res = ast_play_and_wait(chan, "vm-you");
08971 
08972    if (!res && vms->newmessages) {
08973       res = ast_play_and_wait(chan, "vm-have");
08974       if (!res)
08975          res = say_and_wait(chan, vms->newmessages, chan->language);
08976       if (!res)
08977          res = ast_play_and_wait(chan, "vm-tong");
08978       if (!res)
08979          res = ast_play_and_wait(chan, "vm-INBOX");
08980       if (vms->oldmessages && !res)
08981          res = ast_play_and_wait(chan, "vm-and");
08982       else if (!res) 
08983          res = ast_play_and_wait(chan, "vm-messages");
08984    }
08985    if (!res && vms->oldmessages) {
08986       res = ast_play_and_wait(chan, "vm-have");
08987       if (!res)
08988          res = say_and_wait(chan, vms->oldmessages, chan->language);
08989       if (!res)
08990          res = ast_play_and_wait(chan, "vm-tong");
08991       if (!res)
08992          res = ast_play_and_wait(chan, "vm-Old");
08993       if (!res)
08994          res = ast_play_and_wait(chan, "vm-messages");
08995    }
08996    if (!res && !vms->oldmessages && !vms->newmessages) {
08997       res = ast_play_and_wait(chan, "vm-haveno");
08998       if (!res)
08999          res = ast_play_and_wait(chan, "vm-messages");
09000    }
09001    return res;
09002 }
09003 
09004 /* Vietnamese syntax */
09005 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09006 {
09007    int res;
09008 
09009    /* Introduce messages they have */
09010    res = ast_play_and_wait(chan, "vm-youhave");
09011    if (!res) {
09012       if (vms->newmessages) {
09013          res = say_and_wait(chan, vms->newmessages, chan->language);
09014          if (!res)
09015             res = ast_play_and_wait(chan, "vm-INBOX");
09016          if (vms->oldmessages && !res)
09017             res = ast_play_and_wait(chan, "vm-and");
09018       }
09019       if (!res && vms->oldmessages) {
09020          res = say_and_wait(chan, vms->oldmessages, chan->language);
09021          if (!res)
09022             res = ast_play_and_wait(chan, "vm-Old");        
09023       }
09024       if (!res) {
09025          if (!vms->oldmessages && !vms->newmessages) {
09026             res = ast_play_and_wait(chan, "vm-no");
09027             if (!res)
09028                res = ast_play_and_wait(chan, "vm-message");
09029          }
09030       }
09031    }
09032    return res;
09033 }
09034 
09035 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09036 {
09037    char prefile[256];
09038    
09039    /* Notify the user that the temp greeting is set and give them the option to remove it */
09040    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09041    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09042       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09043       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09044          ast_play_and_wait(chan, "vm-tempgreetactive");
09045       }
09046       DISPOSE(prefile, -1);
09047    }
09048 
09049    /* Play voicemail intro - syntax is different for different languages */
09050    if (0) {
09051       return 0;
09052    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09053       return vm_intro_cs(chan, vms);
09054    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09055       static int deprecation_warning = 0;
09056       if (deprecation_warning++ % 10 == 0) {
09057          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09058       }
09059       return vm_intro_cs(chan, vms);
09060    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09061       return vm_intro_de(chan, vms);
09062    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09063       return vm_intro_es(chan, vms);
09064    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09065       return vm_intro_fr(chan, vms);
09066    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09067       return vm_intro_gr(chan, vms);
09068    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09069       return vm_intro_he(chan, vms);
09070    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09071       return vm_intro_it(chan, vms);
09072    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09073       return vm_intro_nl(chan, vms);
09074    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09075       return vm_intro_no(chan, vms);
09076    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09077       return vm_intro_pl(chan, vms);
09078    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09079       return vm_intro_pt_BR(chan, vms);
09080    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09081       return vm_intro_pt(chan, vms);
09082    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09083       return vm_intro_multilang(chan, vms, "n");
09084    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09085       return vm_intro_se(chan, vms);
09086    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09087       return vm_intro_multilang(chan, vms, "n");
09088    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09089       return vm_intro_vi(chan, vms);
09090    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09091       return vm_intro_zh(chan, vms);
09092    } else {                                             /* Default to ENGLISH */
09093       return vm_intro_en(chan, vms);
09094    }
09095 }
09096 
09097 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09098 {
09099    int res = 0;
09100    /* Play instructions and wait for new command */
09101    while (!res) {
09102       if (vms->starting) {
09103          if (vms->lastmsg > -1) {
09104             if (skipadvanced)
09105                res = ast_play_and_wait(chan, "vm-onefor-full");
09106             else
09107                res = ast_play_and_wait(chan, "vm-onefor");
09108             if (!res)
09109                res = vm_play_folder_name(chan, vms->vmbox);
09110          }
09111          if (!res) {
09112             if (skipadvanced)
09113                res = ast_play_and_wait(chan, "vm-opts-full");
09114             else
09115                res = ast_play_and_wait(chan, "vm-opts");
09116          }
09117       } else {
09118          /* Added for additional help */
09119          if (skipadvanced) {
09120             res = ast_play_and_wait(chan, "vm-onefor-full");
09121             if (!res)
09122                res = vm_play_folder_name(chan, vms->vmbox);
09123             res = ast_play_and_wait(chan, "vm-opts-full");
09124          }
09125          /* Logic:
09126           * If the current message is not the first OR
09127           * if we're listening to the first new message and there are
09128           * also urgent messages, then prompt for navigation to the
09129           * previous message
09130           */
09131          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09132             res = ast_play_and_wait(chan, "vm-prev");
09133          }
09134          if (!res && !skipadvanced)
09135             res = ast_play_and_wait(chan, "vm-advopts");
09136          if (!res)
09137             res = ast_play_and_wait(chan, "vm-repeat");
09138          /* Logic:
09139           * If we're not listening to the last message OR
09140           * we're listening to the last urgent message and there are
09141           * also new non-urgent messages, then prompt for navigation
09142           * to the next message
09143           */
09144          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09145             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09146             res = ast_play_and_wait(chan, "vm-next");
09147          }
09148          if (!res) {
09149             int curmsg_deleted;
09150 #ifdef IMAP_STORAGE
09151             ast_mutex_lock(&vms->lock);
09152 #endif
09153             curmsg_deleted = vms->deleted[vms->curmsg];
09154 #ifdef IMAP_STORAGE
09155             ast_mutex_unlock(&vms->lock);
09156 #endif
09157             if (!curmsg_deleted) {
09158                res = ast_play_and_wait(chan, "vm-delete");
09159             } else {
09160                res = ast_play_and_wait(chan, "vm-undelete");
09161             }
09162             if (!res) {
09163                res = ast_play_and_wait(chan, "vm-toforward");
09164             }
09165             if (!res) {
09166                res = ast_play_and_wait(chan, "vm-savemessage");
09167             }
09168          }
09169       }
09170       if (!res) {
09171          res = ast_play_and_wait(chan, "vm-helpexit");
09172       }
09173       if (!res)
09174          res = ast_waitfordigit(chan, 6000);
09175       if (!res) {
09176          vms->repeats++;
09177          if (vms->repeats > 2) {
09178             res = 't';
09179          }
09180       }
09181    }
09182    return res;
09183 }
09184 
09185 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09186 {
09187    int res = 0;
09188    /* Play instructions and wait for new command */
09189    while (!res) {
09190       if (vms->lastmsg > -1) {
09191          res = ast_play_and_wait(chan, "vm-listen");
09192          if (!res)
09193             res = vm_play_folder_name(chan, vms->vmbox);
09194          if (!res)
09195             res = ast_play_and_wait(chan, "press");
09196          if (!res)
09197             res = ast_play_and_wait(chan, "digits/1");
09198       }
09199       if (!res)
09200          res = ast_play_and_wait(chan, "vm-opts");
09201       if (!res) {
09202          vms->starting = 0;
09203          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09204       }
09205    }
09206    return res;
09207 }
09208 
09209 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09210 {
09211    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09212       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09213    } else {             /* Default to ENGLISH */
09214       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09215    }
09216 }
09217 
09218 
09219 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09220 {
09221    int cmd = 0;
09222    int duration = 0;
09223    int tries = 0;
09224    char newpassword[80] = "";
09225    char newpassword2[80] = "";
09226    char prefile[PATH_MAX] = "";
09227    unsigned char buf[256];
09228    int bytes = 0;
09229 
09230    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09231    if (ast_adsi_available(chan)) {
09232       bytes += adsi_logo(buf + bytes);
09233       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09234       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09235       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09236       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09237       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09238    }
09239 
09240    /* If forcename is set, have the user record their name */
09241    if (ast_test_flag(vmu, VM_FORCENAME)) {
09242       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09243       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09244          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09245          if (cmd < 0 || cmd == 't' || cmd == '#')
09246             return cmd;
09247       }
09248    }
09249 
09250    /* If forcegreetings is set, have the user record their greetings */
09251    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09252       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09253       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09254          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09255          if (cmd < 0 || cmd == 't' || cmd == '#')
09256             return cmd;
09257       }
09258 
09259       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09260       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09261          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09262          if (cmd < 0 || cmd == 't' || cmd == '#')
09263             return cmd;
09264       }
09265    }
09266 
09267    /*
09268     * Change the password last since new users will be able to skip over any steps this one comes before
09269     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09270     */
09271    for (;;) {
09272       newpassword[1] = '\0';
09273       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09274       if (cmd == '#')
09275          newpassword[0] = '\0';
09276       if (cmd < 0 || cmd == 't' || cmd == '#')
09277          return cmd;
09278       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09279       if (cmd < 0 || cmd == 't' || cmd == '#')
09280          return cmd;
09281       cmd = check_password(vmu, newpassword); /* perform password validation */
09282       if (cmd != 0) {
09283          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09284          cmd = ast_play_and_wait(chan, vm_invalid_password);
09285       } else {
09286          newpassword2[1] = '\0';
09287          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09288          if (cmd == '#')
09289             newpassword2[0] = '\0';
09290          if (cmd < 0 || cmd == 't' || cmd == '#')
09291             return cmd;
09292          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09293          if (cmd < 0 || cmd == 't' || cmd == '#')
09294             return cmd;
09295          if (!strcmp(newpassword, newpassword2))
09296             break;
09297          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09298          cmd = ast_play_and_wait(chan, vm_mismatch);
09299       }
09300       if (++tries == 3)
09301          return -1;
09302       if (cmd != 0) {
09303          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09304       }
09305    }
09306    if (pwdchange & PWDCHANGE_INTERNAL)
09307       vm_change_password(vmu, newpassword);
09308    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09309       vm_change_password_shell(vmu, newpassword);
09310 
09311    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09312    cmd = ast_play_and_wait(chan, vm_passchanged);
09313 
09314    return cmd;
09315 }
09316 
09317 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09318 {
09319    int cmd = 0;
09320    int retries = 0;
09321    int duration = 0;
09322    char newpassword[80] = "";
09323    char newpassword2[80] = "";
09324    char prefile[PATH_MAX] = "";
09325    unsigned char buf[256];
09326    int bytes = 0;
09327 
09328    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09329    if (ast_adsi_available(chan)) {
09330       bytes += adsi_logo(buf + bytes);
09331       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09332       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09333       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09334       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09335       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09336    }
09337    while ((cmd >= 0) && (cmd != 't')) {
09338       if (cmd)
09339          retries = 0;
09340       switch (cmd) {
09341       case '1': /* Record your unavailable message */
09342          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09343          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09344          break;
09345       case '2':  /* Record your busy message */
09346          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09347          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09348          break;
09349       case '3': /* Record greeting */
09350          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09351          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09352          break;
09353       case '4':  /* manage the temporary greeting */
09354          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09355          break;
09356       case '5': /* change password */
09357          if (vmu->password[0] == '-') {
09358             cmd = ast_play_and_wait(chan, "vm-no");
09359             break;
09360          }
09361          newpassword[1] = '\0';
09362          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09363          if (cmd == '#')
09364             newpassword[0] = '\0';
09365          else {
09366             if (cmd < 0)
09367                break;
09368             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09369                break;
09370             }
09371          }
09372          cmd = check_password(vmu, newpassword); /* perform password validation */
09373          if (cmd != 0) {
09374             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09375             cmd = ast_play_and_wait(chan, vm_invalid_password);
09376             if (!cmd) {
09377                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09378             }
09379             break;
09380          }
09381          newpassword2[1] = '\0';
09382          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09383          if (cmd == '#')
09384             newpassword2[0] = '\0';
09385          else {
09386             if (cmd < 0)
09387                break;
09388 
09389             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09390                break;
09391             }
09392          }
09393          if (strcmp(newpassword, newpassword2)) {
09394             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09395             cmd = ast_play_and_wait(chan, vm_mismatch);
09396             if (!cmd) {
09397                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09398             }
09399             break;
09400          }
09401 
09402          if (pwdchange & PWDCHANGE_INTERNAL) {
09403             vm_change_password(vmu, newpassword);
09404          }
09405          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09406             vm_change_password_shell(vmu, newpassword);
09407          }
09408 
09409          ast_debug(1, "User %s set password to %s of length %d\n",
09410             vms->username, newpassword, (int) strlen(newpassword));
09411          cmd = ast_play_and_wait(chan, vm_passchanged);
09412          break;
09413       case '*': 
09414          cmd = 't';
09415          break;
09416       default: 
09417          cmd = 0;
09418          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09419          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09420          if (ast_fileexists(prefile, NULL, NULL)) {
09421             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09422          }
09423          DISPOSE(prefile, -1);
09424          if (!cmd) {
09425             cmd = ast_play_and_wait(chan, "vm-options");
09426          }
09427          if (!cmd) {
09428             cmd = ast_waitfordigit(chan, 6000);
09429          }
09430          if (!cmd) {
09431             retries++;
09432          }
09433          if (retries > 3) {
09434             cmd = 't';
09435          }
09436          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09437       }
09438    }
09439    if (cmd == 't')
09440       cmd = 0;
09441    return cmd;
09442 }
09443 
09444 /*!
09445  * \brief The handler for 'record a temporary greeting'. 
09446  * \param chan
09447  * \param vmu
09448  * \param vms
09449  * \param fmtc
09450  * \param record_gain
09451  *
09452  * This is option 4 from the mailbox options menu.
09453  * This function manages the following promptings:
09454  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09455  * 2: remove (delete) the temporary greeting.
09456  * *: return to the main menu.
09457  *
09458  * \return zero on success, -1 on error.
09459  */
09460 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09461 {
09462    int cmd = 0;
09463    int retries = 0;
09464    int duration = 0;
09465    char prefile[PATH_MAX] = "";
09466    unsigned char buf[256];
09467    int bytes = 0;
09468 
09469    if (ast_adsi_available(chan)) {
09470       bytes += adsi_logo(buf + bytes);
09471       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09472       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09473       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09474       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09475       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09476    }
09477 
09478    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09479    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09480    while ((cmd >= 0) && (cmd != 't')) {
09481       if (cmd)
09482          retries = 0;
09483       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09484       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09485          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09486          if (cmd == -1) {
09487             break;
09488          }
09489          cmd = 't';  
09490       } else {
09491          switch (cmd) {
09492          case '1':
09493             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09494             break;
09495          case '2':
09496             DELETE(prefile, -1, prefile, vmu);
09497             ast_play_and_wait(chan, "vm-tempremoved");
09498             cmd = 't';  
09499             break;
09500          case '*': 
09501             cmd = 't';
09502             break;
09503          default:
09504             cmd = ast_play_and_wait(chan,
09505                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09506                   "vm-tempgreeting2" : "vm-tempgreeting");
09507             if (!cmd) {
09508                cmd = ast_waitfordigit(chan, 6000);
09509             }
09510             if (!cmd) {
09511                retries++;
09512             }
09513             if (retries > 3) {
09514                cmd = 't';
09515             }
09516             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09517          }
09518       }
09519       DISPOSE(prefile, -1);
09520    }
09521    if (cmd == 't')
09522       cmd = 0;
09523    return cmd;
09524 }
09525 
09526 /*!
09527  * \brief Greek syntax for 'You have N messages' greeting.
09528  * \param chan
09529  * \param vms
09530  * \param vmu
09531  *
09532  * \return zero on success, -1 on error.
09533  */   
09534 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09535 {
09536    int cmd = 0;
09537 
09538    if (vms->lastmsg > -1) {
09539       cmd = play_message(chan, vmu, vms);
09540    } else {
09541       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09542       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09543          if (!cmd) {
09544             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09545             cmd = ast_play_and_wait(chan, vms->fn);
09546          }
09547          if (!cmd)
09548             cmd = ast_play_and_wait(chan, "vm-messages");
09549       } else {
09550          if (!cmd)
09551             cmd = ast_play_and_wait(chan, "vm-messages");
09552          if (!cmd) {
09553             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09554             cmd = ast_play_and_wait(chan, vms->fn);
09555          }
09556       }
09557    } 
09558    return cmd;
09559 }
09560 
09561 /* Hebrew Syntax */
09562 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09563 {
09564    int cmd = 0;
09565 
09566    if (vms->lastmsg > -1) {
09567       cmd = play_message(chan, vmu, vms);
09568    } else {
09569       if (!strcasecmp(vms->fn, "INBOX")) {
09570          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09571       } else {
09572          cmd = ast_play_and_wait(chan, "vm-nomessages");
09573       }
09574    }
09575    return cmd;
09576 }
09577 
09578 /*! 
09579  * \brief Default English syntax for 'You have N messages' greeting.
09580  * \param chan
09581  * \param vms
09582  * \param vmu
09583  *
09584  * \return zero on success, -1 on error.
09585  */
09586 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09587 {
09588    int cmd = 0;
09589 
09590    if (vms->lastmsg > -1) {
09591       cmd = play_message(chan, vmu, vms);
09592    } else {
09593       cmd = ast_play_and_wait(chan, "vm-youhave");
09594       if (!cmd) 
09595          cmd = ast_play_and_wait(chan, "vm-no");
09596       if (!cmd) {
09597          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09598          cmd = ast_play_and_wait(chan, vms->fn);
09599       }
09600       if (!cmd)
09601          cmd = ast_play_and_wait(chan, "vm-messages");
09602    }
09603    return cmd;
09604 }
09605 
09606 /*! 
09607  *\brief Italian syntax for 'You have N messages' greeting.
09608  * \param chan
09609  * \param vms
09610  * \param vmu
09611  *
09612  * \return zero on success, -1 on error.
09613  */
09614 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09615 {
09616    int cmd;
09617 
09618    if (vms->lastmsg > -1) {
09619       cmd = play_message(chan, vmu, vms);
09620    } else {
09621       cmd = ast_play_and_wait(chan, "vm-no");
09622       if (!cmd)
09623          cmd = ast_play_and_wait(chan, "vm-message");
09624       if (!cmd) {
09625          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09626          cmd = ast_play_and_wait(chan, vms->fn);
09627       }
09628    }
09629    return cmd;
09630 }
09631 
09632 /*! 
09633  * \brief Spanish syntax for 'You have N messages' greeting.
09634  * \param chan
09635  * \param vms
09636  * \param vmu
09637  *
09638  * \return zero on success, -1 on error.
09639  */
09640 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09641 {
09642    int cmd;
09643 
09644    if (vms->lastmsg > -1) {
09645       cmd = play_message(chan, vmu, vms);
09646    } else {
09647       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09648       if (!cmd)
09649          cmd = ast_play_and_wait(chan, "vm-messages");
09650       if (!cmd) {
09651          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09652          cmd = ast_play_and_wait(chan, vms->fn);
09653       }
09654    }
09655    return cmd;
09656 }
09657 
09658 /*! 
09659  * \brief Portuguese syntax for 'You have N messages' greeting.
09660  * \param chan
09661  * \param vms
09662  * \param vmu
09663  *
09664  * \return zero on success, -1 on error.
09665  */
09666 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09667 {
09668    int cmd;
09669 
09670    if (vms->lastmsg > -1) {
09671       cmd = play_message(chan, vmu, vms);
09672    } else {
09673       cmd = ast_play_and_wait(chan, "vm-no");
09674       if (!cmd) {
09675          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09676          cmd = ast_play_and_wait(chan, vms->fn);
09677       }
09678       if (!cmd)
09679          cmd = ast_play_and_wait(chan, "vm-messages");
09680    }
09681    return cmd;
09682 }
09683 
09684 /*! 
09685  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09686  * \param chan
09687  * \param vms
09688  * \param vmu
09689  *
09690  * \return zero on success, -1 on error.
09691  */
09692 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09693 {
09694    int cmd;
09695 
09696    if (vms->lastmsg > -1) {
09697       cmd = play_message(chan, vmu, vms);
09698    } else {
09699       cmd = ast_play_and_wait(chan, "vm-you");
09700       if (!cmd) 
09701          cmd = ast_play_and_wait(chan, "vm-haveno");
09702       if (!cmd)
09703          cmd = ast_play_and_wait(chan, "vm-messages");
09704       if (!cmd) {
09705          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09706          cmd = ast_play_and_wait(chan, vms->fn);
09707       }
09708    }
09709    return cmd;
09710 }
09711 
09712 /*! 
09713  * \brief Vietnamese syntax for 'You have N messages' greeting.
09714  * \param chan
09715  * \param vms
09716  * \param vmu
09717  *
09718  * \return zero on success, -1 on error.
09719  */
09720 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09721 {
09722    int cmd = 0;
09723 
09724    if (vms->lastmsg > -1) {
09725       cmd = play_message(chan, vmu, vms);
09726    } else {
09727       cmd = ast_play_and_wait(chan, "vm-no");
09728       if (!cmd) {
09729          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09730          cmd = ast_play_and_wait(chan, vms->fn);
09731       }
09732    }
09733    return cmd;
09734 }
09735 
09736 /*!
09737  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09738  * \param chan The channel for the current user. We read the language property from this.
09739  * \param vms passed into the language-specific vm_browse_messages function.
09740  * \param vmu passed into the language-specific vm_browse_messages function.
09741  * 
09742  * The method to be invoked is determined by the value of language code property in the user's channel.
09743  * The default (when unable to match) is to use english.
09744  *
09745  * \return zero on success, -1 on error.
09746  */
09747 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09748 {
09749    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09750       return vm_browse_messages_es(chan, vms, vmu);
09751    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09752       return vm_browse_messages_gr(chan, vms, vmu);
09753    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09754       return vm_browse_messages_he(chan, vms, vmu);
09755    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09756       return vm_browse_messages_it(chan, vms, vmu);
09757    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09758       return vm_browse_messages_pt(chan, vms, vmu);
09759    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09760       return vm_browse_messages_vi(chan, vms, vmu);
09761    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09762       return vm_browse_messages_zh(chan, vms, vmu);
09763    } else {                                             /* Default to English syntax */
09764       return vm_browse_messages_en(chan, vms, vmu);
09765    }
09766 }
09767 
09768 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09769          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09770          int skipuser, int max_logins, int silent)
09771 {
09772    int useadsi = 0, valid = 0, logretries = 0;
09773    char password[AST_MAX_EXTENSION]="", *passptr;
09774    struct ast_vm_user vmus, *vmu = NULL;
09775 
09776    /* If ADSI is supported, setup login screen */
09777    adsi_begin(chan, &useadsi);
09778    if (!skipuser && useadsi)
09779       adsi_login(chan);
09780    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09781    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09782       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09783       return -1;
09784    }
09785 
09786    /* Authenticate them and get their mailbox/password */
09787 
09788    while (!valid && (logretries < max_logins)) {
09789       /* Prompt for, and read in the username */
09790       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09791          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09792          return -1;
09793       }
09794       if (ast_strlen_zero(mailbox)) {
09795          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09796             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09797          } else {
09798             ast_verb(3, "Username not entered\n"); 
09799             return -1;
09800          }
09801       } else if (mailbox[0] == '*') {
09802          /* user entered '*' */
09803          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09804          if (ast_exists_extension(chan, chan->context, "a", 1,
09805             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09806             return -1;
09807          }
09808          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09809          mailbox[0] = '\0';
09810       }
09811 
09812       if (useadsi)
09813          adsi_password(chan);
09814 
09815       if (!ast_strlen_zero(prefix)) {
09816          char fullusername[80] = "";
09817          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09818          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09819          ast_copy_string(mailbox, fullusername, mailbox_size);
09820       }
09821 
09822       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09823       vmu = find_user(&vmus, context, mailbox);
09824       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09825          /* saved password is blank, so don't bother asking */
09826          password[0] = '\0';
09827       } else {
09828          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09829          if (ast_streamfile(chan, vm_password, chan->language)) {
09830             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09831             return -1;
09832          }
09833          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09834             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09835             return -1;
09836          } else if (password[0] == '*') {
09837             /* user entered '*' */
09838             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09839             if (ast_exists_extension(chan, chan->context, "a", 1,
09840                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09841                mailbox[0] = '*';
09842                return -1;
09843             }
09844             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09845             mailbox[0] = '\0';
09846             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09847             vmu = NULL;
09848          }
09849       }
09850 
09851       if (vmu) {
09852          passptr = vmu->password;
09853          if (passptr[0] == '-') passptr++;
09854       }
09855       if (vmu && !strcmp(passptr, password))
09856          valid++;
09857       else {
09858          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09859          if (!ast_strlen_zero(prefix))
09860             mailbox[0] = '\0';
09861       }
09862       logretries++;
09863       if (!valid) {
09864          if (skipuser || logretries >= max_logins) {
09865             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09866             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09867                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09868                return -1;
09869             }
09870          } else {
09871             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09872             if (useadsi)
09873                adsi_login(chan);
09874             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09875                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09876                return -1;
09877             }
09878          }
09879          if (ast_waitstream(chan, "")) /* Channel is hung up */
09880             return -1;
09881       }
09882    }
09883    if (!valid && (logretries >= max_logins)) {
09884       ast_stopstream(chan);
09885       ast_play_and_wait(chan, "vm-goodbye");
09886       return -1;
09887    }
09888    if (vmu && !skipuser) {
09889       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09890    }
09891    return 0;
09892 }
09893 
09894 static int vm_execmain(struct ast_channel *chan, const char *data)
09895 {
09896    /* XXX This is, admittedly, some pretty horrendous code.  For some
09897       reason it just seemed a lot easier to do with GOTO's.  I feel
09898       like I'm back in my GWBASIC days. XXX */
09899    int res = -1;
09900    int cmd = 0;
09901    int valid = 0;
09902    char prefixstr[80] ="";
09903    char ext_context[256]="";
09904    int box;
09905    int useadsi = 0;
09906    int skipuser = 0;
09907    struct vm_state vms;
09908    struct ast_vm_user *vmu = NULL, vmus;
09909    char *context = NULL;
09910    int silentexit = 0;
09911    struct ast_flags flags = { 0 };
09912    signed char record_gain = 0;
09913    int play_auto = 0;
09914    int play_folder = 0;
09915    int in_urgent = 0;
09916 #ifdef IMAP_STORAGE
09917    int deleted = 0;
09918 #endif
09919 
09920    /* Add the vm_state to the active list and keep it active */
09921    memset(&vms, 0, sizeof(vms));
09922 
09923    vms.lastmsg = -1;
09924 
09925    memset(&vmus, 0, sizeof(vmus));
09926 
09927    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09928    if (chan->_state != AST_STATE_UP) {
09929       ast_debug(1, "Before ast_answer\n");
09930       ast_answer(chan);
09931    }
09932 
09933    if (!ast_strlen_zero(data)) {
09934       char *opts[OPT_ARG_ARRAY_SIZE];
09935       char *parse;
09936       AST_DECLARE_APP_ARGS(args,
09937          AST_APP_ARG(argv0);
09938          AST_APP_ARG(argv1);
09939       );
09940 
09941       parse = ast_strdupa(data);
09942 
09943       AST_STANDARD_APP_ARGS(args, parse);
09944 
09945       if (args.argc == 2) {
09946          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09947             return -1;
09948          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09949             int gain;
09950             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09951                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09952                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09953                   return -1;
09954                } else {
09955                   record_gain = (signed char) gain;
09956                }
09957             } else {
09958                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09959             }
09960          }
09961          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09962             play_auto = 1;
09963             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09964                /* See if it is a folder name first */
09965                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09966                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09967                      play_folder = -1;
09968                   }
09969                } else {
09970                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09971                }
09972             } else {
09973                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09974             }
09975             if (play_folder > 9 || play_folder < 0) {
09976                ast_log(AST_LOG_WARNING,
09977                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09978                   opts[OPT_ARG_PLAYFOLDER]);
09979                play_folder = 0;
09980             }
09981          }
09982       } else {
09983          /* old style options parsing */
09984          while (*(args.argv0)) {
09985             if (*(args.argv0) == 's')
09986                ast_set_flag(&flags, OPT_SILENT);
09987             else if (*(args.argv0) == 'p')
09988                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09989             else 
09990                break;
09991             (args.argv0)++;
09992          }
09993 
09994       }
09995 
09996       valid = ast_test_flag(&flags, OPT_SILENT);
09997 
09998       if ((context = strchr(args.argv0, '@')))
09999          *context++ = '\0';
10000 
10001       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10002          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10003       else
10004          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10005 
10006       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10007          skipuser++;
10008       else
10009          valid = 0;
10010    }
10011 
10012    if (!valid)
10013       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10014 
10015    ast_debug(1, "After vm_authenticate\n");
10016 
10017    if (vms.username[0] == '*') {
10018       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10019 
10020       /* user entered '*' */
10021       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10022          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10023          res = 0; /* prevent hangup */
10024          goto out;
10025       }
10026    }
10027 
10028    if (!res) {
10029       valid = 1;
10030       if (!skipuser)
10031          vmu = &vmus;
10032    } else {
10033       res = 0;
10034    }
10035 
10036    /* If ADSI is supported, setup login screen */
10037    adsi_begin(chan, &useadsi);
10038 
10039    ast_test_suite_assert(valid);
10040    if (!valid) {
10041       goto out;
10042    }
10043    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10044 
10045 #ifdef IMAP_STORAGE
10046    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10047    pthread_setspecific(ts_vmstate.key, &vms);
10048 
10049    vms.interactive = 1;
10050    vms.updated = 1;
10051    if (vmu)
10052       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10053    vmstate_insert(&vms);
10054    init_vm_state(&vms);
10055 #endif
10056    
10057    /* Set language from config to override channel language */
10058    if (!ast_strlen_zero(vmu->language))
10059       ast_string_field_set(chan, language, vmu->language);
10060 
10061    /* Retrieve urgent, old and new message counts */
10062    ast_debug(1, "Before open_mailbox\n");
10063    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10064    if (res < 0)
10065       goto out;
10066    vms.oldmessages = vms.lastmsg + 1;
10067    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10068    /* check INBOX */
10069    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10070    if (res < 0)
10071       goto out;
10072    vms.newmessages = vms.lastmsg + 1;
10073    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10074    /* Start in Urgent */
10075    in_urgent = 1;
10076    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10077    if (res < 0)
10078       goto out;
10079    vms.urgentmessages = vms.lastmsg + 1;
10080    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10081 
10082    /* Select proper mailbox FIRST!! */
10083    if (play_auto) {
10084       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10085       if (vms.urgentmessages) {
10086          in_urgent = 1;
10087          res = open_mailbox(&vms, vmu, 11);
10088       } else {
10089          in_urgent = 0;
10090          res = open_mailbox(&vms, vmu, play_folder);
10091       }
10092       if (res < 0)
10093          goto out;
10094 
10095       /* If there are no new messages, inform the user and hangup */
10096       if (vms.lastmsg == -1) {
10097          in_urgent = 0;
10098          cmd = vm_browse_messages(chan, &vms, vmu);
10099          res = 0;
10100          goto out;
10101       }
10102    } else {
10103       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10104          /* If we only have old messages start here */
10105          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10106          in_urgent = 0;
10107          play_folder = 1;
10108          if (res < 0)
10109             goto out;
10110       } else if (!vms.urgentmessages && vms.newmessages) {
10111          /* If we have new messages but none are urgent */
10112          in_urgent = 0;
10113          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10114          if (res < 0)
10115             goto out;
10116       }
10117    }
10118 
10119    if (useadsi)
10120       adsi_status(chan, &vms);
10121    res = 0;
10122 
10123    /* Check to see if this is a new user */
10124    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10125       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10126       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10127          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10128       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10129       if ((cmd == 't') || (cmd == '#')) {
10130          /* Timeout */
10131          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10132          res = 0;
10133          goto out;
10134       } else if (cmd < 0) {
10135          /* Hangup */
10136          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10137          res = -1;
10138          goto out;
10139       }
10140    }
10141 #ifdef IMAP_STORAGE
10142       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10143       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10144          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10145          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10146       }
10147       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10148       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10149          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10150          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10151       }
10152 #endif
10153 
10154    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10155    if (play_auto) {
10156       cmd = '1';
10157    } else {
10158       cmd = vm_intro(chan, vmu, &vms);
10159    }
10160    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10161 
10162    vms.repeats = 0;
10163    vms.starting = 1;
10164    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10165       /* Run main menu */
10166       switch (cmd) {
10167       case '1': /* First message */
10168          vms.curmsg = 0;
10169          /* Fall through */
10170       case '5': /* Play current message */
10171          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10172          cmd = vm_browse_messages(chan, &vms, vmu);
10173          break;
10174       case '2': /* Change folders */
10175          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10176          if (useadsi)
10177             adsi_folders(chan, 0, "Change to folder...");
10178 
10179          cmd = get_folder2(chan, "vm-changeto", 0);
10180          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10181          if (cmd == '#') {
10182             cmd = 0;
10183          } else if (cmd > 0) {
10184             cmd = cmd - '0';
10185             res = close_mailbox(&vms, vmu);
10186             if (res == ERROR_LOCK_PATH)
10187                goto out;
10188             /* If folder is not urgent, set in_urgent to zero! */
10189             if (cmd != 11) in_urgent = 0;
10190             res = open_mailbox(&vms, vmu, cmd);
10191             if (res < 0)
10192                goto out;
10193             play_folder = cmd;
10194             cmd = 0;
10195          }
10196          if (useadsi)
10197             adsi_status2(chan, &vms);
10198 
10199          if (!cmd) {
10200             cmd = vm_play_folder_name(chan, vms.vmbox);
10201          }
10202 
10203          vms.starting = 1;
10204          vms.curmsg = 0;
10205          break;
10206       case '3': /* Advanced options */
10207          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10208          cmd = 0;
10209          vms.repeats = 0;
10210          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10211             switch (cmd) {
10212             case '1': /* Reply */
10213                if (vms.lastmsg > -1 && !vms.starting) {
10214                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10215                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10216                      res = cmd;
10217                      goto out;
10218                   }
10219                } else {
10220                   cmd = ast_play_and_wait(chan, "vm-sorry");
10221                }
10222                cmd = 't';
10223                break;
10224             case '2': /* Callback */
10225                if (!vms.starting)
10226                   ast_verb(3, "Callback Requested\n");
10227                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10228                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10229                   if (cmd == 9) {
10230                      silentexit = 1;
10231                      goto out;
10232                   } else if (cmd == ERROR_LOCK_PATH) {
10233                      res = cmd;
10234                      goto out;
10235                   }
10236                } else {
10237                   cmd = ast_play_and_wait(chan, "vm-sorry");
10238                }
10239                cmd = 't';
10240                break;
10241             case '3': /* Envelope */
10242                if (vms.lastmsg > -1 && !vms.starting) {
10243                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10244                   if (cmd == ERROR_LOCK_PATH) {
10245                      res = cmd;
10246                      goto out;
10247                   }
10248                } else {
10249                   cmd = ast_play_and_wait(chan, "vm-sorry");
10250                }
10251                cmd = 't';
10252                break;
10253             case '4': /* Dialout */
10254                if (!ast_strlen_zero(vmu->dialout)) {
10255                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10256                   if (cmd == 9) {
10257                      silentexit = 1;
10258                      goto out;
10259                   }
10260                } else {
10261                   cmd = ast_play_and_wait(chan, "vm-sorry");
10262                }
10263                cmd = 't';
10264                break;
10265 
10266             case '5': /* Leave VoiceMail */
10267                if (ast_test_flag(vmu, VM_SVMAIL)) {
10268                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10269                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10270                      res = cmd;
10271                      goto out;
10272                   }
10273                } else {
10274                   cmd = ast_play_and_wait(chan, "vm-sorry");
10275                }
10276                cmd = 't';
10277                break;
10278 
10279             case '*': /* Return to main menu */
10280                cmd = 't';
10281                break;
10282 
10283             default:
10284                cmd = 0;
10285                if (!vms.starting) {
10286                   cmd = ast_play_and_wait(chan, "vm-toreply");
10287                }
10288                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10289                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10290                }
10291                if (!cmd && !vms.starting) {
10292                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10293                }
10294                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10295                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10296                }
10297                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10298                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10299                }
10300                if (!cmd) {
10301                   cmd = ast_play_and_wait(chan, "vm-starmain");
10302                }
10303                if (!cmd) {
10304                   cmd = ast_waitfordigit(chan, 6000);
10305                }
10306                if (!cmd) {
10307                   vms.repeats++;
10308                }
10309                if (vms.repeats > 3) {
10310                   cmd = 't';
10311                }
10312                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10313             }
10314          }
10315          if (cmd == 't') {
10316             cmd = 0;
10317             vms.repeats = 0;
10318          }
10319          break;
10320       case '4': /* Go to the previous message */
10321          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10322          if (vms.curmsg > 0) {
10323             vms.curmsg--;
10324             cmd = play_message(chan, vmu, &vms);
10325          } else {
10326             /* Check if we were listening to new
10327                messages.  If so, go to Urgent messages
10328                instead of saying "no more messages"
10329             */
10330             if (in_urgent == 0 && vms.urgentmessages > 0) {
10331                /* Check for Urgent messages */
10332                in_urgent = 1;
10333                res = close_mailbox(&vms, vmu);
10334                if (res == ERROR_LOCK_PATH)
10335                   goto out;
10336                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10337                if (res < 0)
10338                   goto out;
10339                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10340                vms.curmsg = vms.lastmsg;
10341                if (vms.lastmsg < 0) {
10342                   cmd = ast_play_and_wait(chan, "vm-nomore");
10343                }
10344             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10345                vms.curmsg = vms.lastmsg;
10346                cmd = play_message(chan, vmu, &vms);
10347             } else {
10348                cmd = ast_play_and_wait(chan, "vm-nomore");
10349             }
10350          }
10351          break;
10352       case '6': /* Go to the next message */
10353          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10354          if (vms.curmsg < vms.lastmsg) {
10355             vms.curmsg++;
10356             cmd = play_message(chan, vmu, &vms);
10357          } else {
10358             if (in_urgent && vms.newmessages > 0) {
10359                /* Check if we were listening to urgent
10360                 * messages.  If so, go to regular new messages
10361                 * instead of saying "no more messages"
10362                 */
10363                in_urgent = 0;
10364                res = close_mailbox(&vms, vmu);
10365                if (res == ERROR_LOCK_PATH)
10366                   goto out;
10367                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10368                if (res < 0)
10369                   goto out;
10370                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10371                vms.curmsg = -1;
10372                if (vms.lastmsg < 0) {
10373                   cmd = ast_play_and_wait(chan, "vm-nomore");
10374                }
10375             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10376                vms.curmsg = 0;
10377                cmd = play_message(chan, vmu, &vms);
10378             } else {
10379                cmd = ast_play_and_wait(chan, "vm-nomore");
10380             }
10381          }
10382          break;
10383       case '7': /* Delete the current message */
10384          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10385             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10386             if (useadsi)
10387                adsi_delete(chan, &vms);
10388             if (vms.deleted[vms.curmsg]) {
10389                if (play_folder == 0) {
10390                   if (in_urgent) {
10391                      vms.urgentmessages--;
10392                   } else {
10393                      vms.newmessages--;
10394                   }
10395                }
10396                else if (play_folder == 1)
10397                   vms.oldmessages--;
10398                cmd = ast_play_and_wait(chan, "vm-deleted");
10399             } else {
10400                if (play_folder == 0) {
10401                   if (in_urgent) {
10402                      vms.urgentmessages++;
10403                   } else {
10404                      vms.newmessages++;
10405                   }
10406                }
10407                else if (play_folder == 1)
10408                   vms.oldmessages++;
10409                cmd = ast_play_and_wait(chan, "vm-undeleted");
10410             }
10411             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10412                if (vms.curmsg < vms.lastmsg) {
10413                   vms.curmsg++;
10414                   cmd = play_message(chan, vmu, &vms);
10415                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10416                   vms.curmsg = 0;
10417                   cmd = play_message(chan, vmu, &vms);
10418                } else {
10419                   /* Check if we were listening to urgent
10420                      messages.  If so, go to regular new messages
10421                      instead of saying "no more messages"
10422                   */
10423                   if (in_urgent == 1) {
10424                      /* Check for new messages */
10425                      in_urgent = 0;
10426                      res = close_mailbox(&vms, vmu);
10427                      if (res == ERROR_LOCK_PATH)
10428                         goto out;
10429                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10430                      if (res < 0)
10431                         goto out;
10432                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10433                      vms.curmsg = -1;
10434                      if (vms.lastmsg < 0) {
10435                         cmd = ast_play_and_wait(chan, "vm-nomore");
10436                      }
10437                   } else {
10438                      cmd = ast_play_and_wait(chan, "vm-nomore");
10439                   }
10440                }
10441             }
10442          } else /* Delete not valid if we haven't selected a message */
10443             cmd = 0;
10444 #ifdef IMAP_STORAGE
10445          deleted = 1;
10446 #endif
10447          break;
10448    
10449       case '8': /* Forward the current message */
10450          if (vms.lastmsg > -1) {
10451             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10452             if (cmd == ERROR_LOCK_PATH) {
10453                res = cmd;
10454                goto out;
10455             }
10456          } else {
10457             /* Check if we were listening to urgent
10458                messages.  If so, go to regular new messages
10459                instead of saying "no more messages"
10460             */
10461             if (in_urgent == 1 && vms.newmessages > 0) {
10462                /* Check for new messages */
10463                in_urgent = 0;
10464                res = close_mailbox(&vms, vmu);
10465                if (res == ERROR_LOCK_PATH)
10466                   goto out;
10467                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10468                if (res < 0)
10469                   goto out;
10470                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10471                vms.curmsg = -1;
10472                if (vms.lastmsg < 0) {
10473                   cmd = ast_play_and_wait(chan, "vm-nomore");
10474                }
10475             } else {
10476                cmd = ast_play_and_wait(chan, "vm-nomore");
10477             }
10478          }
10479          break;
10480       case '9': /* Save message to folder */
10481          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10482          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10483             /* No message selected */
10484             cmd = 0;
10485             break;
10486          }
10487          if (useadsi)
10488             adsi_folders(chan, 1, "Save to folder...");
10489          cmd = get_folder2(chan, "vm-savefolder", 1);
10490          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10491          box = 0; /* Shut up compiler */
10492          if (cmd == '#') {
10493             cmd = 0;
10494             break;
10495          } else if (cmd > 0) {
10496             box = cmd = cmd - '0';
10497             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10498             if (cmd == ERROR_LOCK_PATH) {
10499                res = cmd;
10500                goto out;
10501 #ifndef IMAP_STORAGE
10502             } else if (!cmd) {
10503                vms.deleted[vms.curmsg] = 1;
10504 #endif
10505             } else {
10506                vms.deleted[vms.curmsg] = 0;
10507                vms.heard[vms.curmsg] = 0;
10508             }
10509          }
10510          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10511          if (useadsi)
10512             adsi_message(chan, &vms);
10513          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10514          if (!cmd) {
10515             cmd = ast_play_and_wait(chan, "vm-message");
10516             if (!cmd) 
10517                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10518             if (!cmd)
10519                cmd = ast_play_and_wait(chan, "vm-savedto");
10520             if (!cmd)
10521                cmd = vm_play_folder_name(chan, vms.fn);
10522          } else {
10523             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10524          }
10525          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10526             if (vms.curmsg < vms.lastmsg) {
10527                vms.curmsg++;
10528                cmd = play_message(chan, vmu, &vms);
10529             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10530                vms.curmsg = 0;
10531                cmd = play_message(chan, vmu, &vms);
10532             } else {
10533                /* Check if we were listening to urgent
10534                   messages.  If so, go to regular new messages
10535                   instead of saying "no more messages"
10536                */
10537                if (in_urgent == 1 && vms.newmessages > 0) {
10538                   /* Check for new messages */
10539                   in_urgent = 0;
10540                   res = close_mailbox(&vms, vmu);
10541                   if (res == ERROR_LOCK_PATH)
10542                      goto out;
10543                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10544                   if (res < 0)
10545                      goto out;
10546                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10547                   vms.curmsg = -1;
10548                   if (vms.lastmsg < 0) {
10549                      cmd = ast_play_and_wait(chan, "vm-nomore");
10550                   }
10551                } else {
10552                   cmd = ast_play_and_wait(chan, "vm-nomore");
10553                }
10554             }
10555          }
10556          break;
10557       case '*': /* Help */
10558          if (!vms.starting) {
10559             cmd = ast_play_and_wait(chan, "vm-onefor");
10560             if (!strncasecmp(chan->language, "he", 2)) {
10561                cmd = ast_play_and_wait(chan, "vm-for");
10562             }
10563             if (!cmd)
10564                cmd = vm_play_folder_name(chan, vms.vmbox);
10565             if (!cmd)
10566                cmd = ast_play_and_wait(chan, "vm-opts");
10567             if (!cmd)
10568                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10569          } else
10570             cmd = 0;
10571          break;
10572       case '0': /* Mailbox options */
10573          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10574          if (useadsi)
10575             adsi_status(chan, &vms);
10576          break;
10577       default: /* Nothing */
10578          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10579          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10580          break;
10581       }
10582    }
10583    if ((cmd == 't') || (cmd == '#')) {
10584       /* Timeout */
10585       res = 0;
10586    } else {
10587       /* Hangup */
10588       res = -1;
10589    }
10590 
10591 out:
10592    if (res > -1) {
10593       ast_stopstream(chan);
10594       adsi_goodbye(chan);
10595       if (valid && res != OPERATOR_EXIT) {
10596          if (silentexit)
10597             res = ast_play_and_wait(chan, "vm-dialout");
10598          else 
10599             res = ast_play_and_wait(chan, "vm-goodbye");
10600       }
10601       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10602          res = 0;
10603       }
10604       if (useadsi)
10605          ast_adsi_unload_session(chan);
10606    }
10607    if (vmu)
10608       close_mailbox(&vms, vmu);
10609    if (valid) {
10610       int new = 0, old = 0, urgent = 0;
10611       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10612       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10613       /* Urgent flag not passwd to externnotify here */
10614       run_externnotify(vmu->context, vmu->mailbox, NULL);
10615       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10616       queue_mwi_event(ext_context, urgent, new, old);
10617    }
10618 #ifdef IMAP_STORAGE
10619    /* expunge message - use UID Expunge if supported on IMAP server*/
10620    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10621    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10622       ast_mutex_lock(&vms.lock);
10623 #ifdef HAVE_IMAP_TK2006
10624       if (LEVELUIDPLUS (vms.mailstream)) {
10625          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10626       } else 
10627 #endif
10628          mail_expunge(vms.mailstream);
10629       ast_mutex_unlock(&vms.lock);
10630    }
10631    /*  before we delete the state, we should copy pertinent info
10632     *  back to the persistent model */
10633    if (vmu) {
10634       vmstate_delete(&vms);
10635    }
10636 #endif
10637    if (vmu)
10638       free_user(vmu);
10639 
10640 #ifdef IMAP_STORAGE
10641    pthread_setspecific(ts_vmstate.key, NULL);
10642 #endif
10643    return res;
10644 }
10645 
10646 static int vm_exec(struct ast_channel *chan, const char *data)
10647 {
10648    int res = 0;
10649    char *tmp;
10650    struct leave_vm_options leave_options;
10651    struct ast_flags flags = { 0 };
10652    char *opts[OPT_ARG_ARRAY_SIZE];
10653    AST_DECLARE_APP_ARGS(args,
10654       AST_APP_ARG(argv0);
10655       AST_APP_ARG(argv1);
10656    );
10657    
10658    memset(&leave_options, 0, sizeof(leave_options));
10659 
10660    if (chan->_state != AST_STATE_UP)
10661       ast_answer(chan);
10662 
10663    if (!ast_strlen_zero(data)) {
10664       tmp = ast_strdupa(data);
10665       AST_STANDARD_APP_ARGS(args, tmp);
10666       if (args.argc == 2) {
10667          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10668             return -1;
10669          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10670          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10671             int gain;
10672 
10673             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10674                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10675                return -1;
10676             } else {
10677                leave_options.record_gain = (signed char) gain;
10678             }
10679          }
10680          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10681             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10682                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10683          }
10684       }
10685    } else {
10686       char temp[256];
10687       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10688       if (res < 0)
10689          return res;
10690       if (ast_strlen_zero(temp))
10691          return 0;
10692       args.argv0 = ast_strdupa(temp);
10693    }
10694 
10695    res = leave_voicemail(chan, args.argv0, &leave_options);
10696    if (res == 't') {
10697       ast_play_and_wait(chan, "vm-goodbye");
10698       res = 0;
10699    }
10700 
10701    if (res == OPERATOR_EXIT) {
10702       res = 0;
10703    }
10704 
10705    if (res == ERROR_LOCK_PATH) {
10706       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10707       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10708       res = 0;
10709    }
10710 
10711    return res;
10712 }
10713 
10714 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10715 {
10716    struct ast_vm_user *vmu;
10717 
10718    if (!ast_strlen_zero(box) && box[0] == '*') {
10719       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10720             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10721             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10722             "\n\tand will be ignored.\n", box, context);
10723       return NULL;
10724    }
10725 
10726    AST_LIST_TRAVERSE(&users, vmu, list) {
10727       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10728          if (strcasecmp(vmu->context, context)) {
10729             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10730                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10731                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10732                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10733          }
10734          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10735          return NULL;
10736       }
10737       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10738          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10739          return NULL;
10740       }
10741    }
10742    
10743    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10744       return NULL;
10745    
10746    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10747    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10748 
10749    AST_LIST_INSERT_TAIL(&users, vmu, list);
10750    
10751    return vmu;
10752 }
10753 
10754 static int append_mailbox(const char *context, const char *box, const char *data)
10755 {
10756    /* Assumes lock is already held */
10757    char *tmp;
10758    char *stringp;
10759    char *s;
10760    struct ast_vm_user *vmu;
10761    char *mailbox_full;
10762    int new = 0, old = 0, urgent = 0;
10763    char secretfn[PATH_MAX] = "";
10764 
10765    tmp = ast_strdupa(data);
10766 
10767    if (!(vmu = find_or_create(context, box)))
10768       return -1;
10769 
10770    populate_defaults(vmu);
10771 
10772    stringp = tmp;
10773    if ((s = strsep(&stringp, ","))) {
10774       if (!ast_strlen_zero(s) && s[0] == '*') {
10775          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10776             "\n\tmust be reset in voicemail.conf.\n", box);
10777       }
10778       /* assign password regardless of validity to prevent NULL password from being assigned */
10779       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10780    }
10781    if (stringp && (s = strsep(&stringp, ","))) {
10782       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10783    }
10784    if (stringp && (s = strsep(&stringp, ","))) {
10785       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10786    }
10787    if (stringp && (s = strsep(&stringp, ","))) {
10788       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10789    }
10790    if (stringp && (s = strsep(&stringp, ","))) {
10791       apply_options(vmu, s);
10792    }
10793 
10794    switch (vmu->passwordlocation) {
10795    case OPT_PWLOC_SPOOLDIR:
10796       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10797       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10798    }
10799 
10800    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10801    strcpy(mailbox_full, box);
10802    strcat(mailbox_full, "@");
10803    strcat(mailbox_full, context);
10804 
10805    inboxcount2(mailbox_full, &urgent, &new, &old);
10806    queue_mwi_event(mailbox_full, urgent, new, old);
10807 
10808    return 0;
10809 }
10810 
10811 AST_TEST_DEFINE(test_voicemail_vmuser)
10812 {
10813    int res = 0;
10814    struct ast_vm_user *vmu;
10815    /* language parameter seems to only be used for display in manager action */
10816    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10817       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10818       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10819       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10820       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10821       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10822       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10823       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10824       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10825 #ifdef IMAP_STORAGE
10826    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10827       "imapfolder=INBOX|imapvmshareid=6000";
10828 #endif
10829 
10830    switch (cmd) {
10831    case TEST_INIT:
10832       info->name = "vmuser";
10833       info->category = "/apps/app_voicemail/";
10834       info->summary = "Vmuser unit test";
10835       info->description =
10836          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10837       return AST_TEST_NOT_RUN;
10838    case TEST_EXECUTE:
10839       break;
10840    }
10841 
10842    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10843       return AST_TEST_NOT_RUN;
10844    }
10845    populate_defaults(vmu);
10846    ast_set_flag(vmu, VM_ALLOCED);
10847 
10848    apply_options(vmu, options_string);
10849 
10850    if (!ast_test_flag(vmu, VM_ATTACH)) {
10851       ast_test_status_update(test, "Parse failure for attach option\n");
10852       res = 1;
10853    }
10854    if (strcasecmp(vmu->attachfmt, "wav49")) {
10855       ast_test_status_update(test, "Parse failure for attachftm option\n");
10856       res = 1;
10857    }
10858    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10859       ast_test_status_update(test, "Parse failure for serveremail option\n");
10860       res = 1;
10861    }
10862    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10863       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10864       res = 1;
10865    }
10866    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10867       ast_test_status_update(test, "Parse failure for emailbody option\n");
10868       res = 1;
10869    }
10870    if (strcasecmp(vmu->zonetag, "central")) {
10871       ast_test_status_update(test, "Parse failure for tz option\n");
10872       res = 1;
10873    }
10874    if (!ast_test_flag(vmu, VM_DELETE)) {
10875       ast_test_status_update(test, "Parse failure for delete option\n");
10876       res = 1;
10877    }
10878    if (!ast_test_flag(vmu, VM_SAYCID)) {
10879       ast_test_status_update(test, "Parse failure for saycid option\n");
10880       res = 1;
10881    }
10882    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10883       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10884       res = 1;
10885    }
10886    if (!ast_test_flag(vmu, VM_REVIEW)) {
10887       ast_test_status_update(test, "Parse failure for review option\n");
10888       res = 1;
10889    }
10890    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10891       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10892       res = 1;
10893    }
10894    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10895       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10896       res = 1;
10897    }
10898    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10899       ast_test_status_update(test, "Parse failure for operator option\n");
10900       res = 1;
10901    }
10902    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10903       ast_test_status_update(test, "Parse failure for envelope option\n");
10904       res = 1;
10905    }
10906    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10907       ast_test_status_update(test, "Parse failure for moveheard option\n");
10908       res = 1;
10909    }
10910    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10911       ast_test_status_update(test, "Parse failure for sayduration option\n");
10912       res = 1;
10913    }
10914    if (vmu->saydurationm != 5) {
10915       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10916       res = 1;
10917    }
10918    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10919       ast_test_status_update(test, "Parse failure for forcename option\n");
10920       res = 1;
10921    }
10922    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10923       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10924       res = 1;
10925    }
10926    if (strcasecmp(vmu->callback, "somecontext")) {
10927       ast_test_status_update(test, "Parse failure for callbacks option\n");
10928       res = 1;
10929    }
10930    if (strcasecmp(vmu->dialout, "somecontext2")) {
10931       ast_test_status_update(test, "Parse failure for dialout option\n");
10932       res = 1;
10933    }
10934    if (strcasecmp(vmu->exit, "somecontext3")) {
10935       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10936       res = 1;
10937    }
10938    if (vmu->minsecs != 10) {
10939       ast_test_status_update(test, "Parse failure for minsecs option\n");
10940       res = 1;
10941    }
10942    if (vmu->maxsecs != 100) {
10943       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10944       res = 1;
10945    }
10946    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10947       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10948       res = 1;
10949    }
10950    if (vmu->maxdeletedmsg != 50) {
10951       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10952       res = 1;
10953    }
10954    if (vmu->volgain != 1.3) {
10955       ast_test_status_update(test, "Parse failure for volgain option\n");
10956       res = 1;
10957    }
10958    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10959       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10960       res = 1;
10961    }
10962 #ifdef IMAP_STORAGE
10963    apply_options(vmu, option_string2);
10964 
10965    if (strcasecmp(vmu->imapuser, "imapuser")) {
10966       ast_test_status_update(test, "Parse failure for imapuser option\n");
10967       res = 1;
10968    }
10969    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10970       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10971       res = 1;
10972    }
10973    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10974       ast_test_status_update(test, "Parse failure for imapfolder option\n");
10975       res = 1;
10976    }
10977    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10978       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10979       res = 1;
10980    }
10981 #endif
10982 
10983    free_user(vmu);
10984    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10985 }
10986 
10987 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10988 {
10989    struct ast_vm_user svm;
10990    char *context, *box;
10991    AST_DECLARE_APP_ARGS(args,
10992       AST_APP_ARG(mbox);
10993       AST_APP_ARG(options);
10994    );
10995    static int dep_warning = 0;
10996 
10997    if (ast_strlen_zero(data)) {
10998       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10999       return -1;
11000    }
11001 
11002    if (!dep_warning) {
11003       dep_warning = 1;
11004       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11005    }
11006 
11007    box = ast_strdupa(data);
11008 
11009    AST_STANDARD_APP_ARGS(args, box);
11010 
11011    if (args.options) {
11012    }
11013 
11014    if ((context = strchr(args.mbox, '@'))) {
11015       *context = '\0';
11016       context++;
11017    }
11018 
11019    if (find_user(&svm, context, args.mbox)) {
11020       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11021    } else
11022       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11023 
11024    return 0;
11025 }
11026 
11027 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11028 {
11029    struct ast_vm_user svm;
11030    AST_DECLARE_APP_ARGS(arg,
11031       AST_APP_ARG(mbox);
11032       AST_APP_ARG(context);
11033    );
11034 
11035    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11036 
11037    if (ast_strlen_zero(arg.mbox)) {
11038       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11039       return -1;
11040    }
11041 
11042    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11043    return 0;
11044 }
11045 
11046 static struct ast_custom_function mailbox_exists_acf = {
11047    .name = "MAILBOX_EXISTS",
11048    .read = acf_mailbox_exists,
11049 };
11050 
11051 static int vmauthenticate(struct ast_channel *chan, const char *data)
11052 {
11053    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11054    struct ast_vm_user vmus;
11055    char *options = NULL;
11056    int silent = 0, skipuser = 0;
11057    int res = -1;
11058    
11059    if (data) {
11060       s = ast_strdupa(data);
11061       user = strsep(&s, ",");
11062       options = strsep(&s, ",");
11063       if (user) {
11064          s = user;
11065          user = strsep(&s, "@");
11066          context = strsep(&s, "");
11067          if (!ast_strlen_zero(user))
11068             skipuser++;
11069          ast_copy_string(mailbox, user, sizeof(mailbox));
11070       }
11071    }
11072 
11073    if (options) {
11074       silent = (strchr(options, 's')) != NULL;
11075    }
11076 
11077    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11078       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11079       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11080       ast_play_and_wait(chan, "auth-thankyou");
11081       res = 0;
11082    } else if (mailbox[0] == '*') {
11083       /* user entered '*' */
11084       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11085          res = 0; /* prevent hangup */
11086       }
11087    }
11088 
11089    return res;
11090 }
11091 
11092 static char *show_users_realtime(int fd, const char *context)
11093 {
11094    struct ast_config *cfg;
11095    const char *cat = NULL;
11096 
11097    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11098       "context", context, SENTINEL))) {
11099       return CLI_FAILURE;
11100    }
11101 
11102    ast_cli(fd,
11103       "\n"
11104       "=============================================================\n"
11105       "=== Configured Voicemail Users ==============================\n"
11106       "=============================================================\n"
11107       "===\n");
11108 
11109    while ((cat = ast_category_browse(cfg, cat))) {
11110       struct ast_variable *var = NULL;
11111       ast_cli(fd,
11112          "=== Mailbox ...\n"
11113          "===\n");
11114       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11115          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11116       ast_cli(fd,
11117          "===\n"
11118          "=== ---------------------------------------------------------\n"
11119          "===\n");
11120    }
11121 
11122    ast_cli(fd,
11123       "=============================================================\n"
11124       "\n");
11125 
11126    ast_config_destroy(cfg);
11127 
11128    return CLI_SUCCESS;
11129 }
11130 
11131 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11132 {
11133    int which = 0;
11134    int wordlen;
11135    struct ast_vm_user *vmu;
11136    const char *context = "";
11137 
11138    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11139    if (pos > 4)
11140       return NULL;
11141    if (pos == 3)
11142       return (state == 0) ? ast_strdup("for") : NULL;
11143    wordlen = strlen(word);
11144    AST_LIST_TRAVERSE(&users, vmu, list) {
11145       if (!strncasecmp(word, vmu->context, wordlen)) {
11146          if (context && strcmp(context, vmu->context) && ++which > state)
11147             return ast_strdup(vmu->context);
11148          /* ignore repeated contexts ? */
11149          context = vmu->context;
11150       }
11151    }
11152    return NULL;
11153 }
11154 
11155 /*! \brief Show a list of voicemail users in the CLI */
11156 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11157 {
11158    struct ast_vm_user *vmu;
11159 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11160    const char *context = NULL;
11161    int users_counter = 0;
11162 
11163    switch (cmd) {
11164    case CLI_INIT:
11165       e->command = "voicemail show users";
11166       e->usage =
11167          "Usage: voicemail show users [for <context>]\n"
11168          "       Lists all mailboxes currently set up\n";
11169       return NULL;
11170    case CLI_GENERATE:
11171       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11172    }  
11173 
11174    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11175       return CLI_SHOWUSAGE;
11176    if (a->argc == 5) {
11177       if (strcmp(a->argv[3],"for"))
11178          return CLI_SHOWUSAGE;
11179       context = a->argv[4];
11180    }
11181 
11182    if (ast_check_realtime("voicemail")) {
11183       if (!context) {
11184          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11185          return CLI_SHOWUSAGE;
11186       }
11187       return show_users_realtime(a->fd, context);
11188    }
11189 
11190    AST_LIST_LOCK(&users);
11191    if (AST_LIST_EMPTY(&users)) {
11192       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11193       AST_LIST_UNLOCK(&users);
11194       return CLI_FAILURE;
11195    }
11196    if (!context) {
11197       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11198    } else {
11199       int count = 0;
11200       AST_LIST_TRAVERSE(&users, vmu, list) {
11201          if (!strcmp(context, vmu->context)) {
11202             count++;
11203             break;
11204          }
11205       }
11206       if (count) {
11207          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11208       } else {
11209          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11210          AST_LIST_UNLOCK(&users);
11211          return CLI_FAILURE;
11212       }
11213    }
11214    AST_LIST_TRAVERSE(&users, vmu, list) {
11215       int newmsgs = 0, oldmsgs = 0;
11216       char count[12], tmp[256] = "";
11217 
11218       if (!context || !strcmp(context, vmu->context)) {
11219          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11220          inboxcount(tmp, &newmsgs, &oldmsgs);
11221          snprintf(count, sizeof(count), "%d", newmsgs);
11222          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11223          users_counter++;
11224       }
11225    }
11226    AST_LIST_UNLOCK(&users);
11227    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11228    return CLI_SUCCESS;
11229 }
11230 
11231 /*! \brief Show a list of voicemail zones in the CLI */
11232 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11233 {
11234    struct vm_zone *zone;
11235 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11236    char *res = CLI_SUCCESS;
11237 
11238    switch (cmd) {
11239    case CLI_INIT:
11240       e->command = "voicemail show zones";
11241       e->usage =
11242          "Usage: voicemail show zones\n"
11243          "       Lists zone message formats\n";
11244       return NULL;
11245    case CLI_GENERATE:
11246       return NULL;
11247    }
11248 
11249    if (a->argc != 3)
11250       return CLI_SHOWUSAGE;
11251 
11252    AST_LIST_LOCK(&zones);
11253    if (!AST_LIST_EMPTY(&zones)) {
11254       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11255       AST_LIST_TRAVERSE(&zones, zone, list) {
11256          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11257       }
11258    } else {
11259       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11260       res = CLI_FAILURE;
11261    }
11262    AST_LIST_UNLOCK(&zones);
11263 
11264    return res;
11265 }
11266 
11267 /*! \brief Reload voicemail configuration from the CLI */
11268 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11269 {
11270    switch (cmd) {
11271    case CLI_INIT:
11272       e->command = "voicemail reload";
11273       e->usage =
11274          "Usage: voicemail reload\n"
11275          "       Reload voicemail configuration\n";
11276       return NULL;
11277    case CLI_GENERATE:
11278       return NULL;
11279    }
11280 
11281    if (a->argc != 2)
11282       return CLI_SHOWUSAGE;
11283 
11284    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11285    load_config(1);
11286    
11287    return CLI_SUCCESS;
11288 }
11289 
11290 static struct ast_cli_entry cli_voicemail[] = {
11291    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11292    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11293    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11294 };
11295 
11296 #ifdef IMAP_STORAGE
11297    #define DATA_EXPORT_VM_USERS(USER)              \
11298       USER(ast_vm_user, context, AST_DATA_STRING)        \
11299       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11300       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11301       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11302       USER(ast_vm_user, email, AST_DATA_STRING)       \
11303       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11304       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11305       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11306       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11307       USER(ast_vm_user, language, AST_DATA_STRING)       \
11308       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11309       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11310       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11311       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11312       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11313       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11314       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11315       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11316       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11317       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11318       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11319       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11320       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11321       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11322       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11323 #else
11324    #define DATA_EXPORT_VM_USERS(USER)              \
11325       USER(ast_vm_user, context, AST_DATA_STRING)        \
11326       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11327       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11328       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11329       USER(ast_vm_user, email, AST_DATA_STRING)       \
11330       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11331       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11332       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11333       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11334       USER(ast_vm_user, language, AST_DATA_STRING)       \
11335       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11336       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11337       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11338       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11339       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11340       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11341       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11342       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11343       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11344       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11345       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11346       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11347 #endif
11348 
11349 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11350 
11351 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11352    ZONE(vm_zone, name, AST_DATA_STRING)      \
11353    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11354    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11355 
11356 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11357 
11358 /*!
11359  * \internal
11360  * \brief Add voicemail user to the data_root.
11361  * \param[in] search The search tree.
11362  * \param[in] data_root The main result node.
11363  * \param[in] user The voicemail user.
11364  */
11365 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11366     struct ast_data *data_root, struct ast_vm_user *user)
11367 {
11368    struct ast_data *data_user, *data_zone;
11369    struct ast_data *data_state;
11370    struct vm_zone *zone = NULL;
11371    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11372    char ext_context[256] = "";
11373 
11374    data_user = ast_data_add_node(data_root, "user");
11375    if (!data_user) {
11376       return -1;
11377    }
11378 
11379    ast_data_add_structure(ast_vm_user, data_user, user);
11380 
11381    AST_LIST_LOCK(&zones);
11382    AST_LIST_TRAVERSE(&zones, zone, list) {
11383       if (!strcmp(zone->name, user->zonetag)) {
11384          break;
11385       }
11386    }
11387    AST_LIST_UNLOCK(&zones);
11388 
11389    /* state */
11390    data_state = ast_data_add_node(data_user, "state");
11391    if (!data_state) {
11392       return -1;
11393    }
11394    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11395    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11396    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11397    ast_data_add_int(data_state, "newmsg", newmsg);
11398    ast_data_add_int(data_state, "oldmsg", oldmsg);
11399 
11400    if (zone) {
11401       data_zone = ast_data_add_node(data_user, "zone");
11402       ast_data_add_structure(vm_zone, data_zone, zone);
11403    }
11404 
11405    if (!ast_data_search_match(search, data_user)) {
11406       ast_data_remove_node(data_root, data_user);
11407    }
11408 
11409    return 0;
11410 }
11411 
11412 static int vm_users_data_provider_get(const struct ast_data_search *search,
11413    struct ast_data *data_root)
11414 {
11415    struct ast_vm_user *user;
11416 
11417    AST_LIST_LOCK(&users);
11418    AST_LIST_TRAVERSE(&users, user, list) {
11419       vm_users_data_provider_get_helper(search, data_root, user);
11420    }
11421    AST_LIST_UNLOCK(&users);
11422 
11423    return 0;
11424 }
11425 
11426 static const struct ast_data_handler vm_users_data_provider = {
11427    .version = AST_DATA_HANDLER_VERSION,
11428    .get = vm_users_data_provider_get
11429 };
11430 
11431 static const struct ast_data_entry vm_data_providers[] = {
11432    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11433 };
11434 
11435 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11436 {
11437    int new = 0, old = 0, urgent = 0;
11438 
11439    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11440 
11441    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11442       mwi_sub->old_urgent = urgent;
11443       mwi_sub->old_new = new;
11444       mwi_sub->old_old = old;
11445       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11446       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11447    }
11448 }
11449 
11450 static void poll_subscribed_mailboxes(void)
11451 {
11452    struct mwi_sub *mwi_sub;
11453 
11454    AST_RWLIST_RDLOCK(&mwi_subs);
11455    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11456       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11457          poll_subscribed_mailbox(mwi_sub);
11458       }
11459    }
11460    AST_RWLIST_UNLOCK(&mwi_subs);
11461 }
11462 
11463 static void *mb_poll_thread(void *data)
11464 {
11465    while (poll_thread_run) {
11466       struct timespec ts = { 0, };
11467       struct timeval wait;
11468 
11469       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11470       ts.tv_sec = wait.tv_sec;
11471       ts.tv_nsec = wait.tv_usec * 1000;
11472 
11473       ast_mutex_lock(&poll_lock);
11474       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11475       ast_mutex_unlock(&poll_lock);
11476 
11477       if (!poll_thread_run)
11478          break;
11479 
11480       poll_subscribed_mailboxes();
11481    }
11482 
11483    return NULL;
11484 }
11485 
11486 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11487 {
11488    ast_free(mwi_sub);
11489 }
11490 
11491 static int handle_unsubscribe(void *datap)
11492 {
11493    struct mwi_sub *mwi_sub;
11494    uint32_t *uniqueid = datap;
11495    
11496    AST_RWLIST_WRLOCK(&mwi_subs);
11497    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11498       if (mwi_sub->uniqueid == *uniqueid) {
11499          AST_LIST_REMOVE_CURRENT(entry);
11500          break;
11501       }
11502    }
11503    AST_RWLIST_TRAVERSE_SAFE_END
11504    AST_RWLIST_UNLOCK(&mwi_subs);
11505 
11506    if (mwi_sub)
11507       mwi_sub_destroy(mwi_sub);
11508 
11509    ast_free(uniqueid);  
11510    return 0;
11511 }
11512 
11513 static int handle_subscribe(void *datap)
11514 {
11515    unsigned int len;
11516    struct mwi_sub *mwi_sub;
11517    struct mwi_sub_task *p = datap;
11518 
11519    len = sizeof(*mwi_sub);
11520    if (!ast_strlen_zero(p->mailbox))
11521       len += strlen(p->mailbox);
11522 
11523    if (!ast_strlen_zero(p->context))
11524       len += strlen(p->context) + 1; /* Allow for seperator */
11525 
11526    if (!(mwi_sub = ast_calloc(1, len)))
11527       return -1;
11528 
11529    mwi_sub->uniqueid = p->uniqueid;
11530    if (!ast_strlen_zero(p->mailbox))
11531       strcpy(mwi_sub->mailbox, p->mailbox);
11532 
11533    if (!ast_strlen_zero(p->context)) {
11534       strcat(mwi_sub->mailbox, "@");
11535       strcat(mwi_sub->mailbox, p->context);
11536    }
11537 
11538    AST_RWLIST_WRLOCK(&mwi_subs);
11539    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11540    AST_RWLIST_UNLOCK(&mwi_subs);
11541    ast_free((void *) p->mailbox);
11542    ast_free((void *) p->context);
11543    ast_free(p);
11544    poll_subscribed_mailbox(mwi_sub);
11545    return 0;
11546 }
11547 
11548 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11549 {
11550    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11551 
11552    if (!uniqueid) {
11553       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11554       return;
11555    }
11556 
11557    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11558       ast_free(uniqueid);
11559       return;
11560    }
11561 
11562    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11563       ast_free(uniqueid);
11564       return;
11565    }
11566 
11567    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11568    *uniqueid = u;
11569    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11570       ast_free(uniqueid);
11571    }
11572 }
11573 
11574 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11575 {
11576    struct mwi_sub_task *mwist;
11577    
11578    if (ast_event_get_type(event) != AST_EVENT_SUB)
11579       return;
11580 
11581    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11582       return;
11583 
11584    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11585       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11586       return;
11587    }
11588    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11589    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11590    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11591    
11592    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11593       ast_free(mwist);
11594    }
11595 }
11596 
11597 static void start_poll_thread(void)
11598 {
11599    int errcode;
11600    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11601       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11602       AST_EVENT_IE_END);
11603 
11604    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11605       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11606       AST_EVENT_IE_END);
11607 
11608    if (mwi_sub_sub)
11609       ast_event_report_subs(mwi_sub_sub);
11610 
11611    poll_thread_run = 1;
11612 
11613    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11614       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11615    }
11616 }
11617 
11618 static void stop_poll_thread(void)
11619 {
11620    poll_thread_run = 0;
11621 
11622    if (mwi_sub_sub) {
11623       ast_event_unsubscribe(mwi_sub_sub);
11624       mwi_sub_sub = NULL;
11625    }
11626 
11627    if (mwi_unsub_sub) {
11628       ast_event_unsubscribe(mwi_unsub_sub);
11629       mwi_unsub_sub = NULL;
11630    }
11631 
11632    ast_mutex_lock(&poll_lock);
11633    ast_cond_signal(&poll_cond);
11634    ast_mutex_unlock(&poll_lock);
11635 
11636    pthread_join(poll_thread, NULL);
11637 
11638    poll_thread = AST_PTHREADT_NULL;
11639 }
11640 
11641 /*! \brief Manager list voicemail users command */
11642 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11643 {
11644    struct ast_vm_user *vmu = NULL;
11645    const char *id = astman_get_header(m, "ActionID");
11646    char actionid[128] = "";
11647 
11648    if (!ast_strlen_zero(id))
11649       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11650 
11651    AST_LIST_LOCK(&users);
11652 
11653    if (AST_LIST_EMPTY(&users)) {
11654       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11655       AST_LIST_UNLOCK(&users);
11656       return RESULT_SUCCESS;
11657    }
11658    
11659    astman_send_ack(s, m, "Voicemail user list will follow");
11660    
11661    AST_LIST_TRAVERSE(&users, vmu, list) {
11662       char dirname[256];
11663 
11664 #ifdef IMAP_STORAGE
11665       int new, old;
11666       inboxcount(vmu->mailbox, &new, &old);
11667 #endif
11668       
11669       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11670       astman_append(s,
11671          "%s"
11672          "Event: VoicemailUserEntry\r\n"
11673          "VMContext: %s\r\n"
11674          "VoiceMailbox: %s\r\n"
11675          "Fullname: %s\r\n"
11676          "Email: %s\r\n"
11677          "Pager: %s\r\n"
11678          "ServerEmail: %s\r\n"
11679          "MailCommand: %s\r\n"
11680          "Language: %s\r\n"
11681          "TimeZone: %s\r\n"
11682          "Callback: %s\r\n"
11683          "Dialout: %s\r\n"
11684          "UniqueID: %s\r\n"
11685          "ExitContext: %s\r\n"
11686          "SayDurationMinimum: %d\r\n"
11687          "SayEnvelope: %s\r\n"
11688          "SayCID: %s\r\n"
11689          "AttachMessage: %s\r\n"
11690          "AttachmentFormat: %s\r\n"
11691          "DeleteMessage: %s\r\n"
11692          "VolumeGain: %.2f\r\n"
11693          "CanReview: %s\r\n"
11694          "CallOperator: %s\r\n"
11695          "MaxMessageCount: %d\r\n"
11696          "MaxMessageLength: %d\r\n"
11697          "NewMessageCount: %d\r\n"
11698 #ifdef IMAP_STORAGE
11699          "OldMessageCount: %d\r\n"
11700          "IMAPUser: %s\r\n"
11701 #endif
11702          "\r\n",
11703          actionid,
11704          vmu->context,
11705          vmu->mailbox,
11706          vmu->fullname,
11707          vmu->email,
11708          vmu->pager,
11709          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11710          mailcmd,
11711          vmu->language,
11712          vmu->zonetag,
11713          vmu->callback,
11714          vmu->dialout,
11715          vmu->uniqueid,
11716          vmu->exit,
11717          vmu->saydurationm,
11718          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11719          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11720          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11721          vmu->attachfmt,
11722          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11723          vmu->volgain,
11724          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11725          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11726          vmu->maxmsg,
11727          vmu->maxsecs,
11728 #ifdef IMAP_STORAGE
11729          new, old, vmu->imapuser
11730 #else
11731          count_messages(vmu, dirname)
11732 #endif
11733          );
11734    }     
11735    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11736 
11737    AST_LIST_UNLOCK(&users);
11738 
11739    return RESULT_SUCCESS;
11740 }
11741 
11742 /*! \brief Free the users structure. */
11743 static void free_vm_users(void) 
11744 {
11745    struct ast_vm_user *current;
11746    AST_LIST_LOCK(&users);
11747    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11748       ast_set_flag(current, VM_ALLOCED);
11749       free_user(current);
11750    }
11751    AST_LIST_UNLOCK(&users);
11752 }
11753 
11754 /*! \brief Free the zones structure. */
11755 static void free_vm_zones(void)
11756 {
11757    struct vm_zone *zcur;
11758    AST_LIST_LOCK(&zones);
11759    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11760       free_zone(zcur);
11761    AST_LIST_UNLOCK(&zones);
11762 }
11763 
11764 static const char *substitute_escapes(const char *value)
11765 {
11766    char *current;
11767 
11768    /* Add 16 for fudge factor */
11769    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11770 
11771    ast_str_reset(str);
11772    
11773    /* Substitute strings \r, \n, and \t into the appropriate characters */
11774    for (current = (char *) value; *current; current++) {
11775       if (*current == '\\') {
11776          current++;
11777          if (!*current) {
11778             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11779             break;
11780          }
11781          switch (*current) {
11782          case '\\':
11783             ast_str_append(&str, 0, "\\");
11784             break;
11785          case 'r':
11786             ast_str_append(&str, 0, "\r");
11787             break;
11788          case 'n':
11789 #ifdef IMAP_STORAGE
11790             if (!str->used || str->str[str->used - 1] != '\r') {
11791                ast_str_append(&str, 0, "\r");
11792             }
11793 #endif
11794             ast_str_append(&str, 0, "\n");
11795             break;
11796          case 't':
11797             ast_str_append(&str, 0, "\t");
11798             break;
11799          default:
11800             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11801             break;
11802          }
11803       } else {
11804          ast_str_append(&str, 0, "%c", *current);
11805       }
11806    }
11807 
11808    return ast_str_buffer(str);
11809 }
11810 
11811 static int load_config(int reload)
11812 {
11813    struct ast_config *cfg, *ucfg;
11814    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11815    int res;
11816 
11817    ast_unload_realtime("voicemail");
11818    ast_unload_realtime("voicemail_data");
11819 
11820    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11821       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11822          return 0;
11823       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11824          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11825          ucfg = NULL;
11826       }
11827       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11828       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11829          ast_config_destroy(ucfg);
11830          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11831          return 0;
11832       }
11833    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11834       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11835       return 0;
11836    } else {
11837       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11838       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11839          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11840          ucfg = NULL;
11841       }
11842    }
11843 
11844    res = actual_load_config(reload, cfg, ucfg);
11845 
11846    ast_config_destroy(cfg);
11847    ast_config_destroy(ucfg);
11848 
11849    return res;
11850 }
11851 
11852 #ifdef TEST_FRAMEWORK
11853 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11854 {
11855    ast_unload_realtime("voicemail");
11856    ast_unload_realtime("voicemail_data");
11857    return actual_load_config(reload, cfg, ucfg);
11858 }
11859 #endif
11860 
11861 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11862 {
11863    struct ast_vm_user *current;
11864    char *cat;
11865    struct ast_variable *var;
11866    const char *val;
11867    char *q, *stringp, *tmp;
11868    int x;
11869    int tmpadsi[4];
11870    char secretfn[PATH_MAX] = "";
11871 
11872 #ifdef IMAP_STORAGE
11873    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11874 #endif
11875    /* set audio control prompts */
11876    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11877    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11878    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11879    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11880    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11881 
11882    /* Free all the users structure */  
11883    free_vm_users();
11884 
11885    /* Free all the zones structure */
11886    free_vm_zones();
11887 
11888    AST_LIST_LOCK(&users);  
11889 
11890    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11891    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11892 
11893    if (cfg) {
11894       /* General settings */
11895 
11896       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11897          val = "default";
11898       ast_copy_string(userscontext, val, sizeof(userscontext));
11899       /* Attach voice message to mail message ? */
11900       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11901          val = "yes";
11902       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11903 
11904       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11905          val = "no";
11906       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11907 
11908       volgain = 0.0;
11909       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11910          sscanf(val, "%30lf", &volgain);
11911 
11912 #ifdef ODBC_STORAGE
11913       strcpy(odbc_database, "asterisk");
11914       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11915          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11916       }
11917       strcpy(odbc_table, "voicemessages");
11918       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11919          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11920       }
11921 #endif      
11922       /* Mail command */
11923       strcpy(mailcmd, SENDMAIL);
11924       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11925          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11926 
11927       maxsilence = 0;
11928       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11929          maxsilence = atoi(val);
11930          if (maxsilence > 0)
11931             maxsilence *= 1000;
11932       }
11933       
11934       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11935          maxmsg = MAXMSG;
11936       } else {
11937          maxmsg = atoi(val);
11938          if (maxmsg < 0) {
11939             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11940             maxmsg = MAXMSG;
11941          } else if (maxmsg > MAXMSGLIMIT) {
11942             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11943             maxmsg = MAXMSGLIMIT;
11944          }
11945       }
11946 
11947       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11948          maxdeletedmsg = 0;
11949       } else {
11950          if (sscanf(val, "%30d", &x) == 1)
11951             maxdeletedmsg = x;
11952          else if (ast_true(val))
11953             maxdeletedmsg = MAXMSG;
11954          else
11955             maxdeletedmsg = 0;
11956 
11957          if (maxdeletedmsg < 0) {
11958             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11959             maxdeletedmsg = MAXMSG;
11960          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11961             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11962             maxdeletedmsg = MAXMSGLIMIT;
11963          }
11964       }
11965 
11966       /* Load date format config for voicemail mail */
11967       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11968          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11969       }
11970 
11971       /* Load date format config for voicemail pager mail */
11972       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11973          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11974       }
11975 
11976       /* External password changing command */
11977       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11978          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11979          pwdchange = PWDCHANGE_EXTERNAL;
11980       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11981          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11982          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11983       }
11984  
11985       /* External password validation command */
11986       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11987          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11988          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11989       }
11990 
11991 #ifdef IMAP_STORAGE
11992       /* IMAP server address */
11993       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11994          ast_copy_string(imapserver, val, sizeof(imapserver));
11995       } else {
11996          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11997       }
11998       /* IMAP server port */
11999       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12000          ast_copy_string(imapport, val, sizeof(imapport));
12001       } else {
12002          ast_copy_string(imapport, "143", sizeof(imapport));
12003       }
12004       /* IMAP server flags */
12005       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12006          ast_copy_string(imapflags, val, sizeof(imapflags));
12007       }
12008       /* IMAP server master username */
12009       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12010          ast_copy_string(authuser, val, sizeof(authuser));
12011       }
12012       /* IMAP server master password */
12013       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12014          ast_copy_string(authpassword, val, sizeof(authpassword));
12015       }
12016       /* Expunge on exit */
12017       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12018          if (ast_false(val))
12019             expungeonhangup = 0;
12020          else
12021             expungeonhangup = 1;
12022       } else {
12023          expungeonhangup = 1;
12024       }
12025       /* IMAP voicemail folder */
12026       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12027          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12028       } else {
12029          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12030       }
12031       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12032          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12033       }
12034       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12035          imapgreetings = ast_true(val);
12036       } else {
12037          imapgreetings = 0;
12038       }
12039       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12040          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12041       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12042          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12043          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12044       } else {
12045          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12046       }
12047 
12048       /* There is some very unorthodox casting done here. This is due
12049        * to the way c-client handles the argument passed in. It expects a 
12050        * void pointer and casts the pointer directly to a long without
12051        * first dereferencing it. */
12052       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12053          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12054       } else {
12055          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12056       }
12057 
12058       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12059          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12060       } else {
12061          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12062       }
12063 
12064       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12065          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12066       } else {
12067          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12068       }
12069 
12070       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12071          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12072       } else {
12073          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12074       }
12075 
12076       /* Increment configuration version */
12077       imapversion++;
12078 #endif
12079       /* External voicemail notify application */
12080       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12081          ast_copy_string(externnotify, val, sizeof(externnotify));
12082          ast_debug(1, "found externnotify: %s\n", externnotify);
12083       } else {
12084          externnotify[0] = '\0';
12085       }
12086 
12087       /* SMDI voicemail notification */
12088       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12089          ast_debug(1, "Enabled SMDI voicemail notification\n");
12090          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12091             smdi_iface = ast_smdi_interface_find(val);
12092          } else {
12093             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12094             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12095          }
12096          if (!smdi_iface) {
12097             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12098          } 
12099       }
12100 
12101       /* Silence treshold */
12102       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12103       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12104          silencethreshold = atoi(val);
12105       
12106       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12107          val = ASTERISK_USERNAME;
12108       ast_copy_string(serveremail, val, sizeof(serveremail));
12109       
12110       vmmaxsecs = 0;
12111       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12112          if (sscanf(val, "%30d", &x) == 1) {
12113             vmmaxsecs = x;
12114          } else {
12115             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12116          }
12117       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12118          static int maxmessage_deprecate = 0;
12119          if (maxmessage_deprecate == 0) {
12120             maxmessage_deprecate = 1;
12121             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12122          }
12123          if (sscanf(val, "%30d", &x) == 1) {
12124             vmmaxsecs = x;
12125          } else {
12126             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12127          }
12128       }
12129 
12130       vmminsecs = 0;
12131       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12132          if (sscanf(val, "%30d", &x) == 1) {
12133             vmminsecs = x;
12134             if (maxsilence / 1000 >= vmminsecs) {
12135                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12136             }
12137          } else {
12138             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12139          }
12140       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12141          static int maxmessage_deprecate = 0;
12142          if (maxmessage_deprecate == 0) {
12143             maxmessage_deprecate = 1;
12144             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12145          }
12146          if (sscanf(val, "%30d", &x) == 1) {
12147             vmminsecs = x;
12148             if (maxsilence / 1000 >= vmminsecs) {
12149                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12150             }
12151          } else {
12152             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12153          }
12154       }
12155 
12156       val = ast_variable_retrieve(cfg, "general", "format");
12157       if (!val) {
12158          val = "wav";   
12159       } else {
12160          tmp = ast_strdupa(val);
12161          val = ast_format_str_reduce(tmp);
12162          if (!val) {
12163             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12164             val = "wav";
12165          }
12166       }
12167       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12168 
12169       skipms = 3000;
12170       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12171          if (sscanf(val, "%30d", &x) == 1) {
12172             maxgreet = x;
12173          } else {
12174             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12175          }
12176       }
12177 
12178       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12179          if (sscanf(val, "%30d", &x) == 1) {
12180             skipms = x;
12181          } else {
12182             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12183          }
12184       }
12185 
12186       maxlogins = 3;
12187       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12188          if (sscanf(val, "%30d", &x) == 1) {
12189             maxlogins = x;
12190          } else {
12191             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12192          }
12193       }
12194 
12195       minpassword = MINPASSWORD;
12196       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12197          if (sscanf(val, "%30d", &x) == 1) {
12198             minpassword = x;
12199          } else {
12200             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12201          }
12202       }
12203 
12204       /* Force new user to record name ? */
12205       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12206          val = "no";
12207       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12208 
12209       /* Force new user to record greetings ? */
12210       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12211          val = "no";
12212       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12213 
12214       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12215          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12216          stringp = ast_strdupa(val);
12217          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12218             if (!ast_strlen_zero(stringp)) {
12219                q = strsep(&stringp, ",");
12220                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12221                   q++;
12222                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12223                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12224             } else {
12225                cidinternalcontexts[x][0] = '\0';
12226             }
12227          }
12228       }
12229       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12230          ast_debug(1, "VM Review Option disabled globally\n");
12231          val = "no";
12232       }
12233       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12234 
12235       /* Temporary greeting reminder */
12236       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12237          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12238          val = "no";
12239       } else {
12240          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12241       }
12242       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12243       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12244          ast_debug(1, "VM next message wrap disabled globally\n");
12245          val = "no";
12246       }
12247       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12248 
12249       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12250          ast_debug(1, "VM Operator break disabled globally\n");
12251          val = "no";
12252       }
12253       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12254 
12255       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12256          ast_debug(1, "VM CID Info before msg disabled globally\n");
12257          val = "no";
12258       } 
12259       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12260 
12261       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12262          ast_debug(1, "Send Voicemail msg disabled globally\n");
12263          val = "no";
12264       }
12265       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12266    
12267       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12268          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12269          val = "yes";
12270       }
12271       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12272 
12273       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12274          ast_debug(1, "Move Heard enabled globally\n");
12275          val = "yes";
12276       }
12277       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12278 
12279       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12280          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12281          val = "no";
12282       }
12283       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12284 
12285       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12286          ast_debug(1, "Duration info before msg enabled globally\n");
12287          val = "yes";
12288       }
12289       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12290 
12291       saydurationminfo = 2;
12292       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12293          if (sscanf(val, "%30d", &x) == 1) {
12294             saydurationminfo = x;
12295          } else {
12296             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12297          }
12298       }
12299 
12300       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12301          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12302          val = "no";
12303       }
12304       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12305 
12306       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12307          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12308          ast_debug(1, "found dialout context: %s\n", dialcontext);
12309       } else {
12310          dialcontext[0] = '\0';  
12311       }
12312       
12313       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12314          ast_copy_string(callcontext, val, sizeof(callcontext));
12315          ast_debug(1, "found callback context: %s\n", callcontext);
12316       } else {
12317          callcontext[0] = '\0';
12318       }
12319 
12320       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12321          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12322          ast_debug(1, "found operator context: %s\n", exitcontext);
12323       } else {
12324          exitcontext[0] = '\0';
12325       }
12326       
12327       /* load password sounds configuration */
12328       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12329          ast_copy_string(vm_password, val, sizeof(vm_password));
12330       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12331          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12332       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12333          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12334       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12335          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12336       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12337          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12338       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12339          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12340       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12341          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12342       }
12343       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12344          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12345       }
12346       /* load configurable audio prompts */
12347       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12348          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12349       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12350          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12351       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12352          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12353       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12354          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12355       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12356          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12357 
12358       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12359          val = "no";
12360       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12361 
12362       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12363          val = "voicemail.conf";
12364       }
12365       if (!(strcmp(val, "spooldir"))) {
12366          passwordlocation = OPT_PWLOC_SPOOLDIR;
12367       } else {
12368          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12369       }
12370 
12371       poll_freq = DEFAULT_POLL_FREQ;
12372       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12373          if (sscanf(val, "%30u", &poll_freq) != 1) {
12374             poll_freq = DEFAULT_POLL_FREQ;
12375             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12376          }
12377       }
12378 
12379       poll_mailboxes = 0;
12380       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12381          poll_mailboxes = ast_true(val);
12382 
12383       memset(fromstring, 0, sizeof(fromstring));
12384       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12385       strcpy(charset, "ISO-8859-1");
12386       if (emailbody) {
12387          ast_free(emailbody);
12388          emailbody = NULL;
12389       }
12390       if (emailsubject) {
12391          ast_free(emailsubject);
12392          emailsubject = NULL;
12393       }
12394       if (pagerbody) {
12395          ast_free(pagerbody);
12396          pagerbody = NULL;
12397       }
12398       if (pagersubject) {
12399          ast_free(pagersubject);
12400          pagersubject = NULL;
12401       }
12402       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12403          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12404       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12405          ast_copy_string(fromstring, val, sizeof(fromstring));
12406       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12407          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12408       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12409          ast_copy_string(charset, val, sizeof(charset));
12410       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12411          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12412          for (x = 0; x < 4; x++) {
12413             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12414          }
12415       }
12416       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12417          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12418          for (x = 0; x < 4; x++) {
12419             memcpy(&adsisec[x], &tmpadsi[x], 1);
12420          }
12421       }
12422       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12423          if (atoi(val)) {
12424             adsiver = atoi(val);
12425          }
12426       }
12427       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12428          ast_copy_string(zonetag, val, sizeof(zonetag));
12429       }
12430       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12431          ast_copy_string(locale, val, sizeof(locale));
12432       }
12433       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12434          emailsubject = ast_strdup(substitute_escapes(val));
12435       }
12436       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12437          emailbody = ast_strdup(substitute_escapes(val));
12438       }
12439       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12440          pagersubject = ast_strdup(substitute_escapes(val));
12441       }
12442       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12443          pagerbody = ast_strdup(substitute_escapes(val));
12444       }
12445 
12446       /* load mailboxes from users.conf */
12447       if (ucfg) { 
12448          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12449             if (!strcasecmp(cat, "general")) {
12450                continue;
12451             }
12452             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12453                continue;
12454             if ((current = find_or_create(userscontext, cat))) {
12455                populate_defaults(current);
12456                apply_options_full(current, ast_variable_browse(ucfg, cat));
12457                ast_copy_string(current->context, userscontext, sizeof(current->context));
12458                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12459                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12460                }
12461 
12462                switch (current->passwordlocation) {
12463                case OPT_PWLOC_SPOOLDIR:
12464                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12465                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12466                }
12467             }
12468          }
12469       }
12470 
12471       /* load mailboxes from voicemail.conf */
12472       cat = ast_category_browse(cfg, NULL);
12473       while (cat) {
12474          if (strcasecmp(cat, "general")) {
12475             var = ast_variable_browse(cfg, cat);
12476             if (strcasecmp(cat, "zonemessages")) {
12477                /* Process mailboxes in this context */
12478                while (var) {
12479                   append_mailbox(cat, var->name, var->value);
12480                   var = var->next;
12481                }
12482             } else {
12483                /* Timezones in this context */
12484                while (var) {
12485                   struct vm_zone *z;
12486                   if ((z = ast_malloc(sizeof(*z)))) {
12487                      char *msg_format, *tzone;
12488                      msg_format = ast_strdupa(var->value);
12489                      tzone = strsep(&msg_format, "|,");
12490                      if (msg_format) {
12491                         ast_copy_string(z->name, var->name, sizeof(z->name));
12492                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12493                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12494                         AST_LIST_LOCK(&zones);
12495                         AST_LIST_INSERT_HEAD(&zones, z, list);
12496                         AST_LIST_UNLOCK(&zones);
12497                      } else {
12498                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12499                         ast_free(z);
12500                      }
12501                   } else {
12502                      AST_LIST_UNLOCK(&users);
12503                      return -1;
12504                   }
12505                   var = var->next;
12506                }
12507             }
12508          }
12509          cat = ast_category_browse(cfg, cat);
12510       }
12511 
12512       AST_LIST_UNLOCK(&users);
12513 
12514       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12515          start_poll_thread();
12516       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12517          stop_poll_thread();;
12518 
12519       return 0;
12520    } else {
12521       AST_LIST_UNLOCK(&users);
12522       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12523       return 0;
12524    }
12525 }
12526 
12527 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12528 {
12529    int res = -1;
12530    char dir[PATH_MAX];
12531    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12532    ast_debug(2, "About to try retrieving name file %s\n", dir);
12533    RETRIEVE(dir, -1, mailbox, context);
12534    if (ast_fileexists(dir, NULL, NULL)) {
12535       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12536    }
12537    DISPOSE(dir, -1);
12538    return res;
12539 }
12540 
12541 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12542    struct ast_config *pwconf;
12543    struct ast_flags config_flags = { 0 };
12544 
12545    pwconf = ast_config_load(secretfn, config_flags);
12546    if (pwconf) {
12547       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12548       if (val) {
12549          ast_copy_string(password, val, passwordlen);
12550          ast_config_destroy(pwconf);
12551          return;
12552       }
12553       ast_config_destroy(pwconf);
12554    }
12555    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12556 }
12557 
12558 static int write_password_to_file(const char *secretfn, const char *password) {
12559    struct ast_config *conf;
12560    struct ast_category *cat;
12561    struct ast_variable *var;
12562    int res = -1;
12563 
12564    if (!(conf = ast_config_new())) {
12565       ast_log(LOG_ERROR, "Error creating new config structure\n");
12566       return res;
12567    }
12568    if (!(cat = ast_category_new("general", "", 1))) {
12569       ast_log(LOG_ERROR, "Error creating new category structure\n");
12570       ast_config_destroy(conf);
12571       return res;
12572    }
12573    if (!(var = ast_variable_new("password", password, ""))) {
12574       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12575       ast_config_destroy(conf);
12576       ast_category_destroy(cat);
12577       return res;
12578    }
12579    ast_category_append(conf, cat);
12580    ast_variable_append(cat, var);
12581    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12582       res = 0;
12583    } else {
12584       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12585    }
12586 
12587    ast_config_destroy(conf);
12588    return res;
12589 }
12590 
12591 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12592 {
12593    char *context;
12594    char *args_copy;
12595    int res;
12596 
12597    if (ast_strlen_zero(data)) {
12598       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12599       return -1;
12600    }
12601 
12602    args_copy = ast_strdupa(data);
12603    if ((context = strchr(args_copy, '@'))) {
12604       *context++ = '\0';
12605    } else {
12606       context = "default";
12607    }
12608 
12609    if ((res = sayname(chan, args_copy, context) < 0)) {
12610       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12611       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12612       if (!res) {
12613          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12614       }
12615    }
12616 
12617    return res;
12618 }
12619 
12620 #ifdef TEST_FRAMEWORK
12621 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12622 {
12623    return 0;
12624 }
12625 
12626 static struct ast_frame *fake_read(struct ast_channel *ast)
12627 {
12628    return &ast_null_frame;
12629 }
12630 
12631 AST_TEST_DEFINE(test_voicemail_vmsayname)
12632 {
12633    char dir[PATH_MAX];
12634    char dir2[PATH_MAX];
12635    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12636    static const char TEST_EXTENSION[] = "1234";
12637 
12638    struct ast_channel *test_channel1 = NULL;
12639    int res = -1;
12640 
12641    static const struct ast_channel_tech fake_tech = {
12642       .write = fake_write,
12643       .read = fake_read,
12644    };
12645 
12646    switch (cmd) {
12647    case TEST_INIT:
12648       info->name = "vmsayname_exec";
12649       info->category = "/apps/app_voicemail/";
12650       info->summary = "Vmsayname unit test";
12651       info->description =
12652          "This tests passing various parameters to vmsayname";
12653       return AST_TEST_NOT_RUN;
12654    case TEST_EXECUTE:
12655       break;
12656    }
12657 
12658    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12659         NULL, NULL, 0, 0, "TestChannel1"))) {
12660       goto exit_vmsayname_test;
12661    }
12662 
12663    /* normally this is done in the channel driver */
12664    test_channel1->nativeformats = AST_FORMAT_GSM;
12665    test_channel1->writeformat = AST_FORMAT_GSM;
12666    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12667    test_channel1->readformat = AST_FORMAT_GSM;
12668    test_channel1->rawreadformat = AST_FORMAT_GSM;
12669    test_channel1->tech = &fake_tech;
12670 
12671    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12672    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12673    if (!(res = vmsayname_exec(test_channel1, dir))) {
12674       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12675       if (ast_fileexists(dir, NULL, NULL)) {
12676          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12677          res = -1;
12678          goto exit_vmsayname_test;
12679       } else {
12680          /* no greeting already exists as expected, let's create one to fully test sayname */
12681          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12682             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12683             goto exit_vmsayname_test;
12684          }
12685          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12686          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12687          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12688          if ((res = symlink(dir, dir2))) {
12689             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12690             goto exit_vmsayname_test;
12691          }
12692          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12693          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12694          res = vmsayname_exec(test_channel1, dir);
12695 
12696          /* TODO: there may be a better way to do this */
12697          unlink(dir2);
12698          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12699          rmdir(dir2);
12700          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12701          rmdir(dir2);
12702       }
12703    }
12704 
12705 exit_vmsayname_test:
12706 
12707    if (test_channel1) {
12708       ast_hangup(test_channel1);
12709    }
12710 
12711    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12712 }
12713 
12714 AST_TEST_DEFINE(test_voicemail_msgcount)
12715 {
12716    int i, j, res = AST_TEST_PASS, syserr;
12717    struct ast_vm_user *vmu;
12718    struct vm_state vms;
12719 #ifdef IMAP_STORAGE
12720    struct ast_channel *chan = NULL;
12721 #endif
12722    struct {
12723       char dir[256];
12724       char file[256];
12725       char txtfile[256];
12726    } tmp[3];
12727    char syscmd[256];
12728    const char origweasels[] = "tt-weasels";
12729    const char testcontext[] = "test";
12730    const char testmailbox[] = "00000000";
12731    const char testspec[] = "00000000@test";
12732    FILE *txt;
12733    int new, old, urgent;
12734    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12735    const int folder2mbox[3] = { 1, 11, 0 };
12736    const int expected_results[3][12] = {
12737       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12738       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12739       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12740       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12741    };
12742 
12743    switch (cmd) {
12744    case TEST_INIT:
12745       info->name = "test_voicemail_msgcount";
12746       info->category = "/apps/app_voicemail/";
12747       info->summary = "Test Voicemail status checks";
12748       info->description =
12749          "Verify that message counts are correct when retrieved through the public API";
12750       return AST_TEST_NOT_RUN;
12751    case TEST_EXECUTE:
12752       break;
12753    }
12754 
12755    /* Make sure the original path was completely empty */
12756    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12757    if ((syserr = ast_safe_system(syscmd))) {
12758       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12759          syserr > 0 ? strerror(syserr) : "unable to fork()");
12760       return AST_TEST_FAIL;
12761    }
12762 
12763 #ifdef IMAP_STORAGE
12764    if (!(chan = ast_dummy_channel_alloc())) {
12765       ast_test_status_update(test, "Unable to create dummy channel\n");
12766       return AST_TEST_FAIL;
12767    }
12768 #endif
12769 
12770    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12771       !(vmu = find_or_create(testcontext, testmailbox))) {
12772       ast_test_status_update(test, "Cannot create vmu structure\n");
12773       ast_unreplace_sigchld();
12774 #ifdef IMAP_STORAGE
12775       chan = ast_channel_unref(chan);
12776 #endif
12777       return AST_TEST_FAIL;
12778    }
12779 
12780    populate_defaults(vmu);
12781    memset(&vms, 0, sizeof(vms));
12782 
12783    /* Create temporary voicemail */
12784    for (i = 0; i < 3; i++) {
12785       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12786       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12787       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12788 
12789       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12790          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12791             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12792          if ((syserr = ast_safe_system(syscmd))) {
12793             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12794                syserr > 0 ? strerror(syserr) : "unable to fork()");
12795             ast_unreplace_sigchld();
12796 #ifdef IMAP_STORAGE
12797             chan = ast_channel_unref(chan);
12798 #endif
12799             return AST_TEST_FAIL;
12800          }
12801       }
12802 
12803       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12804          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12805          fclose(txt);
12806       } else {
12807          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12808          res = AST_TEST_FAIL;
12809          break;
12810       }
12811       open_mailbox(&vms, vmu, folder2mbox[i]);
12812       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12813 
12814       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12815       for (j = 0; j < 3; j++) {
12816          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12817          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12818             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12819                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12820             res = AST_TEST_FAIL;
12821          }
12822       }
12823 
12824       new = old = urgent = 0;
12825       if (ast_app_inboxcount(testspec, &new, &old)) {
12826          ast_test_status_update(test, "inboxcount returned failure\n");
12827          res = AST_TEST_FAIL;
12828       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12829          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12830             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12831          res = AST_TEST_FAIL;
12832       }
12833 
12834       new = old = urgent = 0;
12835       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12836          ast_test_status_update(test, "inboxcount2 returned failure\n");
12837          res = AST_TEST_FAIL;
12838       } else if (old != expected_results[i][6 + 0] ||
12839             urgent != expected_results[i][6 + 1] ||
12840                new != expected_results[i][6 + 2]    ) {
12841          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12842             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12843          res = AST_TEST_FAIL;
12844       }
12845 
12846       new = old = urgent = 0;
12847       for (j = 0; j < 3; j++) {
12848          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12849             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12850                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12851             res = AST_TEST_FAIL;
12852          }
12853       }
12854    }
12855 
12856    for (i = 0; i < 3; i++) {
12857       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12858        * server, in which case, the rm below will not affect the
12859        * voicemails. */
12860       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12861       DISPOSE(tmp[i].dir, 0);
12862    }
12863 
12864    if (vms.deleted) {
12865       ast_free(vms.deleted);
12866    }
12867    if (vms.heard) {
12868       ast_free(vms.heard);
12869    }
12870 
12871 #ifdef IMAP_STORAGE
12872    chan = ast_channel_unref(chan);
12873 #endif
12874 
12875    /* And remove test directory */
12876    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12877    if ((syserr = ast_safe_system(syscmd))) {
12878       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12879          syserr > 0 ? strerror(syserr) : "unable to fork()");
12880    }
12881 
12882    return res;
12883 }
12884 
12885 AST_TEST_DEFINE(test_voicemail_notify_endl)
12886 {
12887    int res = AST_TEST_PASS;
12888    char testcontext[] = "test";
12889    char testmailbox[] = "00000000";
12890    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12891    char attach[256], attach2[256];
12892    char buf[256] = ""; /* No line should actually be longer than 80 */
12893    struct ast_channel *chan = NULL;
12894    struct ast_vm_user *vmu, vmus = {
12895       .flags = 0,
12896    };
12897    FILE *file;
12898    struct {
12899       char *name;
12900       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12901       void *location;
12902       union {
12903          int intval;
12904          char *strval;
12905       } u;
12906    } test_items[] = {
12907       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12908       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12909       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12910       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12911       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12912       { "attach2", STRPTR, attach2, .u.strval = "" },
12913       { "attach", STRPTR, attach, .u.strval = "" },
12914    };
12915    int which;
12916 
12917    switch (cmd) {
12918    case TEST_INIT:
12919       info->name = "test_voicemail_notify_endl";
12920       info->category = "/apps/app_voicemail/";
12921       info->summary = "Test Voicemail notification end-of-line";
12922       info->description =
12923          "Verify that notification emails use a consistent end-of-line character";
12924       return AST_TEST_NOT_RUN;
12925    case TEST_EXECUTE:
12926       break;
12927    }
12928 
12929    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12930    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12931 
12932    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12933       !(vmu = find_or_create(testcontext, testmailbox))) {
12934       ast_test_status_update(test, "Cannot create vmu structure\n");
12935       return AST_TEST_NOT_RUN;
12936    }
12937 
12938    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12939       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12940       return AST_TEST_NOT_RUN;
12941    }
12942 
12943    populate_defaults(vmu);
12944    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12945 #ifdef IMAP_STORAGE
12946    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12947 #endif
12948 
12949    file = tmpfile();
12950    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12951       /* Kill previous test, if any */
12952       rewind(file);
12953       if (ftruncate(fileno(file), 0)) {
12954          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12955          res = AST_TEST_FAIL;
12956          break;
12957       }
12958 
12959       /* Make each change, in order, to the test mailbox */
12960       if (test_items[which].type == INT) {
12961          *((int *) test_items[which].location) = test_items[which].u.intval;
12962       } else if (test_items[which].type == FLAGVAL) {
12963          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12964             ast_clear_flag(vmu, test_items[which].u.intval);
12965          } else {
12966             ast_set_flag(vmu, test_items[which].u.intval);
12967          }
12968       } else if (test_items[which].type == STATIC) {
12969          strcpy(test_items[which].location, test_items[which].u.strval);
12970       } else if (test_items[which].type == STRPTR) {
12971          test_items[which].location = test_items[which].u.strval;
12972       }
12973 
12974       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12975       rewind(file);
12976       while (fgets(buf, sizeof(buf), file)) {
12977          if (
12978 #ifdef IMAP_STORAGE
12979          buf[strlen(buf) - 2] != '\r'
12980 #else
12981          buf[strlen(buf) - 2] == '\r'
12982 #endif
12983          || buf[strlen(buf) - 1] != '\n') {
12984             res = AST_TEST_FAIL;
12985          }
12986       }
12987    }
12988    fclose(file);
12989    return res;
12990 }
12991 
12992 AST_TEST_DEFINE(test_voicemail_load_config)
12993 {
12994    int res = AST_TEST_PASS;
12995    struct ast_vm_user *vmu;
12996    struct ast_config *cfg;
12997    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12998    int fd;
12999    FILE *file;
13000    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13001 
13002    switch (cmd) {
13003    case TEST_INIT:
13004       info->name = "test_voicemail_load_config";
13005       info->category = "/apps/app_voicemail/";
13006       info->summary = "Test loading Voicemail config";
13007       info->description =
13008          "Verify that configuration is loaded consistently. "
13009          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13010          "some options were loaded after the mailboxes were instantiated, causing "
13011          "those options not to be set correctly.";
13012       return AST_TEST_NOT_RUN;
13013    case TEST_EXECUTE:
13014       break;
13015    }
13016 
13017    /* build a config file by hand... */
13018    if ((fd = mkstemp(config_filename)) < 0) {
13019       return AST_TEST_FAIL;
13020    }
13021    if (!(file = fdopen(fd, "w"))) {
13022       close(fd);
13023       unlink(config_filename);
13024       return AST_TEST_FAIL;
13025    }
13026    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13027    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13028    fputs("00000002 => 9999,Mrs. Test\n", file);
13029    fclose(file);
13030 
13031    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13032       res = AST_TEST_FAIL;
13033       goto cleanup;
13034    }
13035 
13036    load_config_from_memory(1, cfg, NULL);
13037    ast_config_destroy(cfg);
13038 
13039 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13040    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13041    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13042 
13043    AST_LIST_LOCK(&users);
13044    AST_LIST_TRAVERSE(&users, vmu, list) {
13045       if (!strcmp(vmu->mailbox, "00000001")) {
13046          if (0); /* trick to get CHECK to work */
13047          CHECK(vmu, callback, "othercontext")
13048          CHECK(vmu, locale, "nl_NL.UTF-8")
13049          CHECK(vmu, zonetag, "central")
13050       } else if (!strcmp(vmu->mailbox, "00000002")) {
13051          if (0); /* trick to get CHECK to work */
13052          CHECK(vmu, callback, "somecontext")
13053          CHECK(vmu, locale, "de_DE.UTF-8")
13054          CHECK(vmu, zonetag, "european")
13055       }
13056    }
13057    AST_LIST_UNLOCK(&users);
13058 
13059 #undef CHECK
13060 
13061    /* restore config */
13062    load_config(1); /* this might say "Failed to load configuration file." */
13063 
13064 cleanup:
13065    unlink(config_filename);
13066    return res;
13067 }
13068 
13069 #endif /* defined(TEST_FRAMEWORK) */
13070 
13071 static int reload(void)
13072 {
13073    return load_config(1);
13074 }
13075 
13076 static int unload_module(void)
13077 {
13078    int res;
13079 
13080    res = ast_unregister_application(app);
13081    res |= ast_unregister_application(app2);
13082    res |= ast_unregister_application(app3);
13083    res |= ast_unregister_application(app4);
13084    res |= ast_unregister_application(sayname_app);
13085    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13086    res |= ast_manager_unregister("VoicemailUsersList");
13087    res |= ast_data_unregister(NULL);
13088 #ifdef TEST_FRAMEWORK
13089    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13090    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13091    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13092    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13093    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13094 #endif
13095    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13096    ast_uninstall_vm_functions();
13097    ao2_ref(inprocess_container, -1);
13098 
13099    if (poll_thread != AST_PTHREADT_NULL)
13100       stop_poll_thread();
13101 
13102    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13103    ast_unload_realtime("voicemail");
13104    ast_unload_realtime("voicemail_data");
13105 
13106    free_vm_users();
13107    free_vm_zones();
13108    return res;
13109 }
13110 
13111 static int load_module(void)
13112 {
13113    int res;
13114    my_umask = umask(0);
13115    umask(my_umask);
13116 
13117    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13118       return AST_MODULE_LOAD_DECLINE;
13119    }
13120 
13121    /* compute the location of the voicemail spool directory */
13122    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13123    
13124    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13125       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13126    }
13127 
13128    if ((res = load_config(0)))
13129       return res;
13130 
13131    res = ast_register_application_xml(app, vm_exec);
13132    res |= ast_register_application_xml(app2, vm_execmain);
13133    res |= ast_register_application_xml(app3, vm_box_exists);
13134    res |= ast_register_application_xml(app4, vmauthenticate);
13135    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13136    res |= ast_custom_function_register(&mailbox_exists_acf);
13137    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13138 #ifdef TEST_FRAMEWORK
13139    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13140    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13141    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13142    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13143    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13144 #endif
13145 
13146    if (res)
13147       return res;
13148 
13149    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13150    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13151 
13152    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13153    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13154    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13155 
13156    return res;
13157 }
13158 
13159 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13160 {
13161    int cmd = 0;
13162    char destination[80] = "";
13163    int retries = 0;
13164 
13165    if (!num) {
13166       ast_verb(3, "Destination number will be entered manually\n");
13167       while (retries < 3 && cmd != 't') {
13168          destination[1] = '\0';
13169          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13170          if (!cmd)
13171             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13172          if (!cmd)
13173             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13174          if (!cmd) {
13175             cmd = ast_waitfordigit(chan, 6000);
13176             if (cmd)
13177                destination[0] = cmd;
13178          }
13179          if (!cmd) {
13180             retries++;
13181          } else {
13182 
13183             if (cmd < 0)
13184                return 0;
13185             if (cmd == '*') {
13186                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13187                return 0;
13188             }
13189             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13190                retries++;
13191             else
13192                cmd = 't';
13193          }
13194          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13195       }
13196       if (retries >= 3) {
13197          return 0;
13198       }
13199       
13200    } else {
13201       if (option_verbose > 2)
13202          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13203       ast_copy_string(destination, num, sizeof(destination));
13204    }
13205 
13206    if (!ast_strlen_zero(destination)) {
13207       if (destination[strlen(destination) -1 ] == '*')
13208          return 0; 
13209       if (option_verbose > 2)
13210          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13211       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13212       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13213       chan->priority = 0;
13214       return 9;
13215    }
13216    return 0;
13217 }
13218 
13219 /*!
13220  * \brief The advanced options within a message.
13221  * \param chan
13222  * \param vmu 
13223  * \param vms
13224  * \param msg
13225  * \param option
13226  * \param record_gain
13227  *
13228  * Provides handling for the play message envelope, call the person back, or reply to message. 
13229  *
13230  * \return zero on success, -1 on error.
13231  */
13232 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)
13233 {
13234    int res = 0;
13235    char filename[PATH_MAX];
13236    struct ast_config *msg_cfg = NULL;
13237    const char *origtime, *context;
13238    char *name, *num;
13239    int retries = 0;
13240    char *cid;
13241    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13242 
13243    vms->starting = 0; 
13244 
13245    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13246 
13247    /* Retrieve info from VM attribute file */
13248    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13249    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13250    msg_cfg = ast_config_load(filename, config_flags);
13251    DISPOSE(vms->curdir, vms->curmsg);
13252    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13253       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13254       return 0;
13255    }
13256 
13257    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13258       ast_config_destroy(msg_cfg);
13259       return 0;
13260    }
13261 
13262    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13263 
13264    context = ast_variable_retrieve(msg_cfg, "message", "context");
13265    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13266       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13267    switch (option) {
13268    case 3: /* Play message envelope */
13269       if (!res)
13270          res = play_message_datetime(chan, vmu, origtime, filename);
13271       if (!res)
13272          res = play_message_callerid(chan, vms, cid, context, 0);
13273 
13274       res = 't';
13275       break;
13276 
13277    case 2:  /* Call back */
13278 
13279       if (ast_strlen_zero(cid))
13280          break;
13281 
13282       ast_callerid_parse(cid, &name, &num);
13283       while ((res > -1) && (res != 't')) {
13284          switch (res) {
13285          case '1':
13286             if (num) {
13287                /* Dial the CID number */
13288                res = dialout(chan, vmu, num, vmu->callback);
13289                if (res) {
13290                   ast_config_destroy(msg_cfg);
13291                   return 9;
13292                }
13293             } else {
13294                res = '2';
13295             }
13296             break;
13297 
13298          case '2':
13299             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13300             if (!ast_strlen_zero(vmu->dialout)) {
13301                res = dialout(chan, vmu, NULL, vmu->dialout);
13302                if (res) {
13303                   ast_config_destroy(msg_cfg);
13304                   return 9;
13305                }
13306             } else {
13307                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13308                res = ast_play_and_wait(chan, "vm-sorry");
13309             }
13310             ast_config_destroy(msg_cfg);
13311             return res;
13312          case '*':
13313             res = 't';
13314             break;
13315          case '3':
13316          case '4':
13317          case '5':
13318          case '6':
13319          case '7':
13320          case '8':
13321          case '9':
13322          case '0':
13323 
13324             res = ast_play_and_wait(chan, "vm-sorry");
13325             retries++;
13326             break;
13327          default:
13328             if (num) {
13329                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13330                res = ast_play_and_wait(chan, "vm-num-i-have");
13331                if (!res)
13332                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13333                if (!res)
13334                   res = ast_play_and_wait(chan, "vm-tocallnum");
13335                /* Only prompt for a caller-specified number if there is a dialout context specified */
13336                if (!ast_strlen_zero(vmu->dialout)) {
13337                   if (!res)
13338                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13339                }
13340             } else {
13341                res = ast_play_and_wait(chan, "vm-nonumber");
13342                if (!ast_strlen_zero(vmu->dialout)) {
13343                   if (!res)
13344                      res = ast_play_and_wait(chan, "vm-toenternumber");
13345                }
13346             }
13347             if (!res) {
13348                res = ast_play_and_wait(chan, "vm-star-cancel");
13349             }
13350             if (!res) {
13351                res = ast_waitfordigit(chan, 6000);
13352             }
13353             if (!res) {
13354                retries++;
13355                if (retries > 3) {
13356                   res = 't';
13357                }
13358             }
13359             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13360             break; 
13361             
13362          }
13363          if (res == 't')
13364             res = 0;
13365          else if (res == '*')
13366             res = -1;
13367       }
13368       break;
13369       
13370    case 1:  /* Reply */
13371       /* Send reply directly to sender */
13372       if (ast_strlen_zero(cid))
13373          break;
13374 
13375       ast_callerid_parse(cid, &name, &num);
13376       if (!num) {
13377          ast_verb(3, "No CID number available, no reply sent\n");
13378          if (!res)
13379             res = ast_play_and_wait(chan, "vm-nonumber");
13380          ast_config_destroy(msg_cfg);
13381          return res;
13382       } else {
13383          struct ast_vm_user vmu2;
13384          if (find_user(&vmu2, vmu->context, num)) {
13385             struct leave_vm_options leave_options;
13386             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13387             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13388 
13389             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13390             
13391             memset(&leave_options, 0, sizeof(leave_options));
13392             leave_options.record_gain = record_gain;
13393             res = leave_voicemail(chan, mailbox, &leave_options);
13394             if (!res)
13395                res = 't';
13396             ast_config_destroy(msg_cfg);
13397             return res;
13398          } else {
13399             /* Sender has no mailbox, can't reply */
13400             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13401             ast_play_and_wait(chan, "vm-nobox");
13402             res = 't';
13403             ast_config_destroy(msg_cfg);
13404             return res;
13405          }
13406       } 
13407       res = 0;
13408 
13409       break;
13410    }
13411 
13412 #ifndef IMAP_STORAGE
13413    ast_config_destroy(msg_cfg);
13414 
13415    if (!res) {
13416       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13417       vms->heard[msg] = 1;
13418       res = wait_file(chan, vms, vms->fn);
13419    }
13420 #endif
13421    return res;
13422 }
13423 
13424 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13425          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13426          signed char record_gain, struct vm_state *vms, char *flag)
13427 {
13428    /* Record message & let caller review or re-record it, or set options if applicable */
13429    int res = 0;
13430    int cmd = 0;
13431    int max_attempts = 3;
13432    int attempts = 0;
13433    int recorded = 0;
13434    int msg_exists = 0;
13435    signed char zero_gain = 0;
13436    char tempfile[PATH_MAX];
13437    char *acceptdtmf = "#";
13438    char *canceldtmf = "";
13439    int canceleddtmf = 0;
13440 
13441    /* Note that urgent and private are for flagging messages as such in the future */
13442 
13443    /* barf if no pointer passed to store duration in */
13444    if (duration == NULL) {
13445       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13446       return -1;
13447    }
13448 
13449    if (!outsidecaller)
13450       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13451    else
13452       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13453 
13454    cmd = '3';  /* Want to start by recording */
13455 
13456    while ((cmd >= 0) && (cmd != 't')) {
13457       switch (cmd) {
13458       case '1':
13459          if (!msg_exists) {
13460             /* In this case, 1 is to record a message */
13461             cmd = '3';
13462             break;
13463          } else {
13464             /* Otherwise 1 is to save the existing message */
13465             ast_verb(3, "Saving message as is\n");
13466             if (!outsidecaller) 
13467                ast_filerename(tempfile, recordfile, NULL);
13468             ast_stream_and_wait(chan, "vm-msgsaved", "");
13469             if (!outsidecaller) {
13470                /* Saves to IMAP server only if imapgreeting=yes */
13471                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13472                DISPOSE(recordfile, -1);
13473             }
13474             cmd = 't';
13475             return res;
13476          }
13477       case '2':
13478          /* Review */
13479          ast_verb(3, "Reviewing the message\n");
13480          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13481          break;
13482       case '3':
13483          msg_exists = 0;
13484          /* Record */
13485          if (recorded == 1) 
13486             ast_verb(3, "Re-recording the message\n");
13487          else  
13488             ast_verb(3, "Recording the message\n");
13489          
13490          if (recorded && outsidecaller) {
13491             cmd = ast_play_and_wait(chan, INTRO);
13492             cmd = ast_play_and_wait(chan, "beep");
13493          }
13494          recorded = 1;
13495          /* 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 */
13496          if (record_gain)
13497             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13498          if (ast_test_flag(vmu, VM_OPERATOR))
13499             canceldtmf = "0";
13500          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13501          if (strchr(canceldtmf, cmd)) {
13502          /* need this flag here to distinguish between pressing '0' during message recording or after */
13503             canceleddtmf = 1;
13504          }
13505          if (record_gain)
13506             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13507          if (cmd == -1) {
13508             /* User has hung up, no options to give */
13509             if (!outsidecaller) {
13510                /* user was recording a greeting and they hung up, so let's delete the recording. */
13511                ast_filedelete(tempfile, NULL);
13512             }     
13513             return cmd;
13514          }
13515          if (cmd == '0') {
13516             break;
13517          } else if (cmd == '*') {
13518             break;
13519 #if 0
13520          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13521             /* Message is too short */
13522             ast_verb(3, "Message too short\n");
13523             cmd = ast_play_and_wait(chan, "vm-tooshort");
13524             cmd = ast_filedelete(tempfile, NULL);
13525             break;
13526          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13527             /* Message is all silence */
13528             ast_verb(3, "Nothing recorded\n");
13529             cmd = ast_filedelete(tempfile, NULL);
13530             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13531             if (!cmd)
13532                cmd = ast_play_and_wait(chan, "vm-speakup");
13533             break;
13534 #endif
13535          } else {
13536             /* If all is well, a message exists */
13537             msg_exists = 1;
13538             cmd = 0;
13539          }
13540          break;
13541       case '4':
13542          if (outsidecaller) {  /* only mark vm messages */
13543             /* Mark Urgent */
13544             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13545                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13546                res = ast_play_and_wait(chan, "vm-marked-urgent");
13547                strcpy(flag, "Urgent");
13548             } else if (flag) {
13549                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13550                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13551                strcpy(flag, "");
13552             } else {
13553                ast_play_and_wait(chan, "vm-sorry");
13554             }
13555             cmd = 0;
13556          } else {
13557             cmd = ast_play_and_wait(chan, "vm-sorry");
13558          }
13559          break;
13560       case '5':
13561       case '6':
13562       case '7':
13563       case '8':
13564       case '9':
13565       case '*':
13566       case '#':
13567          cmd = ast_play_and_wait(chan, "vm-sorry");
13568          break;
13569 #if 0 
13570 /*  XXX Commented out for the moment because of the dangers of deleting
13571     a message while recording (can put the message numbers out of sync) */
13572       case '*':
13573          /* Cancel recording, delete message, offer to take another message*/
13574          cmd = ast_play_and_wait(chan, "vm-deleted");
13575          cmd = ast_filedelete(tempfile, NULL);
13576          if (outsidecaller) {
13577             res = vm_exec(chan, NULL);
13578             return res;
13579          }
13580          else
13581             return 1;
13582 #endif
13583       case '0':
13584          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13585             cmd = ast_play_and_wait(chan, "vm-sorry");
13586             break;
13587          }
13588          if (msg_exists || recorded) {
13589             cmd = ast_play_and_wait(chan, "vm-saveoper");
13590             if (!cmd)
13591                cmd = ast_waitfordigit(chan, 3000);
13592             if (cmd == '1') {
13593                ast_filerename(tempfile, recordfile, NULL);
13594                ast_play_and_wait(chan, "vm-msgsaved");
13595                cmd = '0';
13596             } else if (cmd == '4') {
13597                if (flag) {
13598                   ast_play_and_wait(chan, "vm-marked-urgent");
13599                   strcpy(flag, "Urgent");
13600                }
13601                ast_play_and_wait(chan, "vm-msgsaved");
13602                cmd = '0';
13603             } else {
13604                ast_play_and_wait(chan, "vm-deleted");
13605                DELETE(tempfile, -1, tempfile, vmu);
13606                cmd = '0';
13607             }
13608          }
13609          return cmd;
13610       default:
13611          /* If the caller is an ouside caller, and the review option is enabled,
13612             allow them to review the message, but let the owner of the box review
13613             their OGM's */
13614          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13615             return cmd;
13616          if (msg_exists) {
13617             cmd = ast_play_and_wait(chan, "vm-review");
13618             if (!cmd && outsidecaller) {
13619                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13620                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13621                } else if (flag) {
13622                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13623                }
13624             }
13625          } else {
13626             cmd = ast_play_and_wait(chan, "vm-torerecord");
13627             if (!cmd)
13628                cmd = ast_waitfordigit(chan, 600);
13629          }
13630          
13631          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13632             cmd = ast_play_and_wait(chan, "vm-reachoper");
13633             if (!cmd)
13634                cmd = ast_waitfordigit(chan, 600);
13635          }
13636 #if 0
13637          if (!cmd)
13638             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13639 #endif
13640          if (!cmd)
13641             cmd = ast_waitfordigit(chan, 6000);
13642          if (!cmd) {
13643             attempts++;
13644          }
13645          if (attempts > max_attempts) {
13646             cmd = 't';
13647          }
13648       }
13649    }
13650    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13651       /* Hang up or timeout, so delete the recording. */
13652       ast_filedelete(tempfile, NULL);
13653    }
13654 
13655    if (cmd != 't' && outsidecaller)
13656       ast_play_and_wait(chan, "vm-goodbye");
13657 
13658    return cmd;
13659 }
13660 
13661 /* This is a workaround so that menuselect displays a proper description
13662  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13663  */
13664 
13665 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13666       .load = load_module,
13667       .unload = unload_module,
13668       .reload = reload,
13669       .nonoptreq = "res_adsi,res_smdi",
13670       );

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