Mon Oct 8 12:38:57 2012

Asterisk developer's documentation


app_voicemail_odbcstorage.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 #include "asterisk.h"
00047 
00048 #ifdef IMAP_STORAGE
00049 #include <ctype.h>
00050 #include <signal.h>
00051 #include <pwd.h>
00052 #ifdef USE_SYSTEM_IMAP
00053 #include <imap/c-client.h>
00054 #include <imap/imap4r1.h>
00055 #include <imap/linkage.h>
00056 #elif defined (USE_SYSTEM_CCLIENT)
00057 #include <c-client/c-client.h>
00058 #include <c-client/imap4r1.h>
00059 #include <c-client/linkage.h>
00060 #else
00061 #include "c-client.h"
00062 #include "imap4r1.h"
00063 #include "linkage.h"
00064 #endif
00065 #endif
00066 
00067 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369652 $")
00068 
00069 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00070 #include <sys/time.h>
00071 #include <sys/stat.h>
00072 #include <sys/mman.h>
00073 #include <time.h>
00074 #include <dirent.h>
00075 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00076 #include <sys/wait.h>
00077 #endif
00078 
00079 #include "asterisk/logger.h"
00080 #include "asterisk/lock.h"
00081 #include "asterisk/file.h"
00082 #include "asterisk/channel.h"
00083 #include "asterisk/pbx.h"
00084 #include "asterisk/config.h"
00085 #include "asterisk/say.h"
00086 #include "asterisk/module.h"
00087 #include "asterisk/adsi.h"
00088 #include "asterisk/app.h"
00089 #include "asterisk/manager.h"
00090 #include "asterisk/dsp.h"
00091 #include "asterisk/localtime.h"
00092 #include "asterisk/cli.h"
00093 #include "asterisk/utils.h"
00094 #include "asterisk/stringfields.h"
00095 #include "asterisk/smdi.h"
00096 #include "asterisk/astobj2.h"
00097 #include "asterisk/event.h"
00098 #include "asterisk/taskprocessor.h"
00099 #include "asterisk/test.h"
00100 
00101 #ifdef ODBC_STORAGE
00102 #include "asterisk/res_odbc.h"
00103 #endif
00104 
00105 #ifdef IMAP_STORAGE
00106 #include "asterisk/threadstorage.h"
00107 #endif
00108 
00109 /*** DOCUMENTATION
00110    <application name="VoiceMail" language="en_US">
00111       <synopsis>
00112          Leave a Voicemail message.
00113       </synopsis>
00114       <syntax>
00115          <parameter name="mailboxs" argsep="&amp;" required="true">
00116             <argument name="mailbox1" argsep="@" required="true">
00117                <argument name="mailbox" required="true" />
00118                <argument name="context" />
00119             </argument>
00120             <argument name="mailbox2" argsep="@" multiple="true">
00121                <argument name="mailbox" required="true" />
00122                <argument name="context" />
00123             </argument>
00124          </parameter>
00125          <parameter name="options">
00126             <optionlist>
00127                <option name="b">
00128                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00129                </option>
00130                <option name="d">
00131                   <argument name="c" />
00132                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00133                   if played during the greeting. Context defaults to the current context.</para>
00134                </option>
00135                <option name="g">
00136                   <argument name="#" required="true" />
00137                   <para>Use the specified amount of gain when recording the voicemail
00138                   message. The units are whole-number decibels (dB). Only works on supported
00139                   technologies, which is DAHDI only.</para>
00140                </option>
00141                <option name="s">
00142                   <para>Skip the playback of instructions for leaving a message to the
00143                   calling party.</para>
00144                </option>
00145                <option name="u">
00146                   <para>Play the <literal>unavailable</literal> greeting.</para>
00147                </option>
00148                <option name="U">
00149                   <para>Mark message as <literal>URGENT</literal>.</para>
00150                </option>
00151                <option name="P">
00152                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00153                </option>
00154             </optionlist>
00155          </parameter>
00156       </syntax>
00157       <description>
00158          <para>This application allows the calling party to leave a message for the specified
00159          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00160          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00161          exist.</para>
00162          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00163          <enumlist>
00164             <enum name="0">
00165                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00166             </enum>
00167             <enum name="*">
00168                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00169             </enum>
00170          </enumlist>
00171          <para>This application will set the following channel variable upon completion:</para>
00172          <variablelist>
00173             <variable name="VMSTATUS">
00174                <para>This indicates the status of the execution of the VoiceMail application.</para>
00175                <value name="SUCCESS" />
00176                <value name="USEREXIT" />
00177                <value name="FAILED" />
00178             </variable>
00179          </variablelist>
00180       </description>
00181       <see-also>
00182          <ref type="application">VoiceMailMain</ref>
00183       </see-also>
00184    </application>
00185    <application name="VoiceMailMain" language="en_US">
00186       <synopsis>
00187          Check Voicemail messages.
00188       </synopsis>
00189       <syntax>
00190          <parameter name="mailbox" required="true" argsep="@">
00191             <argument name="mailbox" />
00192             <argument name="context" />
00193          </parameter>
00194          <parameter name="options">
00195             <optionlist>
00196                <option name="p">
00197                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00198                   the mailbox that is entered by the caller.</para>
00199                </option>
00200                <option name="g">
00201                   <argument name="#" required="true" />
00202                   <para>Use the specified amount of gain when recording a voicemail message.
00203                   The units are whole-number decibels (dB).</para>
00204                </option>
00205                <option name="s">
00206                   <para>Skip checking the passcode for the mailbox.</para>
00207                </option>
00208                <option name="a">
00209                   <argument name="folder" required="true" />
00210                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00211                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00212                   <enumlist>
00213                      <enum name="0"><para>INBOX</para></enum>
00214                      <enum name="1"><para>Old</para></enum>
00215                      <enum name="2"><para>Work</para></enum>
00216                      <enum name="3"><para>Family</para></enum>
00217                      <enum name="4"><para>Friends</para></enum>
00218                      <enum name="5"><para>Cust1</para></enum>
00219                      <enum name="6"><para>Cust2</para></enum>
00220                      <enum name="7"><para>Cust3</para></enum>
00221                      <enum name="8"><para>Cust4</para></enum>
00222                      <enum name="9"><para>Cust5</para></enum>
00223                   </enumlist>
00224                </option>
00225             </optionlist>
00226          </parameter>
00227       </syntax>
00228       <description>
00229          <para>This application allows the calling party to check voicemail messages. A specific
00230          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00231          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00232          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00233          <literal>default</literal> context will be used.</para>
00234          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00235          or Password, and the extension exists:</para>
00236          <enumlist>
00237             <enum name="*">
00238                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00239             </enum>
00240          </enumlist>
00241       </description>
00242       <see-also>
00243          <ref type="application">VoiceMail</ref>
00244       </see-also>
00245    </application>
00246    <application name="MailboxExists" language="en_US">
00247       <synopsis>
00248          Check to see if Voicemail mailbox exists.
00249       </synopsis>
00250       <syntax>
00251          <parameter name="mailbox" required="true" argsep="@">
00252             <argument name="mailbox" required="true" />
00253             <argument name="context" />
00254          </parameter>
00255          <parameter name="options">
00256             <para>None options.</para>
00257          </parameter>
00258       </syntax>
00259       <description>
00260          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00261          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00262          will be used.</para>
00263          <para>This application will set the following channel variable upon completion:</para>
00264          <variablelist>
00265             <variable name="VMBOXEXISTSSTATUS">
00266                <para>This will contain the status of the execution of the MailboxExists application.
00267                Possible values include:</para>
00268                <value name="SUCCESS" />
00269                <value name="FAILED" />
00270             </variable>
00271          </variablelist>
00272       </description>
00273    </application>
00274    <application name="VMAuthenticate" language="en_US">
00275       <synopsis>
00276          Authenticate with Voicemail passwords.
00277       </synopsis>
00278       <syntax>
00279          <parameter name="mailbox" required="true" argsep="@">
00280             <argument name="mailbox" />
00281             <argument name="context" />
00282          </parameter>
00283          <parameter name="options">
00284             <optionlist>
00285                <option name="s">
00286                   <para>Skip playing the initial prompts.</para>
00287                </option>
00288             </optionlist>
00289          </parameter>
00290       </syntax>
00291       <description>
00292          <para>This application behaves the same way as the Authenticate application, but the passwords
00293          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00294          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00295          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00296          mailbox.</para>
00297          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00298          or Password, and the extension exists:</para>
00299          <enumlist>
00300             <enum name="*">
00301                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00302             </enum>
00303          </enumlist>
00304       </description>
00305    </application>
00306    <application name="VMSayName" language="en_US">
00307       <synopsis>
00308          Play the name of a voicemail user
00309       </synopsis>
00310       <syntax>
00311          <parameter name="mailbox" required="true" argsep="@">
00312             <argument name="mailbox" />
00313             <argument name="context" />
00314          </parameter>
00315       </syntax>
00316       <description>
00317          <para>This application will say the recorded name of the voicemail user specified as the
00318          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00319       </description>
00320    </application>
00321    <function name="MAILBOX_EXISTS" language="en_US">
00322       <synopsis>
00323          Tell if a mailbox is configured.
00324       </synopsis>
00325       <syntax argsep="@">
00326          <parameter name="mailbox" required="true" />
00327          <parameter name="context" />
00328       </syntax>
00329       <description>
00330          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00331          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00332          context.</para>
00333       </description>
00334    </function>
00335    <manager name="VoicemailUsersList" language="en_US">
00336       <synopsis>
00337          List All Voicemail User Information.
00338       </synopsis>
00339       <syntax>
00340          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00341       </syntax>
00342       <description>
00343       </description>
00344    </manager>
00345  ***/
00346 
00347 #ifdef IMAP_STORAGE
00348 static char imapserver[48];
00349 static char imapport[8];
00350 static char imapflags[128];
00351 static char imapfolder[64];
00352 static char imapparentfolder[64] = "\0";
00353 static char greetingfolder[64];
00354 static char authuser[32];
00355 static char authpassword[42];
00356 static int imapversion = 1;
00357 
00358 static int expungeonhangup = 1;
00359 static int imapgreetings = 0;
00360 static char delimiter = '\0';
00361 
00362 struct vm_state;
00363 struct ast_vm_user;
00364 
00365 AST_THREADSTORAGE(ts_vmstate);
00366 
00367 /* Forward declarations for IMAP */
00368 static int init_mailstream(struct vm_state *vms, int box);
00369 static void write_file(char *filename, char *buffer, unsigned long len);
00370 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00371 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00372 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00373 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00374 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00375 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00376 static void vmstate_insert(struct vm_state *vms);
00377 static void vmstate_delete(struct vm_state *vms);
00378 static void set_update(MAILSTREAM * stream);
00379 static void init_vm_state(struct vm_state *vms);
00380 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00381 static void get_mailbox_delimiter(MAILSTREAM *stream);
00382 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00383 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00384 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00385 static void update_messages_by_imapuser(const char *user, unsigned long number);
00386 static int vm_delete(char *file);
00387 
00388 static int imap_remove_file (char *dir, int msgnum);
00389 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00390 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00391 static void check_quota(struct vm_state *vms, char *mailbox);
00392 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00393 struct vmstate {
00394    struct vm_state *vms;
00395    AST_LIST_ENTRY(vmstate) list;
00396 };
00397 
00398 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00399 
00400 #endif
00401 
00402 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00403 
00404 #define COMMAND_TIMEOUT 5000
00405 /* Don't modify these here; set your umask at runtime instead */
00406 #define  VOICEMAIL_DIR_MODE   0777
00407 #define  VOICEMAIL_FILE_MODE  0666
00408 #define  CHUNKSIZE   65536
00409 
00410 #define VOICEMAIL_CONFIG "voicemail.conf"
00411 #define ASTERISK_USERNAME "asterisk"
00412 
00413 /* Define fast-forward, pause, restart, and reverse keys
00414  * while listening to a voicemail message - these are
00415  * strings, not characters */
00416 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00417 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00418 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00419 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00420 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00421 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00422 
00423 /* Default mail command to mail voicemail. Change it with the
00424  * mailcmd= command in voicemail.conf */
00425 #define SENDMAIL "/usr/sbin/sendmail -t"
00426 
00427 #define INTRO "vm-intro"
00428 
00429 #define MAXMSG 100
00430 #define MAXMSGLIMIT 9999
00431 
00432 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00433 
00434 #define BASELINELEN 72
00435 #define BASEMAXINLINE 256
00436 #ifdef IMAP_STORAGE
00437 #define ENDL "\r\n"
00438 #else
00439 #define ENDL "\n"
00440 #endif
00441 
00442 #define MAX_DATETIME_FORMAT   512
00443 #define MAX_NUM_CID_CONTEXTS 10
00444 
00445 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00446 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00447 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00448 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00449 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00450 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00451 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00452 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00453 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00454 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00455 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00456 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00457 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00458 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00459 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00460 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00461 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00462 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00463 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00464 #define ERROR_LOCK_PATH  -100
00465 #define OPERATOR_EXIT     300
00466 
00467 
00468 enum vm_box {
00469    NEW_FOLDER,
00470    OLD_FOLDER,
00471    WORK_FOLDER,
00472    FAMILY_FOLDER,
00473    FRIENDS_FOLDER,
00474    GREETINGS_FOLDER
00475 };
00476 
00477 enum vm_option_flags {
00478    OPT_SILENT =           (1 << 0),
00479    OPT_BUSY_GREETING =    (1 << 1),
00480    OPT_UNAVAIL_GREETING = (1 << 2),
00481    OPT_RECORDGAIN =       (1 << 3),
00482    OPT_PREPEND_MAILBOX =  (1 << 4),
00483    OPT_AUTOPLAY =         (1 << 6),
00484    OPT_DTMFEXIT =         (1 << 7),
00485    OPT_MESSAGE_Urgent =   (1 << 8),
00486    OPT_MESSAGE_PRIORITY = (1 << 9)
00487 };
00488 
00489 enum vm_option_args {
00490    OPT_ARG_RECORDGAIN = 0,
00491    OPT_ARG_PLAYFOLDER = 1,
00492    OPT_ARG_DTMFEXIT   = 2,
00493    /* This *must* be the last value in this enum! */
00494    OPT_ARG_ARRAY_SIZE = 3,
00495 };
00496 
00497 enum vm_passwordlocation {
00498    OPT_PWLOC_VOICEMAILCONF = 0,
00499    OPT_PWLOC_SPOOLDIR      = 1,
00500    OPT_PWLOC_USERSCONF     = 2,
00501 };
00502 
00503 AST_APP_OPTIONS(vm_app_options, {
00504    AST_APP_OPTION('s', OPT_SILENT),
00505    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00506    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00507    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00508    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00509    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00510    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00511    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00512    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00513 });
00514 
00515 static int load_config(int reload);
00516 #ifdef TEST_FRAMEWORK
00517 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00518 #endif
00519 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00520 
00521 /*! \page vmlang Voicemail Language Syntaxes Supported
00522 
00523    \par Syntaxes supported, not really language codes.
00524    \arg \b en    - English
00525    \arg \b de    - German
00526    \arg \b es    - Spanish
00527    \arg \b fr    - French
00528    \arg \b it    - Italian
00529    \arg \b nl    - Dutch
00530    \arg \b pt    - Portuguese
00531    \arg \b pt_BR - Portuguese (Brazil)
00532    \arg \b gr    - Greek
00533    \arg \b no    - Norwegian
00534    \arg \b se    - Swedish
00535    \arg \b tw    - Chinese (Taiwan)
00536    \arg \b ua - Ukrainian
00537 
00538 German requires the following additional soundfile:
00539 \arg \b 1F  einE (feminine)
00540 
00541 Spanish requires the following additional soundfile:
00542 \arg \b 1M      un (masculine)
00543 
00544 Dutch, Portuguese & Spanish require the following additional soundfiles:
00545 \arg \b vm-INBOXs singular of 'new'
00546 \arg \b vm-Olds      singular of 'old/heard/read'
00547 
00548 NB these are plural:
00549 \arg \b vm-INBOX  nieuwe (nl)
00550 \arg \b vm-Old    oude (nl)
00551 
00552 Polish uses:
00553 \arg \b vm-new-a  'new', feminine singular accusative
00554 \arg \b vm-new-e  'new', feminine plural accusative
00555 \arg \b vm-new-ych   'new', feminine plural genitive
00556 \arg \b vm-old-a  'old', feminine singular accusative
00557 \arg \b vm-old-e  'old', feminine plural accusative
00558 \arg \b vm-old-ych   'old', feminine plural genitive
00559 \arg \b digits/1-a   'one', not always same as 'digits/1'
00560 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00561 
00562 Swedish uses:
00563 \arg \b vm-nytt      singular of 'new'
00564 \arg \b vm-nya    plural of 'new'
00565 \arg \b vm-gammalt   singular of 'old'
00566 \arg \b vm-gamla  plural of 'old'
00567 \arg \b digits/ett   'one', not always same as 'digits/1'
00568 
00569 Norwegian uses:
00570 \arg \b vm-ny     singular of 'new'
00571 \arg \b vm-nye    plural of 'new'
00572 \arg \b vm-gammel singular of 'old'
00573 \arg \b vm-gamle  plural of 'old'
00574 
00575 Dutch also uses:
00576 \arg \b nl-om     'at'?
00577 
00578 Spanish also uses:
00579 \arg \b vm-youhaveno
00580 
00581 Italian requires the following additional soundfile:
00582 
00583 For vm_intro_it:
00584 \arg \b vm-nuovo  new
00585 \arg \b vm-nuovi  new plural
00586 \arg \b vm-vecchio   old
00587 \arg \b vm-vecchi old plural
00588 
00589 Chinese (Taiwan) requires the following additional soundfile:
00590 \arg \b vm-tong      A class-word for call (tong1)
00591 \arg \b vm-ri     A class-word for day (ri4)
00592 \arg \b vm-you    You (ni3)
00593 \arg \b vm-haveno   Have no (mei2 you3)
00594 \arg \b vm-have     Have (you3)
00595 \arg \b vm-listen   To listen (yao4 ting1)
00596 
00597 
00598 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00599 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00600 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00601 
00602 */
00603 
00604 struct baseio {
00605    int iocp;
00606    int iolen;
00607    int linelength;
00608    int ateof;
00609    unsigned char iobuf[BASEMAXINLINE];
00610 };
00611 
00612 /*! Structure for linked list of users 
00613  * Use ast_vm_user_destroy() to free one of these structures. */
00614 struct ast_vm_user {
00615    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00616    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00617    char password[80];               /*!< Secret pin code, numbers only */
00618    char fullname[80];               /*!< Full name, for directory app */
00619    char email[80];                  /*!< E-mail address */
00620    char *emailsubject;              /*!< E-mail subject */
00621    char *emailbody;                 /*!< E-mail body */
00622    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00623    char serveremail[80];            /*!< From: Mail address */
00624    char mailcmd[160];               /*!< Configurable mail command */
00625    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00626    char zonetag[80];                /*!< Time zone */
00627    char locale[20];                 /*!< The locale (for presentation of date/time) */
00628    char callback[80];
00629    char dialout[80];
00630    char uniqueid[80];               /*!< Unique integer identifier */
00631    char exit[80];
00632    char attachfmt[20];              /*!< Attachment format */
00633    unsigned int flags;              /*!< VM_ flags */ 
00634    int saydurationm;
00635    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00636    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00637    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00638    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00639    int passwordlocation;            /*!< Storage location of the password */
00640 #ifdef IMAP_STORAGE
00641    char imapuser[80];               /*!< IMAP server login */
00642    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00643    char imapfolder[64];             /*!< IMAP voicemail folder */
00644    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00645    int imapversion;                 /*!< If configuration changes, use the new values */
00646 #endif
00647    double volgain;                  /*!< Volume gain for voicemails sent via email */
00648    AST_LIST_ENTRY(ast_vm_user) list;
00649 };
00650 
00651 /*! Voicemail time zones */
00652 struct vm_zone {
00653    AST_LIST_ENTRY(vm_zone) list;
00654    char name[80];
00655    char timezone[80];
00656    char msg_format[512];
00657 };
00658 
00659 #define VMSTATE_MAX_MSG_ARRAY 256
00660 
00661 /*! Voicemail mailbox state */
00662 struct vm_state {
00663    char curbox[80];
00664    char username[80];
00665    char context[80];
00666    char curdir[PATH_MAX];
00667    char vmbox[PATH_MAX];
00668    char fn[PATH_MAX];
00669    char intro[PATH_MAX];
00670    int *deleted;
00671    int *heard;
00672    int dh_arraysize; /* used for deleted / heard allocation */
00673    int curmsg;
00674    int lastmsg;
00675    int newmessages;
00676    int oldmessages;
00677    int urgentmessages;
00678    int starting;
00679    int repeats;
00680 #ifdef IMAP_STORAGE
00681    ast_mutex_t lock;
00682    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00683    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00684    MAILSTREAM *mailstream;
00685    int vmArrayIndex;
00686    char imapuser[80];                   /*!< IMAP server login */
00687    char imapfolder[64];                 /*!< IMAP voicemail folder */
00688    int imapversion;
00689    int interactive;
00690    char introfn[PATH_MAX];              /*!< Name of prepended file */
00691    unsigned int quota_limit;
00692    unsigned int quota_usage;
00693    struct vm_state *persist_vms;
00694 #endif
00695 };
00696 
00697 #ifdef ODBC_STORAGE
00698 static char odbc_database[80];
00699 static char odbc_table[80];
00700 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00701 #define DISPOSE(a,b) remove_file(a,b)
00702 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00703 #define EXISTS(a,b,c,d) (message_exists(a,b))
00704 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00705 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00706 #define DELETE(a,b,c,d) (delete_file(a,b))
00707 #else
00708 #ifdef IMAP_STORAGE
00709 #define DISPOSE(a,b) (imap_remove_file(a,b))
00710 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
00711 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00712 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00713 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00714 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00715 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00716 #else
00717 #define RETRIEVE(a,b,c,d)
00718 #define DISPOSE(a,b)
00719 #define STORE(a,b,c,d,e,f,g,h,i,j)
00720 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00721 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00722 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00723 #define DELETE(a,b,c,d) (vm_delete(c))
00724 #endif
00725 #endif
00726 
00727 static char VM_SPOOL_DIR[PATH_MAX];
00728 
00729 static char ext_pass_cmd[128];
00730 static char ext_pass_check_cmd[128];
00731 
00732 static int my_umask;
00733 
00734 #define PWDCHANGE_INTERNAL (1 << 1)
00735 #define PWDCHANGE_EXTERNAL (1 << 2)
00736 static int pwdchange = PWDCHANGE_INTERNAL;
00737 
00738 #ifdef ODBC_STORAGE
00739 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00740 #else
00741 # ifdef IMAP_STORAGE
00742 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00743 # else
00744 # define tdesc "Comedian Mail (Voicemail System)"
00745 # endif
00746 #endif
00747 
00748 static char userscontext[AST_MAX_EXTENSION] = "default";
00749 
00750 static char *addesc = "Comedian Mail";
00751 
00752 /* Leave a message */
00753 static char *app = "VoiceMail";
00754 
00755 /* Check mail, control, etc */
00756 static char *app2 = "VoiceMailMain";
00757 
00758 static char *app3 = "MailboxExists";
00759 static char *app4 = "VMAuthenticate";
00760 
00761 static char *sayname_app = "VMSayName";
00762 
00763 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00764 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00765 static char zonetag[80];
00766 static char locale[20];
00767 static int maxsilence;
00768 static int maxmsg;
00769 static int maxdeletedmsg;
00770 static int silencethreshold = 128;
00771 static char serveremail[80];
00772 static char mailcmd[160];  /* Configurable mail cmd */
00773 static char externnotify[160]; 
00774 static struct ast_smdi_interface *smdi_iface = NULL;
00775 static char vmfmts[80];
00776 static double volgain;
00777 static int vmminsecs;
00778 static int vmmaxsecs;
00779 static int maxgreet;
00780 static int skipms;
00781 static int maxlogins;
00782 static int minpassword;
00783 static int passwordlocation;
00784 
00785 /*! Poll mailboxes for changes since there is something external to
00786  *  app_voicemail that may change them. */
00787 static unsigned int poll_mailboxes;
00788 
00789 /*! Polling frequency */
00790 static unsigned int poll_freq;
00791 /*! By default, poll every 30 seconds */
00792 #define DEFAULT_POLL_FREQ 30
00793 
00794 AST_MUTEX_DEFINE_STATIC(poll_lock);
00795 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00796 static pthread_t poll_thread = AST_PTHREADT_NULL;
00797 static unsigned char poll_thread_run;
00798 
00799 /*! Subscription to ... MWI event subscriptions */
00800 static struct ast_event_sub *mwi_sub_sub;
00801 /*! Subscription to ... MWI event un-subscriptions */
00802 static struct ast_event_sub *mwi_unsub_sub;
00803 
00804 /*!
00805  * \brief An MWI subscription
00806  *
00807  * This is so we can keep track of which mailboxes are subscribed to.
00808  * This way, we know which mailboxes to poll when the pollmailboxes
00809  * option is being used.
00810  */
00811 struct mwi_sub {
00812    AST_RWLIST_ENTRY(mwi_sub) entry;
00813    int old_urgent;
00814    int old_new;
00815    int old_old;
00816    uint32_t uniqueid;
00817    char mailbox[1];
00818 };
00819 
00820 struct mwi_sub_task {
00821    const char *mailbox;
00822    const char *context;
00823    uint32_t uniqueid;
00824 };
00825 
00826 static struct ast_taskprocessor *mwi_subscription_tps;
00827 
00828 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00829 
00830 /* custom audio control prompts for voicemail playback */
00831 static char listen_control_forward_key[12];
00832 static char listen_control_reverse_key[12];
00833 static char listen_control_pause_key[12];
00834 static char listen_control_restart_key[12];
00835 static char listen_control_stop_key[12];
00836 
00837 /* custom password sounds */
00838 static char vm_password[80] = "vm-password";
00839 static char vm_newpassword[80] = "vm-newpassword";
00840 static char vm_passchanged[80] = "vm-passchanged";
00841 static char vm_reenterpassword[80] = "vm-reenterpassword";
00842 static char vm_mismatch[80] = "vm-mismatch";
00843 static char vm_invalid_password[80] = "vm-invalid-password";
00844 static char vm_pls_try_again[80] = "vm-pls-try-again";
00845 
00846 /*
00847  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00848  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00849  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00850  * app.c's __ast_play_and_record function
00851  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00852  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00853  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00854  * more effort than either of the other two.
00855  */
00856 static char vm_prepend_timeout[80] = "vm-then-pound";
00857 
00858 static struct ast_flags globalflags = {0};
00859 
00860 static int saydurationminfo;
00861 
00862 static char dialcontext[AST_MAX_CONTEXT] = "";
00863 static char callcontext[AST_MAX_CONTEXT] = "";
00864 static char exitcontext[AST_MAX_CONTEXT] = "";
00865 
00866 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00867 
00868 
00869 static char *emailbody = NULL;
00870 static char *emailsubject = NULL;
00871 static char *pagerbody = NULL;
00872 static char *pagersubject = NULL;
00873 static char fromstring[100];
00874 static char pagerfromstring[100];
00875 static char charset[32] = "ISO-8859-1";
00876 
00877 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00878 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00879 static int adsiver = 1;
00880 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00881 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00882 
00883 /* Forward declarations - generic */
00884 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00885 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
00886 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00887 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00888          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00889          signed char record_gain, struct vm_state *vms, char *flag);
00890 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00891 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00892 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
00893 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
00894 static void apply_options(struct ast_vm_user *vmu, const char *options);
00895 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
00896 static int is_valid_dtmf(const char *key);
00897 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00898 static int write_password_to_file(const char *secretfn, const char *password);
00899 static const char *substitute_escapes(const char *value);
00900 static void free_user(struct ast_vm_user *vmu);
00901 
00902 struct ao2_container *inprocess_container;
00903 
00904 struct inprocess {
00905    int count;
00906    char *context;
00907    char mailbox[0];
00908 };
00909 
00910 static int inprocess_hash_fn(const void *obj, const int flags)
00911 {
00912    const struct inprocess *i = obj;
00913    return atoi(i->mailbox);
00914 }
00915 
00916 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00917 {
00918    struct inprocess *i = obj, *j = arg;
00919    if (strcmp(i->mailbox, j->mailbox)) {
00920       return 0;
00921    }
00922    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00923 }
00924 
00925 static int inprocess_count(const char *context, const char *mailbox, int delta)
00926 {
00927    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00928    arg->context = arg->mailbox + strlen(mailbox) + 1;
00929    strcpy(arg->mailbox, mailbox); /* SAFE */
00930    strcpy(arg->context, context); /* SAFE */
00931    ao2_lock(inprocess_container);
00932    if ((i = ao2_find(inprocess_container, arg, 0))) {
00933       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00934       ao2_unlock(inprocess_container);
00935       ao2_ref(i, -1);
00936       return ret;
00937    }
00938    if (delta < 0) {
00939       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00940    }
00941    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00942       ao2_unlock(inprocess_container);
00943       return 0;
00944    }
00945    i->context = i->mailbox + strlen(mailbox) + 1;
00946    strcpy(i->mailbox, mailbox); /* SAFE */
00947    strcpy(i->context, context); /* SAFE */
00948    i->count = delta;
00949    ao2_link(inprocess_container, i);
00950    ao2_unlock(inprocess_container);
00951    ao2_ref(i, -1);
00952    return 0;
00953 }
00954 
00955 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00956 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00957 #endif
00958 
00959 /*!
00960  * \brief Strips control and non 7-bit clean characters from input string.
00961  *
00962  * \note To map control and none 7-bit characters to a 7-bit clean characters
00963  *  please use ast_str_encode_mine().
00964  */
00965 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00966 {
00967    char *bufptr = buf;
00968    for (; *input; input++) {
00969       if (*input < 32) {
00970          continue;
00971       }
00972       *bufptr++ = *input;
00973       if (bufptr == buf + buflen - 1) {
00974          break;
00975       }
00976    }
00977    *bufptr = '\0';
00978    return buf;
00979 }
00980 
00981 
00982 /*!
00983  * \brief Sets default voicemail system options to a voicemail user.
00984  *
00985  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00986  * - all the globalflags
00987  * - the saydurationminfo
00988  * - the callcontext
00989  * - the dialcontext
00990  * - the exitcontext
00991  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00992  * - volume gain
00993  * - emailsubject, emailbody set to NULL
00994  */
00995 static void populate_defaults(struct ast_vm_user *vmu)
00996 {
00997    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
00998    vmu->passwordlocation = passwordlocation;
00999    if (saydurationminfo) {
01000       vmu->saydurationm = saydurationminfo;
01001    }
01002    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01003    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01004    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01005    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01006    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01007    if (vmminsecs) {
01008       vmu->minsecs = vmminsecs;
01009    }
01010    if (vmmaxsecs) {
01011       vmu->maxsecs = vmmaxsecs;
01012    }
01013    if (maxmsg) {
01014       vmu->maxmsg = maxmsg;
01015    }
01016    if (maxdeletedmsg) {
01017       vmu->maxdeletedmsg = maxdeletedmsg;
01018    }
01019    vmu->volgain = volgain;
01020    ast_free(vmu->emailsubject);
01021    vmu->emailsubject = NULL;
01022    ast_free(vmu->emailbody);
01023    vmu->emailbody = NULL;
01024 #ifdef IMAP_STORAGE
01025    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01026 #endif
01027 }
01028 
01029 /*!
01030  * \brief Sets a a specific property value.
01031  * \param vmu The voicemail user object to work with.
01032  * \param var The name of the property to be set.
01033  * \param value The value to be set to the property.
01034  * 
01035  * The property name must be one of the understood properties. See the source for details.
01036  */
01037 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01038 {
01039    int x;
01040    if (!strcasecmp(var, "attach")) {
01041       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01042    } else if (!strcasecmp(var, "attachfmt")) {
01043       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01044    } else if (!strcasecmp(var, "serveremail")) {
01045       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01046    } else if (!strcasecmp(var, "emailbody")) {
01047       vmu->emailbody = ast_strdup(substitute_escapes(value));
01048    } else if (!strcasecmp(var, "emailsubject")) {
01049       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01050    } else if (!strcasecmp(var, "language")) {
01051       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01052    } else if (!strcasecmp(var, "tz")) {
01053       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01054    } else if (!strcasecmp(var, "locale")) {
01055       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01056 #ifdef IMAP_STORAGE
01057    } else if (!strcasecmp(var, "imapuser")) {
01058       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01059       vmu->imapversion = imapversion;
01060    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01061       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01062       vmu->imapversion = imapversion;
01063    } else if (!strcasecmp(var, "imapfolder")) {
01064       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01065    } else if (!strcasecmp(var, "imapvmshareid")) {
01066       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01067       vmu->imapversion = imapversion;
01068 #endif
01069    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01070       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01071    } else if (!strcasecmp(var, "saycid")){
01072       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01073    } else if (!strcasecmp(var, "sendvoicemail")){
01074       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01075    } else if (!strcasecmp(var, "review")){
01076       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01077    } else if (!strcasecmp(var, "tempgreetwarn")){
01078       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01079    } else if (!strcasecmp(var, "messagewrap")){
01080       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01081    } else if (!strcasecmp(var, "operator")) {
01082       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01083    } else if (!strcasecmp(var, "envelope")){
01084       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01085    } else if (!strcasecmp(var, "moveheard")){
01086       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01087    } else if (!strcasecmp(var, "sayduration")){
01088       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01089    } else if (!strcasecmp(var, "saydurationm")){
01090       if (sscanf(value, "%30d", &x) == 1) {
01091          vmu->saydurationm = x;
01092       } else {
01093          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01094       }
01095    } else if (!strcasecmp(var, "forcename")){
01096       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01097    } else if (!strcasecmp(var, "forcegreetings")){
01098       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01099    } else if (!strcasecmp(var, "callback")) {
01100       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01101    } else if (!strcasecmp(var, "dialout")) {
01102       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01103    } else if (!strcasecmp(var, "exitcontext")) {
01104       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01105    } else if (!strcasecmp(var, "minsecs")) {
01106       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01107          vmu->minsecs = x;
01108       } else {
01109          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01110          vmu->minsecs = vmminsecs;
01111       }
01112    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01113       vmu->maxsecs = atoi(value);
01114       if (vmu->maxsecs <= 0) {
01115          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01116          vmu->maxsecs = vmmaxsecs;
01117       } else {
01118          vmu->maxsecs = atoi(value);
01119       }
01120       if (!strcasecmp(var, "maxmessage"))
01121          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01122    } else if (!strcasecmp(var, "maxmsg")) {
01123       vmu->maxmsg = atoi(value);
01124       /* Accept maxmsg=0 (Greetings only voicemail) */
01125       if (vmu->maxmsg < 0) {
01126          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01127          vmu->maxmsg = MAXMSG;
01128       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01129          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01130          vmu->maxmsg = MAXMSGLIMIT;
01131       }
01132    } else if (!strcasecmp(var, "nextaftercmd")) {
01133       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01134    } else if (!strcasecmp(var, "backupdeleted")) {
01135       if (sscanf(value, "%30d", &x) == 1)
01136          vmu->maxdeletedmsg = x;
01137       else if (ast_true(value))
01138          vmu->maxdeletedmsg = MAXMSG;
01139       else
01140          vmu->maxdeletedmsg = 0;
01141 
01142       if (vmu->maxdeletedmsg < 0) {
01143          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01144          vmu->maxdeletedmsg = MAXMSG;
01145       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01146          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01147          vmu->maxdeletedmsg = MAXMSGLIMIT;
01148       }
01149    } else if (!strcasecmp(var, "volgain")) {
01150       sscanf(value, "%30lf", &vmu->volgain);
01151    } else if (!strcasecmp(var, "passwordlocation")) {
01152       if (!strcasecmp(value, "spooldir")) {
01153          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01154       } else {
01155          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01156       }
01157    } else if (!strcasecmp(var, "options")) {
01158       apply_options(vmu, value);
01159    }
01160 }
01161 
01162 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01163 {
01164    int fds[2], pid = 0;
01165 
01166    memset(buf, 0, len);
01167 
01168    if (pipe(fds)) {
01169       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01170    } else {
01171       /* good to go*/
01172       pid = ast_safe_fork(0);
01173 
01174       if (pid < 0) {
01175          /* ok maybe not */
01176          close(fds[0]);
01177          close(fds[1]);
01178          snprintf(buf, len, "FAILURE: Fork failed");
01179       } else if (pid) {
01180          /* parent */
01181          close(fds[1]);
01182          if (read(fds[0], buf, len) < 0) {
01183             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01184          }
01185          close(fds[0]);
01186       } else {
01187          /*  child */
01188          AST_DECLARE_APP_ARGS(arg,
01189             AST_APP_ARG(v)[20];
01190          );
01191          char *mycmd = ast_strdupa(command);
01192 
01193          close(fds[0]);
01194          dup2(fds[1], STDOUT_FILENO);
01195          close(fds[1]);
01196          ast_close_fds_above_n(STDOUT_FILENO);
01197 
01198          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01199 
01200          execv(arg.v[0], arg.v); 
01201          printf("FAILURE: %s", strerror(errno));
01202          _exit(0);
01203       }
01204    }
01205    return buf;
01206 }
01207 
01208 /*!
01209  * \brief Check that password meets minimum required length
01210  * \param vmu The voicemail user to change the password for.
01211  * \param password The password string to check
01212  *
01213  * \return zero on ok, 1 on not ok.
01214  */
01215 static int check_password(struct ast_vm_user *vmu, char *password)
01216 {
01217    /* check minimum length */
01218    if (strlen(password) < minpassword)
01219       return 1;
01220    /* check that password does not contain '*' character */
01221    if (!ast_strlen_zero(password) && password[0] == '*')
01222       return 1;
01223    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01224       char cmd[255], buf[255];
01225 
01226       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01227 
01228       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01229       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01230          ast_debug(5, "Result: %s\n", buf);
01231          if (!strncasecmp(buf, "VALID", 5)) {
01232             ast_debug(3, "Passed password check: '%s'\n", buf);
01233             return 0;
01234          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01235             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01236             return 0;
01237          } else {
01238             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01239             return 1;
01240          }
01241       }
01242    }
01243    return 0;
01244 }
01245 
01246 /*! 
01247  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01248  * \param vmu The voicemail user to change the password for.
01249  * \param password The new value to be set to the password for this user.
01250  * 
01251  * This only works if there is a realtime engine configured.
01252  * This is called from the (top level) vm_change_password.
01253  *
01254  * \return zero on success, -1 on error.
01255  */
01256 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01257 {
01258    int res = -1;
01259    if (!strcmp(vmu->password, password)) {
01260       /* No change (but an update would return 0 rows updated, so we opt out here) */
01261       return 0;
01262    }
01263 
01264    if (strlen(password) > 10) {
01265       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01266    }
01267    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01268       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01269       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01270       res = 0;
01271    }
01272    return res;
01273 }
01274 
01275 /*!
01276  * \brief Destructively Parse options and apply.
01277  */
01278 static void apply_options(struct ast_vm_user *vmu, const char *options)
01279 {  
01280    char *stringp;
01281    char *s;
01282    char *var, *value;
01283    stringp = ast_strdupa(options);
01284    while ((s = strsep(&stringp, "|"))) {
01285       value = s;
01286       if ((var = strsep(&value, "=")) && value) {
01287          apply_option(vmu, var, value);
01288       }
01289    }  
01290 }
01291 
01292 /*!
01293  * \brief Loads the options specific to a voicemail user.
01294  * 
01295  * This is called when a vm_user structure is being set up, such as from load_options.
01296  */
01297 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01298 {
01299    for (; var; var = var->next) {
01300       if (!strcasecmp(var->name, "vmsecret")) {
01301          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01302       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01303          if (ast_strlen_zero(retval->password)) {
01304             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01305                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01306                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01307             } else {
01308                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01309             }
01310          }
01311       } else if (!strcasecmp(var->name, "uniqueid")) {
01312          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01313       } else if (!strcasecmp(var->name, "pager")) {
01314          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01315       } else if (!strcasecmp(var->name, "email")) {
01316          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01317       } else if (!strcasecmp(var->name, "fullname")) {
01318          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01319       } else if (!strcasecmp(var->name, "context")) {
01320          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01321       } else if (!strcasecmp(var->name, "emailsubject")) {
01322          ast_free(retval->emailsubject);
01323          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01324       } else if (!strcasecmp(var->name, "emailbody")) {
01325          ast_free(retval->emailbody);
01326          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01327 #ifdef IMAP_STORAGE
01328       } else if (!strcasecmp(var->name, "imapuser")) {
01329          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01330          retval->imapversion = imapversion;
01331       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01332          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01333          retval->imapversion = imapversion;
01334       } else if (!strcasecmp(var->name, "imapfolder")) {
01335          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01336       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01337          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01338          retval->imapversion = imapversion;
01339 #endif
01340       } else
01341          apply_option(retval, var->name, var->value);
01342    }
01343 }
01344 
01345 /*!
01346  * \brief Determines if a DTMF key entered is valid.
01347  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
01348  *
01349  * Tests the character entered against the set of valid DTMF characters. 
01350  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01351  */
01352 static int is_valid_dtmf(const char *key)
01353 {
01354    int i;
01355    char *local_key = ast_strdupa(key);
01356 
01357    for (i = 0; i < strlen(key); ++i) {
01358       if (!strchr(VALID_DTMF, *local_key)) {
01359          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01360          return 0;
01361       }
01362       local_key++;
01363    }
01364    return 1;
01365 }
01366 
01367 /*!
01368  * \brief Finds a voicemail user from the realtime engine.
01369  * \param ivm
01370  * \param context
01371  * \param mailbox
01372  *
01373  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
01374  *
01375  * \return The ast_vm_user structure for the user that was found.
01376  */
01377 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01378 {
01379    struct ast_variable *var;
01380    struct ast_vm_user *retval;
01381 
01382    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01383       if (!ivm)
01384          ast_set_flag(retval, VM_ALLOCED);   
01385       else
01386          memset(retval, 0, sizeof(*retval));
01387       if (mailbox) 
01388          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01389       populate_defaults(retval);
01390       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01391          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01392       else
01393          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01394       if (var) {
01395          apply_options_full(retval, var);
01396          ast_variables_destroy(var);
01397       } else { 
01398          if (!ivm) 
01399             free_user(retval);
01400          retval = NULL;
01401       }  
01402    } 
01403    return retval;
01404 }
01405 
01406 /*!
01407  * \brief Finds a voicemail user from the users file or the realtime engine.
01408  * \param ivm
01409  * \param context
01410  * \param mailbox
01411  * 
01412  * \return The ast_vm_user structure for the user that was found.
01413  */
01414 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01415 {
01416    /* This function could be made to generate one from a database, too */
01417    struct ast_vm_user *vmu = NULL, *cur;
01418    AST_LIST_LOCK(&users);
01419 
01420    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01421       context = "default";
01422 
01423    AST_LIST_TRAVERSE(&users, cur, list) {
01424 #ifdef IMAP_STORAGE
01425       if (cur->imapversion != imapversion) {
01426          continue;
01427       }
01428 #endif
01429       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01430          break;
01431       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01432          break;
01433    }
01434    if (cur) {
01435       /* Make a copy, so that on a reload, we have no race */
01436       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01437          *vmu = *cur;
01438          if (!ivm) {
01439             vmu->emailbody = ast_strdup(cur->emailbody);
01440             vmu->emailsubject = ast_strdup(cur->emailsubject);
01441          }
01442          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01443          AST_LIST_NEXT(vmu, list) = NULL;
01444       }
01445    } else
01446       vmu = find_user_realtime(ivm, context, mailbox);
01447    AST_LIST_UNLOCK(&users);
01448    return vmu;
01449 }
01450 
01451 /*!
01452  * \brief Resets a user password to a specified password.
01453  * \param context
01454  * \param mailbox
01455  * \param newpass
01456  *
01457  * This does the actual change password work, called by the vm_change_password() function.
01458  *
01459  * \return zero on success, -1 on error.
01460  */
01461 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01462 {
01463    /* This function could be made to generate one from a database, too */
01464    struct ast_vm_user *cur;
01465    int res = -1;
01466    AST_LIST_LOCK(&users);
01467    AST_LIST_TRAVERSE(&users, cur, list) {
01468       if ((!context || !strcasecmp(context, cur->context)) &&
01469          (!strcasecmp(mailbox, cur->mailbox)))
01470             break;
01471    }
01472    if (cur) {
01473       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01474       res = 0;
01475    }
01476    AST_LIST_UNLOCK(&users);
01477    return res;
01478 }
01479 
01480 /*! 
01481  * \brief The handler for the change password option.
01482  * \param vmu The voicemail user to work with.
01483  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01484  * This is called when a new user logs in for the first time and the option to force them to change their password is set.
01485  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01486  */
01487 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01488 {
01489    struct ast_config   *cfg = NULL;
01490    struct ast_variable *var = NULL;
01491    struct ast_category *cat = NULL;
01492    char *category = NULL, *value = NULL, *new = NULL;
01493    const char *tmp = NULL;
01494    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01495    char secretfn[PATH_MAX] = "";
01496    int found = 0;
01497 
01498    if (!change_password_realtime(vmu, newpassword))
01499       return;
01500 
01501    /* check if we should store the secret in the spool directory next to the messages */
01502    switch (vmu->passwordlocation) {
01503    case OPT_PWLOC_SPOOLDIR:
01504       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01505       if (write_password_to_file(secretfn, newpassword) == 0) {
01506          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01507          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01508          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01509          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01510          break;
01511       } else {
01512          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01513       }
01514       /* Fall-through */
01515    case OPT_PWLOC_VOICEMAILCONF:
01516       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01517          while ((category = ast_category_browse(cfg, category))) {
01518             if (!strcasecmp(category, vmu->context)) {
01519                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01520                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01521                   break;
01522                }
01523                value = strstr(tmp, ",");
01524                if (!value) {
01525                   new = alloca(strlen(newpassword)+1);
01526                   sprintf(new, "%s", newpassword);
01527                } else {
01528                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01529                   sprintf(new, "%s%s", newpassword, value);
01530                }
01531                if (!(cat = ast_category_get(cfg, category))) {
01532                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01533                   break;
01534                }
01535                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01536                found = 1;
01537             }
01538          }
01539          /* save the results */
01540          if (found) {
01541             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01542             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01543             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01544             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01545             break;
01546          }
01547       }
01548       /* Fall-through */
01549    case OPT_PWLOC_USERSCONF:
01550       /* check users.conf and update the password stored for the mailbox */
01551       /* if no vmsecret entry exists create one. */
01552       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01553          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01554          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01555             ast_debug(4, "users.conf: %s\n", category);
01556             if (!strcasecmp(category, vmu->mailbox)) {
01557                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01558                   ast_debug(3, "looks like we need to make vmsecret!\n");
01559                   var = ast_variable_new("vmsecret", newpassword, "");
01560                } else {
01561                   var = NULL;
01562                }
01563                new = alloca(strlen(newpassword) + 1);
01564                sprintf(new, "%s", newpassword);
01565                if (!(cat = ast_category_get(cfg, category))) {
01566                   ast_debug(4, "failed to get category!\n");
01567                   ast_free(var);
01568                   break;
01569                }
01570                if (!var) {
01571                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01572                } else {
01573                   ast_variable_append(cat, var);
01574                }
01575                found = 1;
01576                break;
01577             }
01578          }
01579          /* save the results and clean things up */
01580          if (found) {
01581             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01582             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01583             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01584             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01585          }
01586       }
01587    }
01588 }
01589 
01590 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01591 {
01592    char buf[255];
01593    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01594    ast_debug(1, "External password: %s\n",buf);
01595    if (!ast_safe_system(buf)) {
01596       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01597       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01598       /* Reset the password in memory, too */
01599       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01600    }
01601 }
01602 
01603 /*! 
01604  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01605  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01606  * \param len The length of the path string that was written out.
01607  * \param context
01608  * \param ext 
01609  * \param folder 
01610  * 
01611  * The path is constructed as 
01612  *    VM_SPOOL_DIRcontext/ext/folder
01613  *
01614  * \return zero on success, -1 on error.
01615  */
01616 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01617 {
01618    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01619 }
01620 
01621 /*! 
01622  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01623  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01624  * \param len The length of the path string that was written out.
01625  * \param dir 
01626  * \param num 
01627  * 
01628  * The path is constructed as 
01629  *    VM_SPOOL_DIRcontext/ext/folder
01630  *
01631  * \return zero on success, -1 on error.
01632  */
01633 static int make_file(char *dest, const int len, const char *dir, const int num)
01634 {
01635    return snprintf(dest, len, "%s/msg%04d", dir, num);
01636 }
01637 
01638 /* same as mkstemp, but return a FILE * */
01639 static FILE *vm_mkftemp(char *template)
01640 {
01641    FILE *p = NULL;
01642    int pfd = mkstemp(template);
01643    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01644    if (pfd > -1) {
01645       p = fdopen(pfd, "w+");
01646       if (!p) {
01647          close(pfd);
01648          pfd = -1;
01649       }
01650    }
01651    return p;
01652 }
01653 
01654 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01655  * \param dest    String. base directory.
01656  * \param len     Length of dest.
01657  * \param context String. Ignored if is null or empty string.
01658  * \param ext     String. Ignored if is null or empty string.
01659  * \param folder  String. Ignored if is null or empty string. 
01660  * \return -1 on failure, 0 on success.
01661  */
01662 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01663 {
01664    mode_t   mode = VOICEMAIL_DIR_MODE;
01665    int res;
01666 
01667    make_dir(dest, len, context, ext, folder);
01668    if ((res = ast_mkdir(dest, mode))) {
01669       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01670       return -1;
01671    }
01672    return 0;
01673 }
01674 
01675 static const char * const mailbox_folders[] = {
01676 #ifdef IMAP_STORAGE
01677    imapfolder,
01678 #else
01679    "INBOX",
01680 #endif
01681    "Old",
01682    "Work",
01683    "Family",
01684    "Friends",
01685    "Cust1",
01686    "Cust2",
01687    "Cust3",
01688    "Cust4",
01689    "Cust5",
01690    "Deleted",
01691    "Urgent",
01692 };
01693 
01694 static const char *mbox(struct ast_vm_user *vmu, int id)
01695 {
01696 #ifdef IMAP_STORAGE
01697    if (vmu && id == 0) {
01698       return vmu->imapfolder;
01699    }
01700 #endif
01701    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01702 }
01703 
01704 static int get_folder_by_name(const char *name)
01705 {
01706    size_t i;
01707 
01708    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01709       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01710          return i;
01711       }
01712    }
01713 
01714    return -1;
01715 }
01716 
01717 static void free_user(struct ast_vm_user *vmu)
01718 {
01719    if (ast_test_flag(vmu, VM_ALLOCED)) {
01720 
01721       ast_free(vmu->emailbody);
01722       vmu->emailbody = NULL;
01723 
01724       ast_free(vmu->emailsubject);
01725       vmu->emailsubject = NULL;
01726 
01727       ast_free(vmu);
01728    }
01729 }
01730 
01731 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01732 
01733    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01734 
01735    /* remove old allocation */
01736    if (vms->deleted) {
01737       ast_free(vms->deleted);
01738       vms->deleted = NULL;
01739    }
01740    if (vms->heard) {
01741       ast_free(vms->heard);
01742       vms->heard = NULL;
01743    }
01744    vms->dh_arraysize = 0;
01745 
01746    if (arraysize > 0) {
01747       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01748          return -1;
01749       }
01750       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01751          ast_free(vms->deleted);
01752          vms->deleted = NULL;
01753          return -1;
01754       }
01755       vms->dh_arraysize = arraysize;
01756    }
01757 
01758    return 0;
01759 }
01760 
01761 /* All IMAP-specific functions should go in this block. This
01762  * keeps them from being spread out all over the code */
01763 #ifdef IMAP_STORAGE
01764 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01765 {
01766    char arg[10];
01767    struct vm_state *vms;
01768    unsigned long messageNum;
01769 
01770    /* If greetings aren't stored in IMAP, just delete the file */
01771    if (msgnum < 0 && !imapgreetings) {
01772       ast_filedelete(file, NULL);
01773       return;
01774    }
01775 
01776    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01777       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);
01778       return;
01779    }
01780 
01781    /* find real message number based on msgnum */
01782    /* this may be an index into vms->msgArray based on the msgnum. */
01783    messageNum = vms->msgArray[msgnum];
01784    if (messageNum == 0) {
01785       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01786       return;
01787    }
01788    if (option_debug > 2)
01789       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01790    /* delete message */
01791    snprintf (arg, sizeof(arg), "%lu", messageNum);
01792    ast_mutex_lock(&vms->lock);
01793    mail_setflag (vms->mailstream, arg, "\\DELETED");
01794    mail_expunge(vms->mailstream);
01795    ast_mutex_unlock(&vms->lock);
01796 }
01797 
01798 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01799 {
01800    struct vm_state *vms_p;
01801    char *file, *filename;
01802    char *attachment;
01803    int i;
01804    BODY *body;
01805 
01806    /* This function is only used for retrieval of IMAP greetings
01807     * regular messages are not retrieved this way, nor are greetings
01808     * if they are stored locally*/
01809    if (msgnum > -1 || !imapgreetings) {
01810       return 0;
01811    } else {
01812       file = strrchr(ast_strdupa(dir), '/');
01813       if (file)
01814          *file++ = '\0';
01815       else {
01816          ast_debug (1, "Failed to procure file name from directory passed.\n");
01817          return -1;
01818       }
01819    }
01820 
01821    /* check if someone is accessing this box right now... */
01822    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01823       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01824       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01825       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01826       * that's all we need to do.
01827       */
01828       if (!(vms_p = create_vm_state_from_user(vmu))) {
01829          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01830          return -1;
01831       }
01832    }
01833 
01834    /* Greetings will never have a prepended message */
01835    *vms_p->introfn = '\0';
01836 
01837    ast_mutex_lock(&vms_p->lock);
01838    init_mailstream(vms_p, GREETINGS_FOLDER);
01839    if (!vms_p->mailstream) {
01840       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01841       ast_mutex_unlock(&vms_p->lock);
01842       return -1;
01843    }
01844 
01845    /*XXX Yuck, this could probably be done a lot better */
01846    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01847       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01848       /* We have the body, now we extract the file name of the first attachment. */
01849       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01850          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01851       } else {
01852          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01853          ast_mutex_unlock(&vms_p->lock);
01854          return -1;
01855       }
01856       filename = strsep(&attachment, ".");
01857       if (!strcmp(filename, file)) {
01858          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01859          vms_p->msgArray[vms_p->curmsg] = i + 1;
01860          save_body(body, vms_p, "2", attachment, 0);
01861          ast_mutex_unlock(&vms_p->lock);
01862          return 0;
01863       }
01864    }
01865    ast_mutex_unlock(&vms_p->lock);
01866 
01867    return -1;
01868 }
01869 
01870 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01871 {
01872    BODY *body;
01873    char *header_content;
01874    char *attachedfilefmt;
01875    char buf[80];
01876    struct vm_state *vms;
01877    char text_file[PATH_MAX];
01878    FILE *text_file_ptr;
01879    int res = 0;
01880    struct ast_vm_user *vmu;
01881 
01882    if (!(vmu = find_user(NULL, context, mailbox))) {
01883       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01884       return -1;
01885    }
01886    
01887    if (msgnum < 0) {
01888       if (imapgreetings) {
01889          res = imap_retrieve_greeting(dir, msgnum, vmu);
01890          goto exit;
01891       } else {
01892          res = 0;
01893          goto exit;
01894       }
01895    }
01896 
01897    /* Before anything can happen, we need a vm_state so that we can
01898     * actually access the imap server through the vms->mailstream
01899     */
01900    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01901       /* This should not happen. If it does, then I guess we'd
01902        * need to create the vm_state, extract which mailbox to
01903        * open, and then set up the msgArray so that the correct
01904        * IMAP message could be accessed. If I have seen correctly
01905        * though, the vms should be obtainable from the vmstates list
01906        * and should have its msgArray properly set up.
01907        */
01908       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01909       res = -1;
01910       goto exit;
01911    }
01912    
01913    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01914    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01915 
01916    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01917    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01918       res = 0;
01919       goto exit;
01920    }
01921 
01922    if (option_debug > 2)
01923       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01924    if (vms->msgArray[msgnum] == 0) {
01925       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01926       res = -1;
01927       goto exit;
01928    }
01929 
01930    /* This will only work for new messages... */
01931    ast_mutex_lock(&vms->lock);
01932    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01933    ast_mutex_unlock(&vms->lock);
01934    /* empty string means no valid header */
01935    if (ast_strlen_zero(header_content)) {
01936       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01937       res = -1;
01938       goto exit;
01939    }
01940 
01941    ast_mutex_lock(&vms->lock);
01942    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01943    ast_mutex_unlock(&vms->lock);
01944 
01945    /* We have the body, now we extract the file name of the first attachment. */
01946    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01947       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01948    } else {
01949       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01950       res = -1;
01951       goto exit;
01952    }
01953    
01954    /* Find the format of the attached file */
01955 
01956    strsep(&attachedfilefmt, ".");
01957    if (!attachedfilefmt) {
01958       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01959       res = -1;
01960       goto exit;
01961    }
01962    
01963    save_body(body, vms, "2", attachedfilefmt, 0);
01964    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01965       *vms->introfn = '\0';
01966    }
01967 
01968    /* Get info from headers!! */
01969    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01970 
01971    if (!(text_file_ptr = fopen(text_file, "w"))) {
01972       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01973    }
01974 
01975    fprintf(text_file_ptr, "%s\n", "[message]");
01976 
01977    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01978    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01979    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01980    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01981    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01982    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01983    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01984    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01985    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01986    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01987    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01988    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01989    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01990    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01991    fclose(text_file_ptr);
01992 
01993 exit:
01994    free_user(vmu);
01995    return res;
01996 }
01997 
01998 static int folder_int(const char *folder)
01999 {
02000    /*assume a NULL folder means INBOX*/
02001    if (!folder) {
02002       return 0;
02003    }
02004    if (!strcasecmp(folder, imapfolder)) {
02005       return 0;
02006    } else if (!strcasecmp(folder, "Old")) {
02007       return 1;
02008    } else if (!strcasecmp(folder, "Work")) {
02009       return 2;
02010    } else if (!strcasecmp(folder, "Family")) {
02011       return 3;
02012    } else if (!strcasecmp(folder, "Friends")) {
02013       return 4;
02014    } else if (!strcasecmp(folder, "Cust1")) {
02015       return 5;
02016    } else if (!strcasecmp(folder, "Cust2")) {
02017       return 6;
02018    } else if (!strcasecmp(folder, "Cust3")) {
02019       return 7;
02020    } else if (!strcasecmp(folder, "Cust4")) {
02021       return 8;
02022    } else if (!strcasecmp(folder, "Cust5")) {
02023       return 9;
02024    } else if (!strcasecmp(folder, "Urgent")) {
02025       return 11;
02026    } else { /*assume they meant INBOX if folder is not found otherwise*/
02027       return 0;
02028    }
02029 }
02030 
02031 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02032 {
02033    SEARCHPGM *pgm;
02034    SEARCHHEADER *hdr;
02035 
02036    struct ast_vm_user *vmu, vmus;
02037    struct vm_state *vms_p;
02038    int ret = 0;
02039    int fold = folder_int(folder);
02040    int urgent = 0;
02041    
02042    /* If URGENT, then look at INBOX */
02043    if (fold == 11) {
02044       fold = NEW_FOLDER;
02045       urgent = 1;
02046    }
02047 
02048    if (ast_strlen_zero(mailbox))
02049       return 0;
02050 
02051    /* We have to get the user before we can open the stream! */
02052    vmu = find_user(&vmus, context, mailbox);
02053    if (!vmu) {
02054       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02055       return -1;
02056    } else {
02057       /* No IMAP account available */
02058       if (vmu->imapuser[0] == '\0') {
02059          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02060          return -1;
02061       }
02062    }
02063    
02064    /* No IMAP account available */
02065    if (vmu->imapuser[0] == '\0') {
02066       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02067       free_user(vmu);
02068       return -1;
02069    }
02070 
02071    /* check if someone is accessing this box right now... */
02072    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02073    if (!vms_p) {
02074       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02075    }
02076    if (vms_p) {
02077       ast_debug(3, "Returning before search - user is logged in\n");
02078       if (fold == 0) { /* INBOX */
02079          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02080       }
02081       if (fold == 1) { /* Old messages */
02082          return vms_p->oldmessages;
02083       }
02084    }
02085 
02086    /* add one if not there... */
02087    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02088    if (!vms_p) {
02089       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02090    }
02091 
02092    if (!vms_p) {
02093       vms_p = create_vm_state_from_user(vmu);
02094    }
02095    ret = init_mailstream(vms_p, fold);
02096    if (!vms_p->mailstream) {
02097       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02098       return -1;
02099    }
02100    if (ret == 0) {
02101       ast_mutex_lock(&vms_p->lock);
02102       pgm = mail_newsearchpgm ();
02103       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02104       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02105       pgm->header = hdr;
02106       if (fold != OLD_FOLDER) {
02107          pgm->unseen = 1;
02108          pgm->seen = 0;
02109       }
02110       /* In the special case where fold is 1 (old messages) we have to do things a bit
02111        * differently. Old messages are stored in the INBOX but are marked as "seen"
02112        */
02113       else {
02114          pgm->unseen = 0;
02115          pgm->seen = 1;
02116       }
02117       /* look for urgent messages */
02118       if (fold == NEW_FOLDER) {
02119          if (urgent) {
02120             pgm->flagged = 1;
02121             pgm->unflagged = 0;
02122          } else {
02123             pgm->flagged = 0;
02124             pgm->unflagged = 1;
02125          }
02126       }
02127       pgm->undeleted = 1;
02128       pgm->deleted = 0;
02129 
02130       vms_p->vmArrayIndex = 0;
02131       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02132       if (fold == 0 && urgent == 0)
02133          vms_p->newmessages = vms_p->vmArrayIndex;
02134       if (fold == 1)
02135          vms_p->oldmessages = vms_p->vmArrayIndex;
02136       if (fold == 0 && urgent == 1)
02137          vms_p->urgentmessages = vms_p->vmArrayIndex;
02138       /*Freeing the searchpgm also frees the searchhdr*/
02139       mail_free_searchpgm(&pgm);
02140       ast_mutex_unlock(&vms_p->lock);
02141       vms_p->updated = 0;
02142       return vms_p->vmArrayIndex;
02143    } else {
02144       ast_mutex_lock(&vms_p->lock);
02145       mail_ping(vms_p->mailstream);
02146       ast_mutex_unlock(&vms_p->lock);
02147    }
02148    return 0;
02149 }
02150 
02151 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02152 {
02153    /* Check if mailbox is full */
02154    check_quota(vms, vmu->imapfolder);
02155    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02156       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02157       ast_play_and_wait(chan, "vm-mailboxfull");
02158       return -1;
02159    }
02160    
02161    /* Check if we have exceeded maxmsg */
02162    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));
02163    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02164       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02165       ast_play_and_wait(chan, "vm-mailboxfull");
02166       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02167       return -1;
02168    }
02169 
02170    return 0;
02171 }
02172 
02173 /*!
02174  * \brief Gets the number of messages that exist in a mailbox folder.
02175  * \param context
02176  * \param mailbox
02177  * \param folder
02178  * 
02179  * This method is used when IMAP backend is used.
02180  * \return The number of messages in this mailbox folder (zero or more).
02181  */
02182 static int messagecount(const char *context, const char *mailbox, const char *folder)
02183 {
02184    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02185       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02186    } else {
02187       return __messagecount(context, mailbox, folder);
02188    }
02189 }
02190 
02191 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)
02192 {
02193    char *myserveremail = serveremail;
02194    char fn[PATH_MAX];
02195    char introfn[PATH_MAX];
02196    char mailbox[256];
02197    char *stringp;
02198    FILE *p = NULL;
02199    char tmp[80] = "/tmp/astmail-XXXXXX";
02200    long len;
02201    void *buf;
02202    int tempcopy = 0;
02203    STRING str;
02204    int ret; /* for better error checking */
02205    char *imap_flags = NIL;
02206    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02207    int box = NEW_FOLDER;
02208 
02209    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02210    if (msgnum < 0) {
02211       if(!imapgreetings) {
02212          return 0;
02213       } else {
02214          box = GREETINGS_FOLDER;
02215       }
02216    }
02217    
02218    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02219       return -1;
02220    }
02221 
02222    /* Set urgent flag for IMAP message */
02223    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02224       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02225       imap_flags = "\\FLAGGED";
02226    }
02227    
02228    /* Attach only the first format */
02229    fmt = ast_strdupa(fmt);
02230    stringp = fmt;
02231    strsep(&stringp, "|");
02232 
02233    if (!ast_strlen_zero(vmu->serveremail))
02234       myserveremail = vmu->serveremail;
02235 
02236    if (msgnum > -1)
02237       make_file(fn, sizeof(fn), dir, msgnum);
02238    else
02239       ast_copy_string (fn, dir, sizeof(fn));
02240 
02241    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02242    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02243       *introfn = '\0';
02244    }
02245    
02246    if (ast_strlen_zero(vmu->email)) {
02247       /* We need the vmu->email to be set when we call make_email_file, but
02248        * if we keep it set, a duplicate e-mail will be created. So at the end
02249        * of this function, we will revert back to an empty string if tempcopy
02250        * is 1.
02251        */
02252       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02253       tempcopy = 1;
02254    }
02255 
02256    if (!strcmp(fmt, "wav49"))
02257       fmt = "WAV";
02258    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02259 
02260    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02261       command hangs. */
02262    if (!(p = vm_mkftemp(tmp))) {
02263       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02264       if (tempcopy)
02265          *(vmu->email) = '\0';
02266       return -1;
02267    }
02268 
02269    if (msgnum < 0 && imapgreetings) {
02270       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02271          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02272          return -1;
02273       }
02274       imap_delete_old_greeting(fn, vms);
02275    }
02276 
02277    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02278       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02279       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02280       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02281    /* read mail file to memory */
02282    len = ftell(p);
02283    rewind(p);
02284    if (!(buf = ast_malloc(len + 1))) {
02285       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02286       fclose(p);
02287       if (tempcopy)
02288          *(vmu->email) = '\0';
02289       return -1;
02290    }
02291    if (fread(buf, len, 1, p) < len) {
02292       if (ferror(p)) {
02293          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02294          return -1;
02295       }
02296    }
02297    ((char *) buf)[len] = '\0';
02298    INIT(&str, mail_string, buf, len);
02299    ret = init_mailstream(vms, box);
02300    if (ret == 0) {
02301       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02302       ast_mutex_lock(&vms->lock);
02303       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02304          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02305       ast_mutex_unlock(&vms->lock);
02306       fclose(p);
02307       unlink(tmp);
02308       ast_free(buf);
02309    } else {
02310       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02311       fclose(p);
02312       unlink(tmp);
02313       ast_free(buf);
02314       return -1;
02315    }
02316    ast_debug(3, "%s stored\n", fn);
02317    
02318    if (tempcopy)
02319       *(vmu->email) = '\0';
02320    inprocess_count(vmu->mailbox, vmu->context, -1);
02321    return 0;
02322 
02323 }
02324 
02325 /*!
02326  * \brief Gets the number of messages that exist in the inbox folder.
02327  * \param mailbox_context
02328  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02329  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02330  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02331  * 
02332  * This method is used when IMAP backend is used.
02333  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02334  *
02335  * \return zero on success, -1 on error.
02336  */
02337 
02338 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02339 {
02340    char tmp[PATH_MAX] = "";
02341    char *mailboxnc;
02342    char *context;
02343    char *mb;
02344    char *cur;
02345    if (newmsgs)
02346       *newmsgs = 0;
02347    if (oldmsgs)
02348       *oldmsgs = 0;
02349    if (urgentmsgs)
02350       *urgentmsgs = 0;
02351 
02352    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02353    /* If no mailbox, return immediately */
02354    if (ast_strlen_zero(mailbox_context))
02355       return 0;
02356    
02357    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02358    context = strchr(tmp, '@');
02359    if (strchr(mailbox_context, ',')) {
02360       int tmpnew, tmpold, tmpurgent;
02361       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02362       mb = tmp;
02363       while ((cur = strsep(&mb, ", "))) {
02364          if (!ast_strlen_zero(cur)) {
02365             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02366                return -1;
02367             else {
02368                if (newmsgs)
02369                   *newmsgs += tmpnew; 
02370                if (oldmsgs)
02371                   *oldmsgs += tmpold;
02372                if (urgentmsgs)
02373                   *urgentmsgs += tmpurgent;
02374             }
02375          }
02376       }
02377       return 0;
02378    }
02379    if (context) {
02380       *context = '\0';
02381       mailboxnc = tmp;
02382       context++;
02383    } else {
02384       context = "default";
02385       mailboxnc = (char *) mailbox_context;
02386    }
02387 
02388    if (newmsgs) {
02389       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02390       if (!vmu) {
02391          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02392          return -1;
02393       }
02394       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02395          return -1;
02396       }
02397    }
02398    if (oldmsgs) {
02399       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02400          return -1;
02401       }
02402    }
02403    if (urgentmsgs) {
02404       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02405          return -1;
02406       }
02407    }
02408    return 0;
02409 }
02410 
02411 /** 
02412  * \brief Determines if the given folder has messages.
02413  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02414  * \param folder the folder to look in
02415  *
02416  * This function is used when the mailbox is stored in an IMAP back end.
02417  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02418  * \return 1 if the folder has one or more messages. zero otherwise.
02419  */
02420 
02421 static int has_voicemail(const char *mailbox, const char *folder)
02422 {
02423    char tmp[256], *tmp2, *box, *context;
02424    ast_copy_string(tmp, mailbox, sizeof(tmp));
02425    tmp2 = tmp;
02426    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02427       while ((box = strsep(&tmp2, ",&"))) {
02428          if (!ast_strlen_zero(box)) {
02429             if (has_voicemail(box, folder)) {
02430                return 1;
02431             }
02432          }
02433       }
02434    }
02435    if ((context = strchr(tmp, '@'))) {
02436       *context++ = '\0';
02437    } else {
02438       context = "default";
02439    }
02440    return __messagecount(context, tmp, folder) ? 1 : 0;
02441 }
02442 
02443 /*!
02444  * \brief Copies a message from one mailbox to another.
02445  * \param chan
02446  * \param vmu
02447  * \param imbox
02448  * \param msgnum
02449  * \param duration
02450  * \param recip
02451  * \param fmt
02452  * \param dir
02453  *
02454  * This works with IMAP storage based mailboxes.
02455  *
02456  * \return zero on success, -1 on error.
02457  */
02458 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)
02459 {
02460    struct vm_state *sendvms = NULL, *destvms = NULL;
02461    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02462    if (msgnum >= recip->maxmsg) {
02463       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02464       return -1;
02465    }
02466    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02467       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02468       return -1;
02469    }
02470    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02471       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02472       return -1;
02473    }
02474    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02475    ast_mutex_lock(&sendvms->lock);
02476    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02477       ast_mutex_unlock(&sendvms->lock);
02478       return 0;
02479    }
02480    ast_mutex_unlock(&sendvms->lock);
02481    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02482    return -1;
02483 }
02484 
02485 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02486 {
02487    char tmp[256], *t = tmp;
02488    size_t left = sizeof(tmp);
02489    
02490    if (box == OLD_FOLDER) {
02491       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02492    } else {
02493       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02494    }
02495 
02496    if (box == NEW_FOLDER) {
02497       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02498    } else {
02499       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02500    }
02501 
02502    /* Build up server information */
02503    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02504 
02505    /* Add authentication user if present */
02506    if (!ast_strlen_zero(authuser))
02507       ast_build_string(&t, &left, "/authuser=%s", authuser);
02508 
02509    /* Add flags if present */
02510    if (!ast_strlen_zero(imapflags))
02511       ast_build_string(&t, &left, "/%s", imapflags);
02512 
02513    /* End with username */
02514 #if 1
02515    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02516 #else
02517    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02518 #endif
02519    if (box == NEW_FOLDER || box == OLD_FOLDER)
02520       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02521    else if (box == GREETINGS_FOLDER)
02522       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02523    else {   /* Other folders such as Friends, Family, etc... */
02524       if (!ast_strlen_zero(imapparentfolder)) {
02525          /* imapparentfolder would typically be set to INBOX */
02526          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02527       } else {
02528          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02529       }
02530    }
02531 }
02532 
02533 static int init_mailstream(struct vm_state *vms, int box)
02534 {
02535    MAILSTREAM *stream = NIL;
02536    long debug;
02537    char tmp[256];
02538    
02539    if (!vms) {
02540       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02541       return -1;
02542    }
02543    if (option_debug > 2)
02544       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02545    if (vms->mailstream == NIL || !vms->mailstream) {
02546       if (option_debug)
02547          ast_log(LOG_DEBUG, "mailstream not set.\n");
02548    } else {
02549       stream = vms->mailstream;
02550    }
02551    /* debug = T;  user wants protocol telemetry? */
02552    debug = NIL;  /* NO protocol telemetry? */
02553 
02554    if (delimiter == '\0') {      /* did not probe the server yet */
02555       char *cp;
02556 #ifdef USE_SYSTEM_IMAP
02557 #include <imap/linkage.c>
02558 #elif defined(USE_SYSTEM_CCLIENT)
02559 #include <c-client/linkage.c>
02560 #else
02561 #include "linkage.c"
02562 #endif
02563       /* Connect to INBOX first to get folders delimiter */
02564       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02565       ast_mutex_lock(&vms->lock);
02566       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02567       ast_mutex_unlock(&vms->lock);
02568       if (stream == NIL) {
02569          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02570          return -1;
02571       }
02572       get_mailbox_delimiter(stream);
02573       /* update delimiter in imapfolder */
02574       for (cp = vms->imapfolder; *cp; cp++)
02575          if (*cp == '/')
02576             *cp = delimiter;
02577    }
02578    /* Now connect to the target folder */
02579    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02580    if (option_debug > 2)
02581       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02582    ast_mutex_lock(&vms->lock);
02583    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02584    ast_mutex_unlock(&vms->lock);
02585    if (vms->mailstream == NIL) {
02586       return -1;
02587    } else {
02588       return 0;
02589    }
02590 }
02591 
02592 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02593 {
02594    SEARCHPGM *pgm;
02595    SEARCHHEADER *hdr;
02596    int ret, urgent = 0;
02597 
02598    /* If Urgent, then look at INBOX */
02599    if (box == 11) {
02600       box = NEW_FOLDER;
02601       urgent = 1;
02602    }
02603 
02604    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02605    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02606    vms->imapversion = vmu->imapversion;
02607    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02608 
02609    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02610       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02611       return -1;
02612    }
02613    
02614    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02615    
02616    /* Check Quota */
02617    if  (box == 0)  {
02618       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02619       check_quota(vms, (char *) mbox(vmu, box));
02620    }
02621 
02622    ast_mutex_lock(&vms->lock);
02623    pgm = mail_newsearchpgm();
02624 
02625    /* Check IMAP folder for Asterisk messages only... */
02626    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02627    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02628    pgm->header = hdr;
02629    pgm->deleted = 0;
02630    pgm->undeleted = 1;
02631 
02632    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02633    if (box == NEW_FOLDER && urgent == 1) {
02634       pgm->unseen = 1;
02635       pgm->seen = 0;
02636       pgm->flagged = 1;
02637       pgm->unflagged = 0;
02638    } else if (box == NEW_FOLDER && urgent == 0) {
02639       pgm->unseen = 1;
02640       pgm->seen = 0;
02641       pgm->flagged = 0;
02642       pgm->unflagged = 1;
02643    } else if (box == OLD_FOLDER) {
02644       pgm->seen = 1;
02645       pgm->unseen = 0;
02646    }
02647 
02648    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02649 
02650    vms->vmArrayIndex = 0;
02651    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02652    vms->lastmsg = vms->vmArrayIndex - 1;
02653    mail_free_searchpgm(&pgm);
02654    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02655     * ensure to allocate enough space to account for all of them. Warn if old messages
02656     * have not been checked first as that is required.
02657     */
02658    if (box == 0 && !vms->dh_arraysize) {
02659       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02660    }
02661    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02662       ast_mutex_unlock(&vms->lock);
02663       return -1;
02664    }
02665 
02666    ast_mutex_unlock(&vms->lock);
02667    return 0;
02668 }
02669 
02670 static void write_file(char *filename, char *buffer, unsigned long len)
02671 {
02672    FILE *output;
02673 
02674    output = fopen (filename, "w");
02675    if (fwrite(buffer, len, 1, output) != 1) {
02676       if (ferror(output)) {
02677          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02678       }
02679    }
02680    fclose (output);
02681 }
02682 
02683 static void update_messages_by_imapuser(const char *user, unsigned long number)
02684 {
02685    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02686 
02687    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02688       return;
02689    }
02690 
02691    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02692    vms->msgArray[vms->vmArrayIndex++] = number;
02693 }
02694 
02695 void mm_searched(MAILSTREAM *stream, unsigned long number)
02696 {
02697    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02698 
02699    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02700       return;
02701 
02702    update_messages_by_imapuser(user, number);
02703 }
02704 
02705 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02706 {
02707    struct ast_variable *var;
02708    struct ast_vm_user *vmu;
02709 
02710    vmu = ast_calloc(1, sizeof *vmu);
02711    if (!vmu)
02712       return NULL;
02713    ast_set_flag(vmu, VM_ALLOCED);
02714    populate_defaults(vmu);
02715 
02716    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02717    if (var) {
02718       apply_options_full(vmu, var);
02719       ast_variables_destroy(var);
02720       return vmu;
02721    } else {
02722       ast_free(vmu);
02723       return NULL;
02724    }
02725 }
02726 
02727 /* Interfaces to C-client */
02728 
02729 void mm_exists(MAILSTREAM * stream, unsigned long number)
02730 {
02731    /* mail_ping will callback here if new mail! */
02732    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02733    if (number == 0) return;
02734    set_update(stream);
02735 }
02736 
02737 
02738 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02739 {
02740    /* mail_ping will callback here if expunged mail! */
02741    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02742    if (number == 0) return;
02743    set_update(stream);
02744 }
02745 
02746 
02747 void mm_flags(MAILSTREAM * stream, unsigned long number)
02748 {
02749    /* mail_ping will callback here if read mail! */
02750    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02751    if (number == 0) return;
02752    set_update(stream);
02753 }
02754 
02755 
02756 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02757 {
02758    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02759    mm_log (string, errflg);
02760 }
02761 
02762 
02763 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02764 {
02765    if (delimiter == '\0') {
02766       delimiter = delim;
02767    }
02768 
02769    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02770    if (attributes & LATT_NOINFERIORS)
02771       ast_debug(5, "no inferiors\n");
02772    if (attributes & LATT_NOSELECT)
02773       ast_debug(5, "no select\n");
02774    if (attributes & LATT_MARKED)
02775       ast_debug(5, "marked\n");
02776    if (attributes & LATT_UNMARKED)
02777       ast_debug(5, "unmarked\n");
02778 }
02779 
02780 
02781 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02782 {
02783    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02784    if (attributes & LATT_NOINFERIORS)
02785       ast_debug(5, "no inferiors\n");
02786    if (attributes & LATT_NOSELECT)
02787       ast_debug(5, "no select\n");
02788    if (attributes & LATT_MARKED)
02789       ast_debug(5, "marked\n");
02790    if (attributes & LATT_UNMARKED)
02791       ast_debug(5, "unmarked\n");
02792 }
02793 
02794 
02795 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02796 {
02797    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02798    if (status->flags & SA_MESSAGES)
02799       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02800    if (status->flags & SA_RECENT)
02801       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02802    if (status->flags & SA_UNSEEN)
02803       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02804    if (status->flags & SA_UIDVALIDITY)
02805       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02806    if (status->flags & SA_UIDNEXT)
02807       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02808    ast_log(AST_LOG_NOTICE, "\n");
02809 }
02810 
02811 
02812 void mm_log(char *string, long errflg)
02813 {
02814    switch ((short) errflg) {
02815       case NIL:
02816          ast_debug(1, "IMAP Info: %s\n", string);
02817          break;
02818       case PARSE:
02819       case WARN:
02820          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02821          break;
02822       case ERROR:
02823          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02824          break;
02825    }
02826 }
02827 
02828 
02829 void mm_dlog(char *string)
02830 {
02831    ast_log(AST_LOG_NOTICE, "%s\n", string);
02832 }
02833 
02834 
02835 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02836 {
02837    struct ast_vm_user *vmu;
02838 
02839    ast_debug(4, "Entering callback mm_login\n");
02840 
02841    ast_copy_string(user, mb->user, MAILTMPLEN);
02842 
02843    /* We should only do this when necessary */
02844    if (!ast_strlen_zero(authpassword)) {
02845       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02846    } else {
02847       AST_LIST_TRAVERSE(&users, vmu, list) {
02848          if (!strcasecmp(mb->user, vmu->imapuser)) {
02849             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02850             break;
02851          }
02852       }
02853       if (!vmu) {
02854          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02855             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02856             free_user(vmu);
02857          }
02858       }
02859    }
02860 }
02861 
02862 
02863 void mm_critical(MAILSTREAM * stream)
02864 {
02865 }
02866 
02867 
02868 void mm_nocritical(MAILSTREAM * stream)
02869 {
02870 }
02871 
02872 
02873 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02874 {
02875    kill (getpid (), SIGSTOP);
02876    return NIL;
02877 }
02878 
02879 
02880 void mm_fatal(char *string)
02881 {
02882    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02883 }
02884 
02885 /* C-client callback to handle quota */
02886 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02887 {
02888    struct vm_state *vms;
02889    char *mailbox = stream->mailbox, *user;
02890    char buf[1024] = "";
02891    unsigned long usage = 0, limit = 0;
02892    
02893    while (pquota) {
02894       usage = pquota->usage;
02895       limit = pquota->limit;
02896       pquota = pquota->next;
02897    }
02898    
02899    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)))) {
02900       ast_log(AST_LOG_ERROR, "No state found.\n");
02901       return;
02902    }
02903 
02904    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02905 
02906    vms->quota_usage = usage;
02907    vms->quota_limit = limit;
02908 }
02909 
02910 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02911 {
02912    char *start, *eol_pnt;
02913    int taglen;
02914 
02915    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02916       return NULL;
02917 
02918    taglen = strlen(tag) + 1;
02919    if (taglen < 1)
02920       return NULL;
02921 
02922    if (!(start = strstr(header, tag)))
02923       return NULL;
02924 
02925    /* Since we can be called multiple times we should clear our buffer */
02926    memset(buf, 0, len);
02927 
02928    ast_copy_string(buf, start+taglen, len);
02929    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02930       *eol_pnt = '\0';
02931    return buf;
02932 }
02933 
02934 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02935 {
02936    char *start, *quote, *eol_pnt;
02937 
02938    if (ast_strlen_zero(mailbox))
02939       return NULL;
02940 
02941    if (!(start = strstr(mailbox, "/user=")))
02942       return NULL;
02943 
02944    ast_copy_string(buf, start+6, len);
02945 
02946    if (!(quote = strchr(buf, '\"'))) {
02947       if (!(eol_pnt = strchr(buf, '/')))
02948          eol_pnt = strchr(buf,'}');
02949       *eol_pnt = '\0';
02950       return buf;
02951    } else {
02952       eol_pnt = strchr(buf+1,'\"');
02953       *eol_pnt = '\0';
02954       return buf+1;
02955    }
02956 }
02957 
02958 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02959 {
02960    struct vm_state *vms_p;
02961 
02962    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02963    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02964       return vms_p;
02965    }
02966    if (option_debug > 4)
02967       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02968    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02969       return NULL;
02970    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02971    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02972    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02973    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02974    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02975    vms_p->imapversion = vmu->imapversion;
02976    if (option_debug > 4)
02977       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02978    vms_p->updated = 1;
02979    /* set mailbox to INBOX! */
02980    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02981    init_vm_state(vms_p);
02982    vmstate_insert(vms_p);
02983    return vms_p;
02984 }
02985 
02986 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02987 {
02988    struct vmstate *vlist = NULL;
02989 
02990    if (interactive) {
02991       struct vm_state *vms;
02992       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02993       vms = pthread_getspecific(ts_vmstate.key);
02994       return vms;
02995    }
02996 
02997    AST_LIST_LOCK(&vmstates);
02998    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02999       if (!vlist->vms) {
03000          ast_debug(3, "error: vms is NULL for %s\n", user);
03001          continue;
03002       }
03003       if (vlist->vms->imapversion != imapversion) {
03004          continue;
03005       }
03006       if (!vlist->vms->imapuser) {
03007          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03008          continue;
03009       }
03010 
03011       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03012          AST_LIST_UNLOCK(&vmstates);
03013          return vlist->vms;
03014       }
03015    }
03016    AST_LIST_UNLOCK(&vmstates);
03017 
03018    ast_debug(3, "%s not found in vmstates\n", user);
03019 
03020    return NULL;
03021 }
03022 
03023 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03024 {
03025 
03026    struct vmstate *vlist = NULL;
03027    const char *local_context = S_OR(context, "default");
03028 
03029    if (interactive) {
03030       struct vm_state *vms;
03031       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03032       vms = pthread_getspecific(ts_vmstate.key);
03033       return vms;
03034    }
03035 
03036    AST_LIST_LOCK(&vmstates);
03037    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03038       if (!vlist->vms) {
03039          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03040          continue;
03041       }
03042       if (vlist->vms->imapversion != imapversion) {
03043          continue;
03044       }
03045       if (!vlist->vms->username || !vlist->vms->context) {
03046          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03047          continue;
03048       }
03049 
03050       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);
03051       
03052       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03053          ast_debug(3, "Found it!\n");
03054          AST_LIST_UNLOCK(&vmstates);
03055          return vlist->vms;
03056       }
03057    }
03058    AST_LIST_UNLOCK(&vmstates);
03059 
03060    ast_debug(3, "%s not found in vmstates\n", mailbox);
03061 
03062    return NULL;
03063 }
03064 
03065 static void vmstate_insert(struct vm_state *vms) 
03066 {
03067    struct vmstate *v;
03068    struct vm_state *altvms;
03069 
03070    /* If interactive, it probably already exists, and we should
03071       use the one we already have since it is more up to date.
03072       We can compare the username to find the duplicate */
03073    if (vms->interactive == 1) {
03074       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03075       if (altvms) {  
03076          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03077          vms->newmessages = altvms->newmessages;
03078          vms->oldmessages = altvms->oldmessages;
03079          vms->vmArrayIndex = altvms->vmArrayIndex;
03080          vms->lastmsg = altvms->lastmsg;
03081          vms->curmsg = altvms->curmsg;
03082          /* get a pointer to the persistent store */
03083          vms->persist_vms = altvms;
03084          /* Reuse the mailstream? */
03085 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03086          vms->mailstream = altvms->mailstream;
03087 #else
03088          vms->mailstream = NIL;
03089 #endif
03090       }
03091       return;
03092    }
03093 
03094    if (!(v = ast_calloc(1, sizeof(*v))))
03095       return;
03096    
03097    v->vms = vms;
03098 
03099    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03100 
03101    AST_LIST_LOCK(&vmstates);
03102    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03103    AST_LIST_UNLOCK(&vmstates);
03104 }
03105 
03106 static void vmstate_delete(struct vm_state *vms) 
03107 {
03108    struct vmstate *vc = NULL;
03109    struct vm_state *altvms = NULL;
03110 
03111    /* If interactive, we should copy pertinent info
03112       back to the persistent state (to make update immediate) */
03113    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03114       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03115       altvms->newmessages = vms->newmessages;
03116       altvms->oldmessages = vms->oldmessages;
03117       altvms->updated = 1;
03118       vms->mailstream = mail_close(vms->mailstream);
03119 
03120       /* Interactive states are not stored within the persistent list */
03121       return;
03122    }
03123    
03124    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03125    
03126    AST_LIST_LOCK(&vmstates);
03127    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03128       if (vc->vms == vms) {
03129          AST_LIST_REMOVE_CURRENT(list);
03130          break;
03131       }
03132    }
03133    AST_LIST_TRAVERSE_SAFE_END
03134    AST_LIST_UNLOCK(&vmstates);
03135    
03136    if (vc) {
03137       ast_mutex_destroy(&vc->vms->lock);
03138       ast_free(vc);
03139    }
03140    else
03141       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03142 }
03143 
03144 static void set_update(MAILSTREAM * stream) 
03145 {
03146    struct vm_state *vms;
03147    char *mailbox = stream->mailbox, *user;
03148    char buf[1024] = "";
03149 
03150    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03151       if (user && option_debug > 2)
03152          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03153       return;
03154    }
03155 
03156    ast_debug(3, "User %s mailbox set for update.\n", user);
03157 
03158    vms->updated = 1; /* Set updated flag since mailbox changed */
03159 }
03160 
03161 static void init_vm_state(struct vm_state *vms) 
03162 {
03163    int x;
03164    vms->vmArrayIndex = 0;
03165    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03166       vms->msgArray[x] = 0;
03167    }
03168    ast_mutex_init(&vms->lock);
03169 }
03170 
03171 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03172 {
03173    char *body_content;
03174    char *body_decoded;
03175    char *fn = is_intro ? vms->introfn : vms->fn;
03176    unsigned long len;
03177    unsigned long newlen;
03178    char filename[256];
03179    
03180    if (!body || body == NIL)
03181       return -1;
03182 
03183    ast_mutex_lock(&vms->lock);
03184    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03185    ast_mutex_unlock(&vms->lock);
03186    if (body_content != NIL) {
03187       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03188       /* ast_debug(1,body_content); */
03189       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03190       /* If the body of the file is empty, return an error */
03191       if (!newlen) {
03192          return -1;
03193       }
03194       write_file(filename, (char *) body_decoded, newlen);
03195    } else {
03196       ast_debug(5, "Body of message is NULL.\n");
03197       return -1;
03198    }
03199    return 0;
03200 }
03201 
03202 /*! 
03203  * \brief Get delimiter via mm_list callback 
03204  * \param stream
03205  *
03206  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03207  */
03208 /* MUTEX should already be held */
03209 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03210    char tmp[50];
03211    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03212    mail_list(stream, tmp, "*");
03213 }
03214 
03215 /*! 
03216  * \brief Check Quota for user 
03217  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03218  * \param mailbox the mailbox to check the quota for.
03219  *
03220  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03221  */
03222 static void check_quota(struct vm_state *vms, char *mailbox) {
03223    ast_mutex_lock(&vms->lock);
03224    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03225    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03226    if (vms && vms->mailstream != NULL) {
03227       imap_getquotaroot(vms->mailstream, mailbox);
03228    } else {
03229       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03230    }
03231    ast_mutex_unlock(&vms->lock);
03232 }
03233 
03234 #endif /* IMAP_STORAGE */
03235 
03236 /*! \brief Lock file path
03237  * only return failure if ast_lock_path returns 'timeout',
03238  * not if the path does not exist or any other reason
03239  */
03240 static int vm_lock_path(const char *path)
03241 {
03242    switch (ast_lock_path(path)) {
03243    case AST_LOCK_TIMEOUT:
03244       return -1;
03245    default:
03246       return 0;
03247    }
03248 }
03249 
03250 
03251 #ifdef ODBC_STORAGE
03252 struct generic_prepare_struct {
03253    char *sql;
03254    int argc;
03255    char **argv;
03256 };
03257 
03258 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03259 {
03260    struct generic_prepare_struct *gps = data;
03261    int res, i;
03262    SQLHSTMT stmt;
03263 
03264    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03265    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03266       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03267       return NULL;
03268    }
03269    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03270    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03271       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03272       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03273       return NULL;
03274    }
03275    for (i = 0; i < gps->argc; i++)
03276       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03277 
03278    return stmt;
03279 }
03280 
03281 /*!
03282  * \brief Retrieves a file from an ODBC data store.
03283  * \param dir the path to the file to be retreived.
03284  * \param msgnum the message number, such as within a mailbox folder.
03285  * 
03286  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03287  * 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.
03288  *
03289  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03290  * The output is the message information file with the name msgnum and the extension .txt
03291  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03292  * 
03293  * \return 0 on success, -1 on error.
03294  */
03295 static int retrieve_file(char *dir, int msgnum)
03296 {
03297    int x = 0;
03298    int res;
03299    int fd = -1;
03300    size_t fdlen = 0;
03301    void *fdm = MAP_FAILED;
03302    SQLSMALLINT colcount = 0;
03303    SQLHSTMT stmt;
03304    char sql[PATH_MAX];
03305    char fmt[80]="";
03306    char *c;
03307    char coltitle[256];
03308    SQLSMALLINT collen;
03309    SQLSMALLINT datatype;
03310    SQLSMALLINT decimaldigits;
03311    SQLSMALLINT nullable;
03312    SQLULEN colsize;
03313    SQLLEN colsize2;
03314    FILE *f = NULL;
03315    char rowdata[80];
03316    char fn[PATH_MAX];
03317    char full_fn[PATH_MAX];
03318    char msgnums[80];
03319    char *argv[] = { dir, msgnums };
03320    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03321 
03322    struct odbc_obj *obj;
03323    obj = ast_odbc_request_obj(odbc_database, 0);
03324    if (obj) {
03325       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03326       c = strchr(fmt, '|');
03327       if (c)
03328          *c = '\0';
03329       if (!strcasecmp(fmt, "wav49"))
03330          strcpy(fmt, "WAV");
03331       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03332       if (msgnum > -1)
03333          make_file(fn, sizeof(fn), dir, msgnum);
03334       else
03335          ast_copy_string(fn, dir, sizeof(fn));
03336 
03337       /* Create the information file */
03338       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03339       
03340       if (!(f = fopen(full_fn, "w+"))) {
03341          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03342          goto yuck;
03343       }
03344       
03345       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03346       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03347       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03348       if (!stmt) {
03349          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03350          ast_odbc_release_obj(obj);
03351          goto yuck;
03352       }
03353       res = SQLFetch(stmt);
03354       if (res == SQL_NO_DATA) {
03355          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03356          ast_odbc_release_obj(obj);
03357          goto yuck;
03358       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03359          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03360          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03361          ast_odbc_release_obj(obj);
03362          goto yuck;
03363       }
03364       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03365       if (fd < 0) {
03366          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03367          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03368          ast_odbc_release_obj(obj);
03369          goto yuck;
03370       }
03371       res = SQLNumResultCols(stmt, &colcount);
03372       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03373          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03374          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03375          ast_odbc_release_obj(obj);
03376          goto yuck;
03377       }
03378       if (f) 
03379          fprintf(f, "[message]\n");
03380       for (x = 0; x < colcount; x++) {
03381          rowdata[0] = '\0';
03382          colsize = 0;
03383          collen = sizeof(coltitle);
03384          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03385                   &datatype, &colsize, &decimaldigits, &nullable);
03386          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03387             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03388             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03389             ast_odbc_release_obj(obj);
03390             goto yuck;
03391          }
03392          if (!strcasecmp(coltitle, "recording")) {
03393             off_t offset;
03394             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03395             fdlen = colsize2;
03396             if (fd > -1) {
03397                char tmp[1]="";
03398                lseek(fd, fdlen - 1, SEEK_SET);
03399                if (write(fd, tmp, 1) != 1) {
03400                   close(fd);
03401                   fd = -1;
03402                   continue;
03403                }
03404                /* Read out in small chunks */
03405                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03406                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03407                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03408                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03409                      ast_odbc_release_obj(obj);
03410                      goto yuck;
03411                   } else {
03412                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03413                      munmap(fdm, CHUNKSIZE);
03414                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03415                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03416                         unlink(full_fn);
03417                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03418                         ast_odbc_release_obj(obj);
03419                         goto yuck;
03420                      }
03421                   }
03422                }
03423                if (truncate(full_fn, fdlen) < 0) {
03424                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03425                }
03426             }
03427          } else {
03428             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03429             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03430                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03431                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03432                ast_odbc_release_obj(obj);
03433                goto yuck;
03434             }
03435             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03436                fprintf(f, "%s=%s\n", coltitle, rowdata);
03437          }
03438       }
03439       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03440       ast_odbc_release_obj(obj);
03441    } else
03442       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03443 yuck:
03444    if (f)
03445       fclose(f);
03446    if (fd > -1)
03447       close(fd);
03448    return x - 1;
03449 }
03450 
03451 /*!
03452  * \brief Determines the highest message number in use for a given user and mailbox folder.
03453  * \param vmu 
03454  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03455  *
03456  * This method is used when mailboxes are stored in an ODBC back end.
03457  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03458  *
03459  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03460 
03461  */
03462 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03463 {
03464    int x = 0;
03465    int res;
03466    SQLHSTMT stmt;
03467    char sql[PATH_MAX];
03468    char rowdata[20];
03469    char *argv[] = { dir };
03470    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03471 
03472    struct odbc_obj *obj;
03473    obj = ast_odbc_request_obj(odbc_database, 0);
03474    if (obj) {
03475       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03476 
03477       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03478       if (!stmt) {
03479          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03480          ast_odbc_release_obj(obj);
03481          goto yuck;
03482       }
03483       res = SQLFetch(stmt);
03484       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03485          if (res == SQL_NO_DATA) {
03486             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03487          } else {
03488             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03489          }
03490 
03491          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03492          ast_odbc_release_obj(obj);
03493          goto yuck;
03494       }
03495       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03496       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03497          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03498          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03499          ast_odbc_release_obj(obj);
03500          goto yuck;
03501       }
03502       if (sscanf(rowdata, "%30d", &x) != 1)
03503          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03504       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03505       ast_odbc_release_obj(obj);
03506       return x;
03507    } else
03508       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03509 yuck:
03510    return x - 1;
03511 }
03512 
03513 /*!
03514  * \brief Determines if the specified message exists.
03515  * \param dir the folder the mailbox folder to look for messages. 
03516  * \param msgnum the message index to query for.
03517  *
03518  * This method is used when mailboxes are stored in an ODBC back end.
03519  *
03520  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03521  */
03522 static int message_exists(char *dir, int msgnum)
03523 {
03524    int x = 0;
03525    int res;
03526    SQLHSTMT stmt;
03527    char sql[PATH_MAX];
03528    char rowdata[20];
03529    char msgnums[20];
03530    char *argv[] = { dir, msgnums };
03531    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03532 
03533    struct odbc_obj *obj;
03534    obj = ast_odbc_request_obj(odbc_database, 0);
03535    if (obj) {
03536       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03537       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03538       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03539       if (!stmt) {
03540          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03541          ast_odbc_release_obj(obj);
03542          goto yuck;
03543       }
03544       res = SQLFetch(stmt);
03545       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03546          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03547          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03548          ast_odbc_release_obj(obj);
03549          goto yuck;
03550       }
03551       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03552       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03553          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03554          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03555          ast_odbc_release_obj(obj);
03556          goto yuck;
03557       }
03558       if (sscanf(rowdata, "%30d", &x) != 1)
03559          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03560       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03561       ast_odbc_release_obj(obj);
03562    } else
03563       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03564 yuck:
03565    return x;
03566 }
03567 
03568 /*!
03569  * \brief returns the number of messages found.
03570  * \param vmu
03571  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03572  *
03573  * This method is used when mailboxes are stored in an ODBC back end.
03574  *
03575  * \return The count of messages being zero or more, less than zero on error.
03576  */
03577 static int count_messages(struct ast_vm_user *vmu, char *dir)
03578 {
03579    int x = 0;
03580    int res;
03581    SQLHSTMT stmt;
03582    char sql[PATH_MAX];
03583    char rowdata[20];
03584    char *argv[] = { dir };
03585    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03586 
03587    struct odbc_obj *obj;
03588    obj = ast_odbc_request_obj(odbc_database, 0);
03589    if (obj) {
03590       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03591       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03592       if (!stmt) {
03593          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03594          ast_odbc_release_obj(obj);
03595          goto yuck;
03596       }
03597       res = SQLFetch(stmt);
03598       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03599          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03600          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03601          ast_odbc_release_obj(obj);
03602          goto yuck;
03603       }
03604       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03605       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03606          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03607          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03608          ast_odbc_release_obj(obj);
03609          goto yuck;
03610       }
03611       if (sscanf(rowdata, "%30d", &x) != 1)
03612          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03613       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03614       ast_odbc_release_obj(obj);
03615       return x;
03616    } else
03617       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03618 yuck:
03619    return x - 1;
03620 
03621 }
03622 
03623 /*!
03624  * \brief Deletes a message from the mailbox folder.
03625  * \param sdir The mailbox folder to work in.
03626  * \param smsg The message index to be deleted.
03627  *
03628  * This method is used when mailboxes are stored in an ODBC back end.
03629  * The specified message is directly deleted from the database 'voicemessages' table.
03630  * 
03631  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03632  */
03633 static void delete_file(const char *sdir, int smsg)
03634 {
03635    SQLHSTMT stmt;
03636    char sql[PATH_MAX];
03637    char msgnums[20];
03638    char *argv[] = { NULL, msgnums };
03639    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03640    struct odbc_obj *obj;
03641 
03642    argv[0] = ast_strdupa(sdir);
03643 
03644    obj = ast_odbc_request_obj(odbc_database, 0);
03645    if (obj) {
03646       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03647       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03648       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03649       if (!stmt)
03650          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03651       else
03652          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03653       ast_odbc_release_obj(obj);
03654    } else
03655       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03656    return;  
03657 }
03658 
03659 /*!
03660  * \brief Copies a voicemail from one mailbox to another.
03661  * \param sdir the folder for which to look for the message to be copied.
03662  * \param smsg the index of the message to be copied.
03663  * \param ddir the destination folder to copy the message into.
03664  * \param dmsg the index to be used for the copied message.
03665  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03666  * \param dmailboxcontext The context for the destination user.
03667  *
03668  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03669  */
03670 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03671 {
03672    SQLHSTMT stmt;
03673    char sql[512];
03674    char msgnums[20];
03675    char msgnumd[20];
03676    struct odbc_obj *obj;
03677    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03678    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03679 
03680    delete_file(ddir, dmsg);
03681    obj = ast_odbc_request_obj(odbc_database, 0);
03682    if (obj) {
03683       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03684       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03685       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);
03686       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03687       if (!stmt)
03688          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03689       else
03690          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03691       ast_odbc_release_obj(obj);
03692    } else
03693       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03694    return;  
03695 }
03696 
03697 struct insert_data {
03698    char *sql;
03699    const char *dir;
03700    const char *msgnums;
03701    void *data;
03702    SQLLEN datalen;
03703    SQLLEN indlen;
03704    const char *context;
03705    const char *macrocontext;
03706    const char *callerid;
03707    const char *origtime;
03708    const char *duration;
03709    const char *mailboxuser;
03710    const char *mailboxcontext;
03711    const char *category;
03712    const char *flag;
03713 };
03714 
03715 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03716 {
03717    struct insert_data *data = vdata;
03718    int res;
03719    SQLHSTMT stmt;
03720 
03721    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03722    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03723       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03724       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03725       return NULL;
03726    }
03727 
03728    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03729    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03730    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03731    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03732    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03733    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03734    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03735    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03736    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03737    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03738    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03739    if (!ast_strlen_zero(data->category)) {
03740       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03741    }
03742    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03743    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03744       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03745       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03746       return NULL;
03747    }
03748 
03749    return stmt;
03750 }
03751 
03752 /*!
03753  * \brief Stores a voicemail into the database.
03754  * \param dir the folder the mailbox folder to store the message.
03755  * \param mailboxuser the user owning the mailbox folder.
03756  * \param mailboxcontext
03757  * \param msgnum the message index for the message to be stored.
03758  *
03759  * This method is used when mailboxes are stored in an ODBC back end.
03760  * The message sound file and information file is looked up on the file system. 
03761  * A SQL query is invoked to store the message into the (MySQL) database.
03762  *
03763  * \return the zero on success -1 on error.
03764  */
03765 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03766 {
03767    int res = 0;
03768    int fd = -1;
03769    void *fdm = MAP_FAILED;
03770    off_t fdlen = -1;
03771    SQLHSTMT stmt;
03772    char sql[PATH_MAX];
03773    char msgnums[20];
03774    char fn[PATH_MAX];
03775    char full_fn[PATH_MAX];
03776    char fmt[80]="";
03777    char *c;
03778    struct ast_config *cfg = NULL;
03779    struct odbc_obj *obj;
03780    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03781       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03782    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03783 
03784    delete_file(dir, msgnum);
03785    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03786       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03787       return -1;
03788    }
03789 
03790    do {
03791       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03792       c = strchr(fmt, '|');
03793       if (c)
03794          *c = '\0';
03795       if (!strcasecmp(fmt, "wav49"))
03796          strcpy(fmt, "WAV");
03797       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03798       if (msgnum > -1)
03799          make_file(fn, sizeof(fn), dir, msgnum);
03800       else
03801          ast_copy_string(fn, dir, sizeof(fn));
03802       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03803       cfg = ast_config_load(full_fn, config_flags);
03804       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03805       fd = open(full_fn, O_RDWR);
03806       if (fd < 0) {
03807          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03808          res = -1;
03809          break;
03810       }
03811       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03812          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03813             idata.context = "";
03814          }
03815          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03816             idata.macrocontext = "";
03817          }
03818          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03819             idata.callerid = "";
03820          }
03821          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03822             idata.origtime = "";
03823          }
03824          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03825             idata.duration = "";
03826          }
03827          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03828             idata.category = "";
03829          }
03830          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03831             idata.flag = "";
03832          }
03833       }
03834       fdlen = lseek(fd, 0, SEEK_END);
03835       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03836          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03837          res = -1;
03838          break;
03839       }
03840       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03841       if (fdm == MAP_FAILED) {
03842          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03843          res = -1;
03844          break;
03845       } 
03846       idata.data = fdm;
03847       idata.datalen = idata.indlen = fdlen;
03848 
03849       if (!ast_strlen_zero(idata.category)) 
03850          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03851       else
03852          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03853 
03854       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03855          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03856       } else {
03857          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03858          res = -1;
03859       }
03860    } while (0);
03861    if (obj) {
03862       ast_odbc_release_obj(obj);
03863    }
03864    if (cfg)
03865       ast_config_destroy(cfg);
03866    if (fdm != MAP_FAILED)
03867       munmap(fdm, fdlen);
03868    if (fd > -1)
03869       close(fd);
03870    return res;
03871 }
03872 
03873 /*!
03874  * \brief Renames a message in a mailbox folder.
03875  * \param sdir The folder of the message to be renamed.
03876  * \param smsg The index of the message to be renamed.
03877  * \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.
03878  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03879  * \param ddir The destination folder for the message to be renamed into
03880  * \param dmsg The destination message for the message to be renamed.
03881  *
03882  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03883  * 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.
03884  * 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.
03885  */
03886 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03887 {
03888    SQLHSTMT stmt;
03889    char sql[PATH_MAX];
03890    char msgnums[20];
03891    char msgnumd[20];
03892    struct odbc_obj *obj;
03893    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03894    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03895 
03896    delete_file(ddir, dmsg);
03897    obj = ast_odbc_request_obj(odbc_database, 0);
03898    if (obj) {
03899       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03900       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03901       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03902       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03903       if (!stmt)
03904          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03905       else
03906          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03907       ast_odbc_release_obj(obj);
03908    } else
03909       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03910    return;  
03911 }
03912 
03913 /*!
03914  * \brief Removes a voicemail message file.
03915  * \param dir the path to the message file.
03916  * \param msgnum the unique number for the message within the mailbox.
03917  *
03918  * Removes the message content file and the information file.
03919  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03920  * Typical use is to clean up after a RETRIEVE operation. 
03921  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03922  * \return zero on success, -1 on error.
03923  */
03924 static int remove_file(char *dir, int msgnum)
03925 {
03926    char fn[PATH_MAX];
03927    char full_fn[PATH_MAX];
03928    char msgnums[80];
03929    
03930    if (msgnum > -1) {
03931       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03932       make_file(fn, sizeof(fn), dir, msgnum);
03933    } else
03934       ast_copy_string(fn, dir, sizeof(fn));
03935    ast_filedelete(fn, NULL);  
03936    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03937    unlink(full_fn);
03938    return 0;
03939 }
03940 #else
03941 #ifndef IMAP_STORAGE
03942 /*!
03943  * \brief Find all .txt files - even if they are not in sequence from 0000.
03944  * \param vmu
03945  * \param dir
03946  *
03947  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03948  *
03949  * \return the count of messages, zero or more.
03950  */
03951 static int count_messages(struct ast_vm_user *vmu, char *dir)
03952 {
03953 
03954    int vmcount = 0;
03955    DIR *vmdir = NULL;
03956    struct dirent *vment = NULL;
03957 
03958    if (vm_lock_path(dir))
03959       return ERROR_LOCK_PATH;
03960 
03961    if ((vmdir = opendir(dir))) {
03962       while ((vment = readdir(vmdir))) {
03963          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03964             vmcount++;
03965          }
03966       }
03967       closedir(vmdir);
03968    }
03969    ast_unlock_path(dir);
03970    
03971    return vmcount;
03972 }
03973 
03974 /*!
03975  * \brief Renames a message in a mailbox folder.
03976  * \param sfn The path to the mailbox information and data file to be renamed.
03977  * \param dfn The path for where the message data and information files will be renamed to.
03978  *
03979  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03980  */
03981 static void rename_file(char *sfn, char *dfn)
03982 {
03983    char stxt[PATH_MAX];
03984    char dtxt[PATH_MAX];
03985    ast_filerename(sfn, dfn, NULL);
03986    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03987    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03988    if (ast_check_realtime("voicemail_data")) {
03989       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03990    }
03991    rename(stxt, dtxt);
03992 }
03993 
03994 /*! 
03995  * \brief Determines the highest message number in use for a given user and mailbox folder.
03996  * \param vmu 
03997  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03998  *
03999  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04000  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04001  *
04002  * \note Should always be called with a lock already set on dir.
04003  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04004  */
04005 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04006 {
04007    int x;
04008    unsigned char map[MAXMSGLIMIT] = "";
04009    DIR *msgdir;
04010    struct dirent *msgdirent;
04011    int msgdirint;
04012    char extension[4];
04013    int stopcount = 0;
04014 
04015    /* Reading the entire directory into a file map scales better than
04016     * doing a stat repeatedly on a predicted sequence.  I suspect this
04017     * is partially due to stat(2) internally doing a readdir(2) itself to
04018     * find each file. */
04019    if (!(msgdir = opendir(dir))) {
04020       return -1;
04021    }
04022 
04023    while ((msgdirent = readdir(msgdir))) {
04024       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04025          map[msgdirint] = 1;
04026          stopcount++;
04027          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04028       }
04029    }
04030    closedir(msgdir);
04031 
04032    for (x = 0; x < vmu->maxmsg; x++) {
04033       if (map[x] == 1) {
04034          stopcount--;
04035       } else if (map[x] == 0 && !stopcount) {
04036          break;
04037       }
04038    }
04039 
04040    return x - 1;
04041 }
04042 
04043 #endif /* #ifndef IMAP_STORAGE */
04044 #endif /* #else of #ifdef ODBC_STORAGE */
04045 #ifndef IMAP_STORAGE
04046 /*!
04047  * \brief Utility function to copy a file.
04048  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04049  * \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.
04050  *
04051  * 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.
04052  * The copy operation copies up to 4096 bytes at once.
04053  *
04054  * \return zero on success, -1 on error.
04055  */
04056 static int copy(char *infile, char *outfile)
04057 {
04058    int ifd;
04059    int ofd;
04060    int res;
04061    int len;
04062    char buf[4096];
04063 
04064 #ifdef HARDLINK_WHEN_POSSIBLE
04065    /* Hard link if possible; saves disk space & is faster */
04066    if (link(infile, outfile)) {
04067 #endif
04068       if ((ifd = open(infile, O_RDONLY)) < 0) {
04069          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04070          return -1;
04071       }
04072       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04073          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04074          close(ifd);
04075          return -1;
04076       }
04077       do {
04078          len = read(ifd, buf, sizeof(buf));
04079          if (len < 0) {
04080             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04081             close(ifd);
04082             close(ofd);
04083             unlink(outfile);
04084          } else if (len) {
04085             res = write(ofd, buf, len);
04086             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04087                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04088                close(ifd);
04089                close(ofd);
04090                unlink(outfile);
04091             }
04092          }
04093       } while (len);
04094       close(ifd);
04095       close(ofd);
04096       return 0;
04097 #ifdef HARDLINK_WHEN_POSSIBLE
04098    } else {
04099       /* Hard link succeeded */
04100       return 0;
04101    }
04102 #endif
04103 }
04104 
04105 /*!
04106  * \brief Copies a voicemail information (envelope) file.
04107  * \param frompath
04108  * \param topath 
04109  *
04110  * Every voicemail has the data (.wav) file, and the information file.
04111  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04112  * This is used by the COPY macro when not using IMAP storage.
04113  */
04114 static void copy_plain_file(char *frompath, char *topath)
04115 {
04116    char frompath2[PATH_MAX], topath2[PATH_MAX];
04117    struct ast_variable *tmp,*var = NULL;
04118    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04119    ast_filecopy(frompath, topath, NULL);
04120    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04121    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04122    if (ast_check_realtime("voicemail_data")) {
04123       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04124       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04125       for (tmp = var; tmp; tmp = tmp->next) {
04126          if (!strcasecmp(tmp->name, "origmailbox")) {
04127             origmailbox = tmp->value;
04128          } else if (!strcasecmp(tmp->name, "context")) {
04129             context = tmp->value;
04130          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04131             macrocontext = tmp->value;
04132          } else if (!strcasecmp(tmp->name, "exten")) {
04133             exten = tmp->value;
04134          } else if (!strcasecmp(tmp->name, "priority")) {
04135             priority = tmp->value;
04136          } else if (!strcasecmp(tmp->name, "callerchan")) {
04137             callerchan = tmp->value;
04138          } else if (!strcasecmp(tmp->name, "callerid")) {
04139             callerid = tmp->value;
04140          } else if (!strcasecmp(tmp->name, "origdate")) {
04141             origdate = tmp->value;
04142          } else if (!strcasecmp(tmp->name, "origtime")) {
04143             origtime = tmp->value;
04144          } else if (!strcasecmp(tmp->name, "category")) {
04145             category = tmp->value;
04146          } else if (!strcasecmp(tmp->name, "duration")) {
04147             duration = tmp->value;
04148          }
04149       }
04150       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);
04151    }
04152    copy(frompath2, topath2);
04153    ast_variables_destroy(var);
04154 }
04155 #endif
04156 
04157 /*! 
04158  * \brief Removes the voicemail sound and information file.
04159  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04160  *
04161  * This is used by the DELETE macro when voicemails are stored on the file system.
04162  *
04163  * \return zero on success, -1 on error.
04164  */
04165 static int vm_delete(char *file)
04166 {
04167    char *txt;
04168    int txtsize = 0;
04169 
04170    txtsize = (strlen(file) + 5)*sizeof(char);
04171    txt = alloca(txtsize);
04172    /* Sprintf here would safe because we alloca'd exactly the right length,
04173     * but trying to eliminate all sprintf's anyhow
04174     */
04175    if (ast_check_realtime("voicemail_data")) {
04176       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04177    }
04178    snprintf(txt, txtsize, "%s.txt", file);
04179    unlink(txt);
04180    return ast_filedelete(file, NULL);
04181 }
04182 
04183 /*!
04184  * \brief utility used by inchar(), for base_encode()
04185  */
04186 static int inbuf(struct baseio *bio, FILE *fi)
04187 {
04188    int l;
04189 
04190    if (bio->ateof)
04191       return 0;
04192 
04193    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04194       if (ferror(fi))
04195          return -1;
04196 
04197       bio->ateof = 1;
04198       return 0;
04199    }
04200 
04201    bio->iolen = l;
04202    bio->iocp = 0;
04203 
04204    return 1;
04205 }
04206 
04207 /*!
04208  * \brief utility used by base_encode()
04209  */
04210 static int inchar(struct baseio *bio, FILE *fi)
04211 {
04212    if (bio->iocp>=bio->iolen) {
04213       if (!inbuf(bio, fi))
04214          return EOF;
04215    }
04216 
04217    return bio->iobuf[bio->iocp++];
04218 }
04219 
04220 /*!
04221  * \brief utility used by base_encode()
04222  */
04223 static int ochar(struct baseio *bio, int c, FILE *so)
04224 {
04225    if (bio->linelength >= BASELINELEN) {
04226       if (fputs(ENDL, so) == EOF) {
04227          return -1;
04228       }
04229 
04230       bio->linelength = 0;
04231    }
04232 
04233    if (putc(((unsigned char) c), so) == EOF) {
04234       return -1;
04235    }
04236 
04237    bio->linelength++;
04238 
04239    return 1;
04240 }
04241 
04242 /*!
04243  * \brief Performs a base 64 encode algorithm on the contents of a File
04244  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04245  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04246  *
04247  * 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 ?
04248  *
04249  * \return zero on success, -1 on error.
04250  */
04251 static int base_encode(char *filename, FILE *so)
04252 {
04253    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04254       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04255       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04256       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04257    int i, hiteof = 0;
04258    FILE *fi;
04259    struct baseio bio;
04260 
04261    memset(&bio, 0, sizeof(bio));
04262    bio.iocp = BASEMAXINLINE;
04263 
04264    if (!(fi = fopen(filename, "rb"))) {
04265       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04266       return -1;
04267    }
04268 
04269    while (!hiteof){
04270       unsigned char igroup[3], ogroup[4];
04271       int c, n;
04272 
04273       memset(igroup, 0, sizeof(igroup));
04274 
04275       for (n = 0; n < 3; n++) {
04276          if ((c = inchar(&bio, fi)) == EOF) {
04277             hiteof = 1;
04278             break;
04279          }
04280 
04281          igroup[n] = (unsigned char) c;
04282       }
04283 
04284       if (n > 0) {
04285          ogroup[0]= dtable[igroup[0] >> 2];
04286          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04287          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04288          ogroup[3]= dtable[igroup[2] & 0x3F];
04289 
04290          if (n < 3) {
04291             ogroup[3] = '=';
04292 
04293             if (n < 2)
04294                ogroup[2] = '=';
04295          }
04296 
04297          for (i = 0; i < 4; i++)
04298             ochar(&bio, ogroup[i], so);
04299       }
04300    }
04301 
04302    fclose(fi);
04303    
04304    if (fputs(ENDL, so) == EOF) {
04305       return 0;
04306    }
04307 
04308    return 1;
04309 }
04310 
04311 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)
04312 {
04313    char callerid[256];
04314    char num[12];
04315    char fromdir[256], fromfile[256];
04316    struct ast_config *msg_cfg;
04317    const char *origcallerid, *origtime;
04318    char origcidname[80], origcidnum[80], origdate[80];
04319    int inttime;
04320    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04321 
04322    /* Prepare variables for substitution in email body and subject */
04323    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04324    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04325    snprintf(num, sizeof(num), "%d", msgnum);
04326    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04327    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04328    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04329    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04330       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04331    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04332    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04333    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04334    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04335    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04336 
04337    /* Retrieve info from VM attribute file */
04338    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04339    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04340    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04341       strcat(fromfile, ".txt");
04342    }
04343    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04344       if (option_debug > 0) {
04345          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04346       }
04347       return;
04348    }
04349 
04350    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04351       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04352       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04353       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04354       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04355    }
04356 
04357    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04358       struct timeval tv = { inttime, };
04359       struct ast_tm tm;
04360       ast_localtime(&tv, &tm, NULL);
04361       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04362       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04363    }
04364    ast_config_destroy(msg_cfg);
04365 }
04366 
04367 /*!
04368  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04369  * \param from The string to work with.
04370  * \param buf The buffer into which to write the modified quoted string.
04371  * \param maxlen Always zero, but see \see ast_str
04372  * 
04373  * \return The destination string with quotes wrapped on it (the to field).
04374  */
04375 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04376 {
04377    const char *ptr;
04378 
04379    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04380    ast_str_set(buf, maxlen, "\"");
04381    for (ptr = from; *ptr; ptr++) {
04382       if (*ptr == '"' || *ptr == '\\') {
04383          ast_str_append(buf, maxlen, "\\%c", *ptr);
04384       } else {
04385          ast_str_append(buf, maxlen, "%c", *ptr);
04386       }
04387    }
04388    ast_str_append(buf, maxlen, "\"");
04389 
04390    return ast_str_buffer(*buf);
04391 }
04392 
04393 /*! \brief
04394  * fill in *tm for current time according to the proper timezone, if any.
04395  * \return tm so it can be used as a function argument.
04396  */
04397 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04398 {
04399    const struct vm_zone *z = NULL;
04400    struct timeval t = ast_tvnow();
04401 
04402    /* Does this user have a timezone specified? */
04403    if (!ast_strlen_zero(vmu->zonetag)) {
04404       /* Find the zone in the list */
04405       AST_LIST_LOCK(&zones);
04406       AST_LIST_TRAVERSE(&zones, z, list) {
04407          if (!strcmp(z->name, vmu->zonetag))
04408             break;
04409       }
04410       AST_LIST_UNLOCK(&zones);
04411    }
04412    ast_localtime(&t, tm, z ? z->timezone : NULL);
04413    return tm;
04414 }
04415 
04416 /*!\brief Check if the string would need encoding within the MIME standard, to
04417  * avoid confusing certain mail software that expects messages to be 7-bit
04418  * clean.
04419  */
04420 static int check_mime(const char *str)
04421 {
04422    for (; *str; str++) {
04423       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04424          return 1;
04425       }
04426    }
04427    return 0;
04428 }
04429 
04430 /*!\brief Encode a string according to the MIME rules for encoding strings
04431  * that are not 7-bit clean or contain control characters.
04432  *
04433  * Additionally, if the encoded string would exceed the MIME limit of 76
04434  * characters per line, then the encoding will be broken up into multiple
04435  * sections, separated by a space character, in order to facilitate
04436  * breaking up the associated header across multiple lines.
04437  *
04438  * \param end An expandable buffer for holding the result
04439  * \param maxlen Always zero, but see \see ast_str
04440  * \param start A string to be encoded
04441  * \param preamble The length of the first line already used for this string,
04442  * to ensure that each line maintains a maximum length of 76 chars.
04443  * \param postamble the length of any additional characters appended to the
04444  * line, used to ensure proper field wrapping.
04445  * \retval The encoded string.
04446  */
04447 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04448 {
04449    struct ast_str *tmp = ast_str_alloca(80);
04450    int first_section = 1;
04451 
04452    ast_str_reset(*end);
04453    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04454    for (; *start; start++) {
04455       int need_encoding = 0;
04456       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04457          need_encoding = 1;
04458       }
04459       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04460          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04461          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04462          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04463          /* Start new line */
04464          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04465          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04466          first_section = 0;
04467       }
04468       if (need_encoding && *start == ' ') {
04469          ast_str_append(&tmp, -1, "_");
04470       } else if (need_encoding) {
04471          ast_str_append(&tmp, -1, "=%hhX", *start);
04472       } else {
04473          ast_str_append(&tmp, -1, "%c", *start);
04474       }
04475    }
04476    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04477    return ast_str_buffer(*end);
04478 }
04479 
04480 /*!
04481  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04482  * \param p The output file to generate the email contents into.
04483  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04484  * \param vmu The voicemail user who is sending the voicemail.
04485  * \param msgnum The message index in the mailbox folder.
04486  * \param context 
04487  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04488  * \param fromfolder
04489  * \param cidnum The caller ID number.
04490  * \param cidname The caller ID name.
04491  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04492  * \param attach2 
04493  * \param format The message sound file format. i.e. .wav
04494  * \param duration The time of the message content, in seconds.
04495  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04496  * \param chan
04497  * \param category
04498  * \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.
04499  * \param flag
04500  *
04501  * 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.
04502  */
04503 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)
04504 {
04505    char date[256];
04506    char host[MAXHOSTNAMELEN] = "";
04507    char who[256];
04508    char bound[256];
04509    char dur[256];
04510    struct ast_tm tm;
04511    char enc_cidnum[256] = "", enc_cidname[256] = "";
04512    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04513    char *greeting_attachment; 
04514    char filename[256];
04515 
04516    if (!str1 || !str2) {
04517       ast_free(str1);
04518       ast_free(str2);
04519       return;
04520    }
04521 
04522    if (cidnum) {
04523       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04524    }
04525    if (cidname) {
04526       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04527    }
04528    gethostname(host, sizeof(host) - 1);
04529 
04530    if (strchr(srcemail, '@')) {
04531       ast_copy_string(who, srcemail, sizeof(who));
04532    } else {
04533       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04534    }
04535 
04536    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04537    if (greeting_attachment) {
04538       *greeting_attachment++ = '\0';
04539    }
04540 
04541    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04542    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04543    fprintf(p, "Date: %s" ENDL, date);
04544 
04545    /* Set date format for voicemail mail */
04546    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04547 
04548    if (!ast_strlen_zero(fromstring)) {
04549       struct ast_channel *ast;
04550       if ((ast = ast_dummy_channel_alloc())) {
04551          char *ptr;
04552          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04553          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04554 
04555          if (check_mime(ast_str_buffer(str1))) {
04556             int first_line = 1;
04557             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04558             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04559                *ptr = '\0';
04560                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04561                first_line = 0;
04562                /* Substring is smaller, so this will never grow */
04563                ast_str_set(&str2, 0, "%s", ptr + 1);
04564             }
04565             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04566          } else {
04567             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04568          }
04569          ast = ast_channel_unref(ast);
04570       } else {
04571          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04572       }
04573    } else {
04574       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04575    }
04576 
04577    if (check_mime(vmu->fullname)) {
04578       int first_line = 1;
04579       char *ptr;
04580       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04581       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04582          *ptr = '\0';
04583          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04584          first_line = 0;
04585          /* Substring is smaller, so this will never grow */
04586          ast_str_set(&str2, 0, "%s", ptr + 1);
04587       }
04588       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04589    } else {
04590       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04591    }
04592 
04593    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04594       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04595       struct ast_channel *ast;
04596       if ((ast = ast_dummy_channel_alloc())) {
04597          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04598          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04599          if (check_mime(ast_str_buffer(str1))) {
04600             int first_line = 1;
04601             char *ptr;
04602             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04603             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04604                *ptr = '\0';
04605                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04606                first_line = 0;
04607                /* Substring is smaller, so this will never grow */
04608                ast_str_set(&str2, 0, "%s", ptr + 1);
04609             }
04610             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04611          } else {
04612             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04613          }
04614          ast = ast_channel_unref(ast);
04615       } else {
04616          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04617       }
04618    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04619       if (ast_strlen_zero(flag)) {
04620          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04621       } else {
04622          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04623       }
04624    } else {
04625       if (ast_strlen_zero(flag)) {
04626          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04627       } else {
04628          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04629       }
04630    }
04631 
04632    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04633       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04634    if (imap) {
04635       /* additional information needed for IMAP searching */
04636       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04637       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04638       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04639       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04640 #ifdef IMAP_STORAGE
04641       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04642 #else
04643       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04644 #endif
04645       /* flag added for Urgent */
04646       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04647       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04648       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04649       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04650       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04651       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04652       if (!ast_strlen_zero(category)) {
04653          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04654       } else {
04655          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04656       }
04657       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04658       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04659       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04660    }
04661    if (!ast_strlen_zero(cidnum)) {
04662       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04663    }
04664    if (!ast_strlen_zero(cidname)) {
04665       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04666    }
04667    fprintf(p, "MIME-Version: 1.0" ENDL);
04668    if (attach_user_voicemail) {
04669       /* Something unique. */
04670       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04671          (int) getpid(), (unsigned int) ast_random());
04672 
04673       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04674       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04675       fprintf(p, "--%s" ENDL, bound);
04676    }
04677    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04678    if (emailbody || vmu->emailbody) {
04679       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04680       struct ast_channel *ast;
04681       if ((ast = ast_dummy_channel_alloc())) {
04682          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04683          ast_str_substitute_variables(&str1, 0, ast, e_body);
04684 #ifdef IMAP_STORAGE
04685             {
04686                /* Convert body to native line terminators for IMAP backend */
04687                char *line = ast_str_buffer(str1), *next;
04688                do {
04689                   /* Terminate line before outputting it to the file */
04690                   if ((next = strchr(line, '\n'))) {
04691                      *next++ = '\0';
04692                   }
04693                   fprintf(p, "%s" ENDL, line);
04694                   line = next;
04695                } while (!ast_strlen_zero(line));
04696             }
04697 #else
04698          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04699 #endif
04700          ast = ast_channel_unref(ast);
04701       } else {
04702          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04703       }
04704    } else if (msgnum > -1) {
04705       if (strcmp(vmu->mailbox, mailbox)) {
04706          /* Forwarded type */
04707          struct ast_config *msg_cfg;
04708          const char *v;
04709          int inttime;
04710          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04711          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04712          /* Retrieve info from VM attribute file */
04713          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04714          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04715          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04716             strcat(fromfile, ".txt");
04717          }
04718          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04719             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04720                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04721             }
04722 
04723             /* You might be tempted to do origdate, except that a) it's in the wrong
04724              * format, and b) it's missing for IMAP recordings. */
04725             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04726                struct timeval tv = { inttime, };
04727                struct ast_tm tm;
04728                ast_localtime(&tv, &tm, NULL);
04729                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04730             }
04731             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04732                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04733                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04734                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04735                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04736                date, origcallerid, origdate);
04737             ast_config_destroy(msg_cfg);
04738          } else {
04739             goto plain_message;
04740          }
04741       } else {
04742 plain_message:
04743          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04744             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04745             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04746             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04747             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04748       }
04749    } else {
04750       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04751             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04752    }
04753 
04754    if (imap || attach_user_voicemail) {
04755       if (!ast_strlen_zero(attach2)) {
04756          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04757          ast_debug(5, "creating second attachment filename %s\n", filename);
04758          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04759          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04760          ast_debug(5, "creating attachment filename %s\n", filename);
04761          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04762       } else {
04763          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04764          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04765          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04766       }
04767    }
04768    ast_free(str1);
04769    ast_free(str2);
04770 }
04771 
04772 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)
04773 {
04774    char tmpdir[256], newtmp[256];
04775    char fname[256];
04776    char tmpcmd[256];
04777    int tmpfd = -1;
04778    int soxstatus = 0;
04779 
04780    /* Eww. We want formats to tell us their own MIME type */
04781    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04782 
04783    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04784       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04785       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04786       tmpfd = mkstemp(newtmp);
04787       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04788       ast_debug(3, "newtmp: %s\n", newtmp);
04789       if (tmpfd > -1) {
04790          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04791          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04792             attach = newtmp;
04793             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04794          } else {
04795             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04796                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04797             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04798          }
04799       }
04800    }
04801    fprintf(p, "--%s" ENDL, bound);
04802    if (msgnum > -1)
04803       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04804    else
04805       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04806    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04807    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04808    if (msgnum > -1)
04809       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04810    else
04811       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04812    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04813    base_encode(fname, p);
04814    if (last)
04815       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04816    if (tmpfd > -1) {
04817       if (soxstatus == 0) {
04818          unlink(fname);
04819       }
04820       close(tmpfd);
04821       unlink(newtmp);
04822    }
04823    return 0;
04824 }
04825 
04826 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)
04827 {
04828    FILE *p = NULL;
04829    char tmp[80] = "/tmp/astmail-XXXXXX";
04830    char tmp2[256];
04831    char *stringp;
04832 
04833    if (vmu && ast_strlen_zero(vmu->email)) {
04834       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04835       return(0);
04836    }
04837 
04838    /* Mail only the first format */
04839    format = ast_strdupa(format);
04840    stringp = format;
04841    strsep(&stringp, "|");
04842 
04843    if (!strcmp(format, "wav49"))
04844       format = "WAV";
04845    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));
04846    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04847       command hangs */
04848    if ((p = vm_mkftemp(tmp)) == NULL) {
04849       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04850       return -1;
04851    } else {
04852       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04853       fclose(p);
04854       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04855       ast_safe_system(tmp2);
04856       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04857    }
04858    return 0;
04859 }
04860 
04861 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)
04862 {
04863    char enc_cidnum[256], enc_cidname[256];
04864    char date[256];
04865    char host[MAXHOSTNAMELEN] = "";
04866    char who[256];
04867    char dur[PATH_MAX];
04868    char tmp[80] = "/tmp/astmail-XXXXXX";
04869    char tmp2[PATH_MAX];
04870    struct ast_tm tm;
04871    FILE *p;
04872    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04873 
04874    if (!str1 || !str2) {
04875       ast_free(str1);
04876       ast_free(str2);
04877       return -1;
04878    }
04879 
04880    if (cidnum) {
04881       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04882    }
04883    if (cidname) {
04884       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04885    }
04886 
04887    if ((p = vm_mkftemp(tmp)) == NULL) {
04888       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04889       ast_free(str1);
04890       ast_free(str2);
04891       return -1;
04892    }
04893    gethostname(host, sizeof(host)-1);
04894    if (strchr(srcemail, '@')) {
04895       ast_copy_string(who, srcemail, sizeof(who));
04896    } else {
04897       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04898    }
04899    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04900    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04901    fprintf(p, "Date: %s\n", date);
04902 
04903    /* Reformat for custom pager format */
04904    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04905 
04906    if (!ast_strlen_zero(pagerfromstring)) {
04907       struct ast_channel *ast;
04908       if ((ast = ast_dummy_channel_alloc())) {
04909          char *ptr;
04910          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04911          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04912 
04913          if (check_mime(ast_str_buffer(str1))) {
04914             int first_line = 1;
04915             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04916             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04917                *ptr = '\0';
04918                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04919                first_line = 0;
04920                /* Substring is smaller, so this will never grow */
04921                ast_str_set(&str2, 0, "%s", ptr + 1);
04922             }
04923             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04924          } else {
04925             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04926          }
04927          ast = ast_channel_unref(ast);
04928       } else {
04929          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04930       }
04931    } else {
04932       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04933    }
04934 
04935    if (check_mime(vmu->fullname)) {
04936       int first_line = 1;
04937       char *ptr;
04938       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04939       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04940          *ptr = '\0';
04941          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04942          first_line = 0;
04943          /* Substring is smaller, so this will never grow */
04944          ast_str_set(&str2, 0, "%s", ptr + 1);
04945       }
04946       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04947    } else {
04948       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04949    }
04950 
04951    if (!ast_strlen_zero(pagersubject)) {
04952       struct ast_channel *ast;
04953       if ((ast = ast_dummy_channel_alloc())) {
04954          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04955          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04956          if (check_mime(ast_str_buffer(str1))) {
04957             int first_line = 1;
04958             char *ptr;
04959             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04960             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04961                *ptr = '\0';
04962                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04963                first_line = 0;
04964                /* Substring is smaller, so this will never grow */
04965                ast_str_set(&str2, 0, "%s", ptr + 1);
04966             }
04967             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04968          } else {
04969             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04970          }
04971          ast = ast_channel_unref(ast);
04972       } else {
04973          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04974       }
04975    } else {
04976       if (ast_strlen_zero(flag)) {
04977          fprintf(p, "Subject: New VM\n\n");
04978       } else {
04979          fprintf(p, "Subject: New %s VM\n\n", flag);
04980       }
04981    }
04982 
04983    if (pagerbody) {
04984       struct ast_channel *ast;
04985       if ((ast = ast_dummy_channel_alloc())) {
04986          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04987          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04988          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04989          ast = ast_channel_unref(ast);
04990       } else {
04991          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04992       }
04993    } else {
04994       fprintf(p, "New %s long %s msg in box %s\n"
04995             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04996    }
04997 
04998    fclose(p);
04999    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05000    ast_safe_system(tmp2);
05001    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05002    ast_free(str1);
05003    ast_free(str2);
05004    return 0;
05005 }
05006 
05007 /*!
05008  * \brief Gets the current date and time, as formatted string.
05009  * \param s The buffer to hold the output formatted date.
05010  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05011  * 
05012  * The date format string used is "%a %b %e %r UTC %Y".
05013  * 
05014  * \return zero on success, -1 on error.
05015  */
05016 static int get_date(char *s, int len)
05017 {
05018    struct ast_tm tm;
05019    struct timeval t = ast_tvnow();
05020    
05021    ast_localtime(&t, &tm, "UTC");
05022 
05023    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05024 }
05025 
05026 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05027 {
05028    int res;
05029    char fn[PATH_MAX];
05030    char dest[PATH_MAX];
05031 
05032    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05033 
05034    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05035       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05036       return -1;
05037    }
05038 
05039    RETRIEVE(fn, -1, ext, context);
05040    if (ast_fileexists(fn, NULL, NULL) > 0) {
05041       res = ast_stream_and_wait(chan, fn, ecodes);
05042       if (res) {
05043          DISPOSE(fn, -1);
05044          return res;
05045       }
05046    } else {
05047       /* Dispose just in case */
05048       DISPOSE(fn, -1);
05049       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05050       if (res)
05051          return res;
05052       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05053       if (res)
05054          return res;
05055    }
05056    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05057    return res;
05058 }
05059 
05060 static void free_zone(struct vm_zone *z)
05061 {
05062    ast_free(z);
05063 }
05064 
05065 #ifdef ODBC_STORAGE
05066 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05067 {
05068    int x = -1;
05069    int res;
05070    SQLHSTMT stmt = NULL;
05071    char sql[PATH_MAX];
05072    char rowdata[20];
05073    char tmp[PATH_MAX] = "";
05074    struct odbc_obj *obj = NULL;
05075    char *context;
05076    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05077 
05078    if (newmsgs)
05079       *newmsgs = 0;
05080    if (oldmsgs)
05081       *oldmsgs = 0;
05082    if (urgentmsgs)
05083       *urgentmsgs = 0;
05084 
05085    /* If no mailbox, return immediately */
05086    if (ast_strlen_zero(mailbox))
05087       return 0;
05088 
05089    ast_copy_string(tmp, mailbox, sizeof(tmp));
05090 
05091    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05092       int u, n, o;
05093       char *next, *remaining = tmp;
05094       while ((next = strsep(&remaining, " ,"))) {
05095          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05096             return -1;
05097          }
05098          if (urgentmsgs) {
05099             *urgentmsgs += u;
05100          }
05101          if (newmsgs) {
05102             *newmsgs += n;
05103          }
05104          if (oldmsgs) {
05105             *oldmsgs += o;
05106          }
05107       }
05108       return 0;
05109    }
05110 
05111    context = strchr(tmp, '@');
05112    if (context) {
05113       *context = '\0';
05114       context++;
05115    } else
05116       context = "default";
05117 
05118    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05119       do {
05120          if (newmsgs) {
05121             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05122             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05123                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05124                break;
05125             }
05126             res = SQLFetch(stmt);
05127             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05128                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05129                break;
05130             }
05131             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05132             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05133                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05134                break;
05135             }
05136             *newmsgs = atoi(rowdata);
05137             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05138          }
05139 
05140          if (oldmsgs) {
05141             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05142             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05143                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05144                break;
05145             }
05146             res = SQLFetch(stmt);
05147             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05148                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05149                break;
05150             }
05151             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05152             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05153                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05154                break;
05155             }
05156             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05157             *oldmsgs = atoi(rowdata);
05158          }
05159 
05160          if (urgentmsgs) {
05161             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05162             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05163                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05164                break;
05165             }
05166             res = SQLFetch(stmt);
05167             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05168                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05169                break;
05170             }
05171             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05172             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05173                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05174                break;
05175             }
05176             *urgentmsgs = atoi(rowdata);
05177          }
05178 
05179          x = 0;
05180       } while (0);
05181    } else {
05182       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05183    }
05184 
05185    if (stmt) {
05186       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05187    }
05188    if (obj) {
05189       ast_odbc_release_obj(obj);
05190    }
05191    return x;
05192 }
05193 
05194 /*!
05195  * \brief Gets the number of messages that exist in a mailbox folder.
05196  * \param context
05197  * \param mailbox
05198  * \param folder
05199  * 
05200  * This method is used when ODBC backend is used.
05201  * \return The number of messages in this mailbox folder (zero or more).
05202  */
05203 static int messagecount(const char *context, const char *mailbox, const char *folder)
05204 {
05205    struct odbc_obj *obj = NULL;
05206    int nummsgs = 0;
05207    int res;
05208    SQLHSTMT stmt = NULL;
05209    char sql[PATH_MAX];
05210    char rowdata[20];
05211    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05212    if (!folder)
05213       folder = "INBOX";
05214    /* If no mailbox, return immediately */
05215    if (ast_strlen_zero(mailbox))
05216       return 0;
05217 
05218    obj = ast_odbc_request_obj(odbc_database, 0);
05219    if (obj) {
05220       if (!strcmp(folder, "INBOX")) {
05221          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);
05222       } else {
05223          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05224       }
05225       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05226       if (!stmt) {
05227          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05228          goto yuck;
05229       }
05230       res = SQLFetch(stmt);
05231       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05232          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05233          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05234          goto yuck;
05235       }
05236       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05237       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05238          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05239          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05240          goto yuck;
05241       }
05242       nummsgs = atoi(rowdata);
05243       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05244    } else
05245       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05246 
05247 yuck:
05248    if (obj)
05249       ast_odbc_release_obj(obj);
05250    return nummsgs;
05251 }
05252 
05253 /** 
05254  * \brief Determines if the given folder has messages.
05255  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05256  * 
05257  * This function is used when the mailbox is stored in an ODBC back end.
05258  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05259  * \return 1 if the folder has one or more messages. zero otherwise.
05260  */
05261 static int has_voicemail(const char *mailbox, const char *folder)
05262 {
05263    char tmp[256], *tmp2 = tmp, *box, *context;
05264    ast_copy_string(tmp, mailbox, sizeof(tmp));
05265    while ((context = box = strsep(&tmp2, ",&"))) {
05266       strsep(&context, "@");
05267       if (ast_strlen_zero(context))
05268          context = "default";
05269       if (messagecount(context, box, folder))
05270          return 1;
05271    }
05272    return 0;
05273 }
05274 #endif
05275 #ifndef IMAP_STORAGE
05276 /*! 
05277  * \brief Copies a message from one mailbox to another.
05278  * \param chan
05279  * \param vmu
05280  * \param imbox
05281  * \param msgnum
05282  * \param duration
05283  * \param recip
05284  * \param fmt
05285  * \param dir
05286  * \param flag
05287  *
05288  * This is only used by file storage based mailboxes.
05289  *
05290  * \return zero on success, -1 on error.
05291  */
05292 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)
05293 {
05294    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05295    const char *frombox = mbox(vmu, imbox);
05296    const char *userfolder;
05297    int recipmsgnum;
05298    int res = 0;
05299 
05300    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05301 
05302    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05303       userfolder = "Urgent";
05304    } else {
05305       userfolder = "INBOX";
05306    }
05307 
05308    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05309 
05310    if (!dir)
05311       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05312    else
05313       ast_copy_string(fromdir, dir, sizeof(fromdir));
05314 
05315    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05316    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05317 
05318    if (vm_lock_path(todir))
05319       return ERROR_LOCK_PATH;
05320 
05321    recipmsgnum = last_message_index(recip, todir) + 1;
05322    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05323       make_file(topath, sizeof(topath), todir, recipmsgnum);
05324 #ifndef ODBC_STORAGE
05325       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05326          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05327       } else {
05328 #endif
05329          /* If we are prepending a message for ODBC, then the message already
05330           * exists in the database, but we want to force copying from the
05331           * filesystem (since only the FS contains the prepend). */
05332          copy_plain_file(frompath, topath);
05333          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05334          vm_delete(topath);
05335 #ifndef ODBC_STORAGE
05336       }
05337 #endif
05338    } else {
05339       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05340       res = -1;
05341    }
05342    ast_unlock_path(todir);
05343    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05344       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05345       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05346       flag);
05347    
05348    return res;
05349 }
05350 #endif
05351 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05352 
05353 static int messagecount(const char *context, const char *mailbox, const char *folder)
05354 {
05355    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05356 }
05357 
05358 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05359 {
05360    DIR *dir;
05361    struct dirent *de;
05362    char fn[256];
05363    int ret = 0;
05364 
05365    /* If no mailbox, return immediately */
05366    if (ast_strlen_zero(mailbox))
05367       return 0;
05368 
05369    if (ast_strlen_zero(folder))
05370       folder = "INBOX";
05371    if (ast_strlen_zero(context))
05372       context = "default";
05373 
05374    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05375 
05376    if (!(dir = opendir(fn)))
05377       return 0;
05378 
05379    while ((de = readdir(dir))) {
05380       if (!strncasecmp(de->d_name, "msg", 3)) {
05381          if (shortcircuit) {
05382             ret = 1;
05383             break;
05384          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05385             ret++;
05386          }
05387       }
05388    }
05389 
05390    closedir(dir);
05391 
05392    return ret;
05393 }
05394 
05395 /** 
05396  * \brief Determines if the given folder has messages.
05397  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05398  * \param folder the folder to look in
05399  *
05400  * This function is used when the mailbox is stored in a filesystem back end.
05401  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05402  * \return 1 if the folder has one or more messages. zero otherwise.
05403  */
05404 static int has_voicemail(const char *mailbox, const char *folder)
05405 {
05406    char tmp[256], *tmp2 = tmp, *box, *context;
05407    ast_copy_string(tmp, mailbox, sizeof(tmp));
05408    if (ast_strlen_zero(folder)) {
05409       folder = "INBOX";
05410    }
05411    while ((box = strsep(&tmp2, ",&"))) {
05412       if ((context = strchr(box, '@')))
05413          *context++ = '\0';
05414       else
05415          context = "default";
05416       if (__has_voicemail(context, box, folder, 1))
05417          return 1;
05418       /* If we are checking INBOX, we should check Urgent as well */
05419       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05420          return 1;
05421       }
05422    }
05423    return 0;
05424 }
05425 
05426 
05427 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05428 {
05429    char tmp[256];
05430    char *context;
05431 
05432    /* If no mailbox, return immediately */
05433    if (ast_strlen_zero(mailbox))
05434       return 0;
05435 
05436    if (newmsgs)
05437       *newmsgs = 0;
05438    if (oldmsgs)
05439       *oldmsgs = 0;
05440    if (urgentmsgs)
05441       *urgentmsgs = 0;
05442 
05443    if (strchr(mailbox, ',')) {
05444       int tmpnew, tmpold, tmpurgent;
05445       char *mb, *cur;
05446 
05447       ast_copy_string(tmp, mailbox, sizeof(tmp));
05448       mb = tmp;
05449       while ((cur = strsep(&mb, ", "))) {
05450          if (!ast_strlen_zero(cur)) {
05451             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05452                return -1;
05453             else {
05454                if (newmsgs)
05455                   *newmsgs += tmpnew; 
05456                if (oldmsgs)
05457                   *oldmsgs += tmpold;
05458                if (urgentmsgs)
05459                   *urgentmsgs += tmpurgent;
05460             }
05461          }
05462       }
05463       return 0;
05464    }
05465 
05466    ast_copy_string(tmp, mailbox, sizeof(tmp));
05467    
05468    if ((context = strchr(tmp, '@')))
05469       *context++ = '\0';
05470    else
05471       context = "default";
05472 
05473    if (newmsgs)
05474       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05475    if (oldmsgs)
05476       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05477    if (urgentmsgs)
05478       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05479 
05480    return 0;
05481 }
05482 
05483 #endif
05484 
05485 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05486 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05487 {
05488    int urgentmsgs = 0;
05489    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05490    if (newmsgs) {
05491       *newmsgs += urgentmsgs;
05492    }
05493    return res;
05494 }
05495 
05496 static void run_externnotify(char *context, char *extension, const char *flag)
05497 {
05498    char arguments[255];
05499    char ext_context[256] = "";
05500    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05501    struct ast_smdi_mwi_message *mwi_msg;
05502 
05503    if (!ast_strlen_zero(context))
05504       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05505    else
05506       ast_copy_string(ext_context, extension, sizeof(ext_context));
05507 
05508    if (smdi_iface) {
05509       if (ast_app_has_voicemail(ext_context, NULL)) 
05510          ast_smdi_mwi_set(smdi_iface, extension);
05511       else
05512          ast_smdi_mwi_unset(smdi_iface, extension);
05513 
05514       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05515          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05516          if (!strncmp(mwi_msg->cause, "INV", 3))
05517             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05518          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05519             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05520          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05521          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05522       } else {
05523          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05524       }
05525    }
05526 
05527    if (!ast_strlen_zero(externnotify)) {
05528       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05529          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05530       } else {
05531          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05532          ast_debug(1, "Executing %s\n", arguments);
05533          ast_safe_system(arguments);
05534       }
05535    }
05536 }
05537 
05538 /*!
05539  * \brief Variables used for saving a voicemail.
05540  *
05541  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05542  */
05543 struct leave_vm_options {
05544    unsigned int flags;
05545    signed char record_gain;
05546    char *exitcontext;
05547 };
05548 
05549 /*!
05550  * \brief Prompts the user and records a voicemail to a mailbox.
05551  * \param chan
05552  * \param ext
05553  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05554  * 
05555  * 
05556  * 
05557  * \return zero on success, -1 on error.
05558  */
05559 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05560 {
05561 #ifdef IMAP_STORAGE
05562    int newmsgs, oldmsgs;
05563 #else
05564    char urgdir[PATH_MAX];
05565 #endif
05566    char txtfile[PATH_MAX];
05567    char tmptxtfile[PATH_MAX];
05568    struct vm_state *vms = NULL;
05569    char callerid[256];
05570    FILE *txt;
05571    char date[256];
05572    int txtdes;
05573    int res = 0;
05574    int msgnum;
05575    int duration = 0;
05576    int sound_duration = 0;
05577    int ausemacro = 0;
05578    int ousemacro = 0;
05579    int ouseexten = 0;
05580    char tmpdur[16];
05581    char priority[16];
05582    char origtime[16];
05583    char dir[PATH_MAX];
05584    char tmpdir[PATH_MAX];
05585    char fn[PATH_MAX];
05586    char prefile[PATH_MAX] = "";
05587    char tempfile[PATH_MAX] = "";
05588    char ext_context[256] = "";
05589    char fmt[80];
05590    char *context;
05591    char ecodes[17] = "#";
05592    struct ast_str *tmp = ast_str_create(16);
05593    char *tmpptr;
05594    struct ast_vm_user *vmu;
05595    struct ast_vm_user svm;
05596    const char *category = NULL;
05597    const char *code;
05598    const char *alldtmf = "0123456789ABCD*#";
05599    char flag[80];
05600 
05601    if (!tmp) {
05602       return -1;
05603    }
05604 
05605    ast_str_set(&tmp, 0, "%s", ext);
05606    ext = ast_str_buffer(tmp);
05607    if ((context = strchr(ext, '@'))) {
05608       *context++ = '\0';
05609       tmpptr = strchr(context, '&');
05610    } else {
05611       tmpptr = strchr(ext, '&');
05612    }
05613 
05614    if (tmpptr)
05615       *tmpptr++ = '\0';
05616 
05617    ast_channel_lock(chan);
05618    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05619       category = ast_strdupa(category);
05620    }
05621    ast_channel_unlock(chan);
05622 
05623    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05624       ast_copy_string(flag, "Urgent", sizeof(flag));
05625    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05626       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05627    } else {
05628       flag[0] = '\0';
05629    }
05630 
05631    ast_debug(3, "Before find_user\n");
05632    if (!(vmu = find_user(&svm, context, ext))) {
05633       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05634       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05635       ast_free(tmp);
05636       return res;
05637    }
05638    /* Setup pre-file if appropriate */
05639    if (strcmp(vmu->context, "default"))
05640       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05641    else
05642       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05643 
05644    /* Set the path to the prefile. Will be one of 
05645       VM_SPOOL_DIRcontext/ext/busy
05646       VM_SPOOL_DIRcontext/ext/unavail
05647       Depending on the flag set in options.
05648    */
05649    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05650       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05651    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05652       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05653    }
05654    /* Set the path to the tmpfile as
05655       VM_SPOOL_DIR/context/ext/temp
05656       and attempt to create the folder structure.
05657    */
05658    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05659    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05660       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05661       ast_free(tmp);
05662       return -1;
05663    }
05664    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05665    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05666       ast_copy_string(prefile, tempfile, sizeof(prefile));
05667 
05668    DISPOSE(tempfile, -1);
05669    /* It's easier just to try to make it than to check for its existence */
05670 #ifndef IMAP_STORAGE
05671    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05672 #else
05673    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05674    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05675       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05676    }
05677 #endif
05678 
05679    /* Check current or macro-calling context for special extensions */
05680    if (ast_test_flag(vmu, VM_OPERATOR)) {
05681       if (!ast_strlen_zero(vmu->exit)) {
05682          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05683             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05684             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05685             ouseexten = 1;
05686          }
05687       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05688          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05689          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05690          ouseexten = 1;
05691       } else if (!ast_strlen_zero(chan->macrocontext)
05692          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05693             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05694          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05695          ousemacro = 1;
05696       }
05697    }
05698 
05699    if (!ast_strlen_zero(vmu->exit)) {
05700       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05701          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05702          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05703       }
05704    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05705       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05706       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05707    } else if (!ast_strlen_zero(chan->macrocontext)
05708       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05709          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05710       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05711       ausemacro = 1;
05712    }
05713 
05714    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05715       for (code = alldtmf; *code; code++) {
05716          char e[2] = "";
05717          e[0] = *code;
05718          if (strchr(ecodes, e[0]) == NULL
05719             && ast_canmatch_extension(chan,
05720                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05721                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05722             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05723          }
05724       }
05725    }
05726 
05727    /* Play the beginning intro if desired */
05728    if (!ast_strlen_zero(prefile)) {
05729 #ifdef ODBC_STORAGE
05730       int success = 
05731 #endif
05732          RETRIEVE(prefile, -1, ext, context);
05733       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05734          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05735             res = ast_waitstream(chan, ecodes);
05736 #ifdef ODBC_STORAGE
05737          if (success == -1) {
05738             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05739             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05740             store_file(prefile, vmu->mailbox, vmu->context, -1);
05741          }
05742 #endif
05743       } else {
05744          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05745          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05746       }
05747       DISPOSE(prefile, -1);
05748       if (res < 0) {
05749          ast_debug(1, "Hang up during prefile playback\n");
05750          free_user(vmu);
05751          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05752          ast_free(tmp);
05753          return -1;
05754       }
05755    }
05756    if (res == '#') {
05757       /* On a '#' we skip the instructions */
05758       ast_set_flag(options, OPT_SILENT);
05759       res = 0;
05760    }
05761    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05762    if (vmu->maxmsg == 0) {
05763       if (option_debug > 2)
05764          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05765       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05766       goto leave_vm_out;
05767    }
05768    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05769       res = ast_stream_and_wait(chan, INTRO, ecodes);
05770       if (res == '#') {
05771          ast_set_flag(options, OPT_SILENT);
05772          res = 0;
05773       }
05774    }
05775    if (res > 0)
05776       ast_stopstream(chan);
05777    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05778     other than the operator -- an automated attendant or mailbox login for example */
05779    if (res == '*') {
05780       chan->exten[0] = 'a';
05781       chan->exten[1] = '\0';
05782       if (!ast_strlen_zero(vmu->exit)) {
05783          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05784       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05785          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05786       }
05787       chan->priority = 0;
05788       free_user(vmu);
05789       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05790       ast_free(tmp);
05791       return 0;
05792    }
05793 
05794    /* Check for a '0' here */
05795    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05796    transfer:
05797       if (ouseexten || ousemacro) {
05798          chan->exten[0] = 'o';
05799          chan->exten[1] = '\0';
05800          if (!ast_strlen_zero(vmu->exit)) {
05801             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05802          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05803             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05804          }
05805          ast_play_and_wait(chan, "transfer");
05806          chan->priority = 0;
05807          free_user(vmu);
05808          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05809       }
05810       ast_free(tmp);
05811       return OPERATOR_EXIT;
05812    }
05813 
05814    /* Allow all other digits to exit Voicemail and return to the dialplan */
05815    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05816       if (!ast_strlen_zero(options->exitcontext)) {
05817          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05818       }
05819       free_user(vmu);
05820       ast_free(tmp);
05821       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05822       return res;
05823    }
05824 
05825    if (res < 0) {
05826       free_user(vmu);
05827       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05828       ast_free(tmp);
05829       return -1;
05830    }
05831    /* The meat of recording the message...  All the announcements and beeps have been played*/
05832    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05833    if (!ast_strlen_zero(fmt)) {
05834       msgnum = 0;
05835 
05836 #ifdef IMAP_STORAGE
05837       /* Is ext a mailbox? */
05838       /* must open stream for this user to get info! */
05839       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05840       if (res < 0) {
05841          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05842          ast_free(tmp);
05843          return -1;
05844       }
05845       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05846       /* It is possible under certain circumstances that inboxcount did not
05847        * create a vm_state when it was needed. This is a catchall which will
05848        * rarely be used.
05849        */
05850          if (!(vms = create_vm_state_from_user(vmu))) {
05851             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05852             ast_free(tmp);
05853             return -1;
05854          }
05855       }
05856       vms->newmessages++;
05857       
05858       /* here is a big difference! We add one to it later */
05859       msgnum = newmsgs + oldmsgs;
05860       ast_debug(3, "Messagecount set to %d\n", msgnum);
05861       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05862       /* set variable for compatibility */
05863       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05864 
05865       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05866          goto leave_vm_out;
05867       }
05868 #else
05869       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05870          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05871          if (!res)
05872             res = ast_waitstream(chan, "");
05873          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05874          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05875          inprocess_count(vmu->mailbox, vmu->context, -1);
05876          goto leave_vm_out;
05877       }
05878 
05879 #endif
05880       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05881       txtdes = mkstemp(tmptxtfile);
05882       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05883       if (txtdes < 0) {
05884          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05885          if (!res)
05886             res = ast_waitstream(chan, "");
05887          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05888          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05889          inprocess_count(vmu->mailbox, vmu->context, -1);
05890          goto leave_vm_out;
05891       }
05892 
05893       /* Now play the beep once we have the message number for our next message. */
05894       if (res >= 0) {
05895          /* Unless we're *really* silent, try to send the beep */
05896          res = ast_stream_and_wait(chan, "beep", "");
05897       }
05898             
05899       /* Store information in real-time storage */
05900       if (ast_check_realtime("voicemail_data")) {
05901          snprintf(priority, sizeof(priority), "%d", chan->priority);
05902          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05903          get_date(date, sizeof(date));
05904          ast_callerid_merge(callerid, sizeof(callerid),
05905             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05906             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05907             "Unknown");
05908          ast_store_realtime("voicemail_data",
05909             "origmailbox", ext,
05910             "context", chan->context,
05911             "macrocontext", chan->macrocontext,
05912             "exten", chan->exten,
05913             "priority", priority,
05914             "callerchan", chan->name,
05915             "callerid", callerid,
05916             "origdate", date,
05917             "origtime", origtime,
05918             "category", S_OR(category, ""),
05919             "filename", tmptxtfile,
05920             SENTINEL);
05921       }
05922 
05923       /* Store information */
05924       txt = fdopen(txtdes, "w+");
05925       if (txt) {
05926          get_date(date, sizeof(date));
05927          ast_callerid_merge(callerid, sizeof(callerid),
05928             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05929             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05930             "Unknown");
05931          fprintf(txt, 
05932             ";\n"
05933             "; Message Information file\n"
05934             ";\n"
05935             "[message]\n"
05936             "origmailbox=%s\n"
05937             "context=%s\n"
05938             "macrocontext=%s\n"
05939             "exten=%s\n"
05940             "rdnis=%s\n"
05941             "priority=%d\n"
05942             "callerchan=%s\n"
05943             "callerid=%s\n"
05944             "origdate=%s\n"
05945             "origtime=%ld\n"
05946             "category=%s\n",
05947             ext,
05948             chan->context,
05949             chan->macrocontext, 
05950             chan->exten,
05951             S_COR(chan->redirecting.from.number.valid,
05952                chan->redirecting.from.number.str, "unknown"),
05953             chan->priority,
05954             chan->name,
05955             callerid,
05956             date, (long) time(NULL),
05957             category ? category : "");
05958       } else {
05959          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05960          inprocess_count(vmu->mailbox, vmu->context, -1);
05961          if (ast_check_realtime("voicemail_data")) {
05962             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05963          }
05964          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05965          goto leave_vm_out;
05966       }
05967       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05968 
05969       if (txt) {
05970          fprintf(txt, "flag=%s\n", flag);
05971          if (sound_duration < vmu->minsecs) {
05972             fclose(txt);
05973             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05974             ast_filedelete(tmptxtfile, NULL);
05975             unlink(tmptxtfile);
05976             if (ast_check_realtime("voicemail_data")) {
05977                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05978             }
05979             inprocess_count(vmu->mailbox, vmu->context, -1);
05980          } else {
05981             fprintf(txt, "duration=%d\n", duration);
05982             fclose(txt);
05983             if (vm_lock_path(dir)) {
05984                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05985                /* Delete files */
05986                ast_filedelete(tmptxtfile, NULL);
05987                unlink(tmptxtfile);
05988                inprocess_count(vmu->mailbox, vmu->context, -1);
05989             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05990                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05991                unlink(tmptxtfile);
05992                ast_unlock_path(dir);
05993                inprocess_count(vmu->mailbox, vmu->context, -1);
05994                if (ast_check_realtime("voicemail_data")) {
05995                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05996                }
05997             } else {
05998 #ifndef IMAP_STORAGE
05999                msgnum = last_message_index(vmu, dir) + 1;
06000 #endif
06001                make_file(fn, sizeof(fn), dir, msgnum);
06002 
06003                /* assign a variable with the name of the voicemail file */ 
06004 #ifndef IMAP_STORAGE
06005                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06006 #else
06007                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06008 #endif
06009 
06010                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06011                ast_filerename(tmptxtfile, fn, NULL);
06012                rename(tmptxtfile, txtfile);
06013                inprocess_count(vmu->mailbox, vmu->context, -1);
06014 
06015                /* Properly set permissions on voicemail text descriptor file.
06016                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06017                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06018                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06019 
06020                ast_unlock_path(dir);
06021                if (ast_check_realtime("voicemail_data")) {
06022                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06023                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06024                }
06025                /* We must store the file first, before copying the message, because
06026                 * ODBC storage does the entire copy with SQL.
06027                 */
06028                if (ast_fileexists(fn, NULL, NULL) > 0) {
06029                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06030                }
06031 
06032                /* Are there to be more recipients of this message? */
06033                while (tmpptr) {
06034                   struct ast_vm_user recipu, *recip;
06035                   char *exten, *cntx;
06036 
06037                   exten = strsep(&tmpptr, "&");
06038                   cntx = strchr(exten, '@');
06039                   if (cntx) {
06040                      *cntx = '\0';
06041                      cntx++;
06042                   }
06043                   if ((recip = find_user(&recipu, cntx, exten))) {
06044                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06045                      free_user(recip);
06046                   }
06047                }
06048 #ifndef IMAP_STORAGE
06049                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06050                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06051                   char sfn[PATH_MAX];
06052                   char dfn[PATH_MAX];
06053                   int x;
06054                   /* It's easier just to try to make it than to check for its existence */
06055                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06056                   x = last_message_index(vmu, urgdir) + 1;
06057                   make_file(sfn, sizeof(sfn), dir, msgnum);
06058                   make_file(dfn, sizeof(dfn), urgdir, x);
06059                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06060                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06061                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06062                   ast_copy_string(fn, dfn, sizeof(fn));
06063                   msgnum = x;
06064                }
06065 #endif
06066                /* Notification needs to happen after the copy, though. */
06067                if (ast_fileexists(fn, NULL, NULL)) {
06068 #ifdef IMAP_STORAGE
06069                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06070                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06071                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06072                      flag);
06073 #else
06074                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06075                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06076                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06077                      flag);
06078 #endif
06079                }
06080 
06081                /* Disposal needs to happen after the optional move and copy */
06082                if (ast_fileexists(fn, NULL, NULL)) {
06083                   DISPOSE(dir, msgnum);
06084                }
06085             }
06086          }
06087       } else {
06088          inprocess_count(vmu->mailbox, vmu->context, -1);
06089       }
06090       if (res == '0') {
06091          goto transfer;
06092       } else if (res > 0 && res != 't')
06093          res = 0;
06094 
06095       if (sound_duration < vmu->minsecs)
06096          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06097          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06098       else
06099          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06100    } else
06101       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06102 leave_vm_out:
06103    free_user(vmu);
06104 
06105 #ifdef IMAP_STORAGE
06106    /* expunge message - use UID Expunge if supported on IMAP server*/
06107    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06108    if (expungeonhangup == 1) {
06109       ast_mutex_lock(&vms->lock);
06110 #ifdef HAVE_IMAP_TK2006
06111       if (LEVELUIDPLUS (vms->mailstream)) {
06112          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06113       } else 
06114 #endif
06115          mail_expunge(vms->mailstream);
06116       ast_mutex_unlock(&vms->lock);
06117    }
06118 #endif
06119 
06120    ast_free(tmp);
06121    return res;
06122 }
06123 
06124 #if !defined(IMAP_STORAGE)
06125 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06126 {
06127    /* we know the actual number of messages, so stop process when number is hit */
06128 
06129    int x, dest;
06130    char sfn[PATH_MAX];
06131    char dfn[PATH_MAX];
06132 
06133    if (vm_lock_path(dir)) {
06134       return ERROR_LOCK_PATH;
06135    }
06136 
06137    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06138       make_file(sfn, sizeof(sfn), dir, x);
06139       if (EXISTS(dir, x, sfn, NULL)) {
06140 
06141          if (x != dest) {
06142             make_file(dfn, sizeof(dfn), dir, dest);
06143             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06144          }
06145 
06146          dest++;
06147       }
06148    }
06149    ast_unlock_path(dir);
06150 
06151    return dest;
06152 }
06153 #endif
06154 
06155 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06156 {
06157    int d;
06158    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06159    return d;
06160 }
06161 
06162 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06163 {
06164 #ifdef IMAP_STORAGE
06165    /* we must use mbox(x) folder names, and copy the message there */
06166    /* simple. huh? */
06167    char sequence[10];
06168    char mailbox[256];
06169    int res;
06170 
06171    /* get the real IMAP message number for this message */
06172    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06173    
06174    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06175    ast_mutex_lock(&vms->lock);
06176    /* if save to Old folder, put in INBOX as read */
06177    if (box == OLD_FOLDER) {
06178       mail_setflag(vms->mailstream, sequence, "\\Seen");
06179       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06180    } else if (box == NEW_FOLDER) {
06181       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06182       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06183    }
06184    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06185       ast_mutex_unlock(&vms->lock);
06186       return 0;
06187    }
06188    /* Create the folder if it don't exist */
06189    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06190    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06191    if (mail_create(vms->mailstream, mailbox) == NIL) 
06192       ast_debug(5, "Folder exists.\n");
06193    else
06194       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06195    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06196    ast_mutex_unlock(&vms->lock);
06197    return res;
06198 #else
06199    char *dir = vms->curdir;
06200    char *username = vms->username;
06201    char *context = vmu->context;
06202    char sfn[PATH_MAX];
06203    char dfn[PATH_MAX];
06204    char ddir[PATH_MAX];
06205    const char *dbox = mbox(vmu, box);
06206    int x, i;
06207    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06208 
06209    if (vm_lock_path(ddir))
06210       return ERROR_LOCK_PATH;
06211 
06212    x = last_message_index(vmu, ddir) + 1;
06213 
06214    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06215       x--;
06216       for (i = 1; i <= x; i++) {
06217          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06218          make_file(sfn, sizeof(sfn), ddir, i);
06219          make_file(dfn, sizeof(dfn), ddir, i - 1);
06220          if (EXISTS(ddir, i, sfn, NULL)) {
06221             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06222          } else
06223             break;
06224       }
06225    } else {
06226       if (x >= vmu->maxmsg) {
06227          ast_unlock_path(ddir);
06228          return -1;
06229       }
06230    }
06231    make_file(sfn, sizeof(sfn), dir, msg);
06232    make_file(dfn, sizeof(dfn), ddir, x);
06233    if (strcmp(sfn, dfn)) {
06234       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06235    }
06236    ast_unlock_path(ddir);
06237 #endif
06238    return 0;
06239 }
06240 
06241 static int adsi_logo(unsigned char *buf)
06242 {
06243    int bytes = 0;
06244    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06245    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06246    return bytes;
06247 }
06248 
06249 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06250 {
06251    unsigned char buf[256];
06252    int bytes = 0;
06253    int x;
06254    char num[5];
06255 
06256    *useadsi = 0;
06257    bytes += ast_adsi_data_mode(buf + bytes);
06258    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06259 
06260    bytes = 0;
06261    bytes += adsi_logo(buf);
06262    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06263 #ifdef DISPLAY
06264    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06265 #endif
06266    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06267    bytes += ast_adsi_data_mode(buf + bytes);
06268    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06269 
06270    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06271       bytes = 0;
06272       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06273       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06274       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06275       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06276       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06277       return 0;
06278    }
06279 
06280 #ifdef DISPLAY
06281    /* Add a dot */
06282    bytes = 0;
06283    bytes += ast_adsi_logo(buf);
06284    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06285    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06286    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06287    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06288 #endif
06289    bytes = 0;
06290    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06291    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06292    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06293    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06294    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06295    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06296    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06297 
06298 #ifdef DISPLAY
06299    /* Add another dot */
06300    bytes = 0;
06301    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06302    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06303 
06304    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06305    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06306 #endif
06307 
06308    bytes = 0;
06309    /* These buttons we load but don't use yet */
06310    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06311    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06312    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06313    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06314    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06315    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06316    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06317 
06318 #ifdef DISPLAY
06319    /* Add another dot */
06320    bytes = 0;
06321    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06322    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06323    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06324 #endif
06325 
06326    bytes = 0;
06327    for (x = 0; x < 5; x++) {
06328       snprintf(num, sizeof(num), "%d", x);
06329       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06330    }
06331    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06332    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06333 
06334 #ifdef DISPLAY
06335    /* Add another dot */
06336    bytes = 0;
06337    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06338    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06339    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06340 #endif
06341 
06342    if (ast_adsi_end_download(chan)) {
06343       bytes = 0;
06344       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06345       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06346       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06347       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06348       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06349       return 0;
06350    }
06351    bytes = 0;
06352    bytes += ast_adsi_download_disconnect(buf + bytes);
06353    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06354    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06355 
06356    ast_debug(1, "Done downloading scripts...\n");
06357 
06358 #ifdef DISPLAY
06359    /* Add last dot */
06360    bytes = 0;
06361    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06362    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06363 #endif
06364    ast_debug(1, "Restarting session...\n");
06365 
06366    bytes = 0;
06367    /* Load the session now */
06368    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06369       *useadsi = 1;
06370       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06371    } else
06372       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06373 
06374    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06375    return 0;
06376 }
06377 
06378 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06379 {
06380    int x;
06381    if (!ast_adsi_available(chan))
06382       return;
06383    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06384    if (x < 0)
06385       return;
06386    if (!x) {
06387       if (adsi_load_vmail(chan, useadsi)) {
06388          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06389          return;
06390       }
06391    } else
06392       *useadsi = 1;
06393 }
06394 
06395 static void adsi_login(struct ast_channel *chan)
06396 {
06397    unsigned char buf[256];
06398    int bytes = 0;
06399    unsigned char keys[8];
06400    int x;
06401    if (!ast_adsi_available(chan))
06402       return;
06403 
06404    for (x = 0; x < 8; x++)
06405       keys[x] = 0;
06406    /* Set one key for next */
06407    keys[3] = ADSI_KEY_APPS + 3;
06408 
06409    bytes += adsi_logo(buf + bytes);
06410    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06411    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06412    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06413    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06414    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06415    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06416    bytes += ast_adsi_set_keys(buf + bytes, keys);
06417    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06418    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06419 }
06420 
06421 static void adsi_password(struct ast_channel *chan)
06422 {
06423    unsigned char buf[256];
06424    int bytes = 0;
06425    unsigned char keys[8];
06426    int x;
06427    if (!ast_adsi_available(chan))
06428       return;
06429 
06430    for (x = 0; x < 8; x++)
06431       keys[x] = 0;
06432    /* Set one key for next */
06433    keys[3] = ADSI_KEY_APPS + 3;
06434 
06435    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06436    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06437    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06438    bytes += ast_adsi_set_keys(buf + bytes, keys);
06439    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06440    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06441 }
06442 
06443 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06444 {
06445    unsigned char buf[256];
06446    int bytes = 0;
06447    unsigned char keys[8];
06448    int x, y;
06449 
06450    if (!ast_adsi_available(chan))
06451       return;
06452 
06453    for (x = 0; x < 5; x++) {
06454       y = ADSI_KEY_APPS + 12 + start + x;
06455       if (y > ADSI_KEY_APPS + 12 + 4)
06456          y = 0;
06457       keys[x] = ADSI_KEY_SKT | y;
06458    }
06459    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06460    keys[6] = 0;
06461    keys[7] = 0;
06462 
06463    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06464    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06465    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06466    bytes += ast_adsi_set_keys(buf + bytes, keys);
06467    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06468 
06469    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06470 }
06471 
06472 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06473 {
06474    int bytes = 0;
06475    unsigned char buf[256]; 
06476    char buf1[256], buf2[256];
06477    char fn2[PATH_MAX];
06478 
06479    char cid[256] = "";
06480    char *val;
06481    char *name, *num;
06482    char datetime[21] = "";
06483    FILE *f;
06484 
06485    unsigned char keys[8];
06486 
06487    int x;
06488 
06489    if (!ast_adsi_available(chan))
06490       return;
06491 
06492    /* Retrieve important info */
06493    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06494    f = fopen(fn2, "r");
06495    if (f) {
06496       while (!feof(f)) {   
06497          if (!fgets((char *) buf, sizeof(buf), f)) {
06498             continue;
06499          }
06500          if (!feof(f)) {
06501             char *stringp = NULL;
06502             stringp = (char *) buf;
06503             strsep(&stringp, "=");
06504             val = strsep(&stringp, "=");
06505             if (!ast_strlen_zero(val)) {
06506                if (!strcmp((char *) buf, "callerid"))
06507                   ast_copy_string(cid, val, sizeof(cid));
06508                if (!strcmp((char *) buf, "origdate"))
06509                   ast_copy_string(datetime, val, sizeof(datetime));
06510             }
06511          }
06512       }
06513       fclose(f);
06514    }
06515    /* New meaning for keys */
06516    for (x = 0; x < 5; x++)
06517       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06518    keys[6] = 0x0;
06519    keys[7] = 0x0;
06520 
06521    if (!vms->curmsg) {
06522       /* No prev key, provide "Folder" instead */
06523       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06524    }
06525    if (vms->curmsg >= vms->lastmsg) {
06526       /* If last message ... */
06527       if (vms->curmsg) {
06528          /* but not only message, provide "Folder" instead */
06529          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06530          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06531 
06532       } else {
06533          /* Otherwise if only message, leave blank */
06534          keys[3] = 1;
06535       }
06536    }
06537 
06538    if (!ast_strlen_zero(cid)) {
06539       ast_callerid_parse(cid, &name, &num);
06540       if (!name)
06541          name = num;
06542    } else
06543       name = "Unknown Caller";
06544 
06545    /* If deleted, show "undeleted" */
06546 #ifdef IMAP_STORAGE
06547    ast_mutex_lock(&vms->lock);
06548 #endif
06549    if (vms->deleted[vms->curmsg]) {
06550       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06551    }
06552 #ifdef IMAP_STORAGE
06553    ast_mutex_unlock(&vms->lock);
06554 #endif
06555 
06556    /* Except "Exit" */
06557    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06558    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06559       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06560    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06561 
06562    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06563    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06564    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06565    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06566    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06567    bytes += ast_adsi_set_keys(buf + bytes, keys);
06568    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06569 
06570    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06571 }
06572 
06573 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06574 {
06575    int bytes = 0;
06576    unsigned char buf[256];
06577    unsigned char keys[8];
06578 
06579    int x;
06580 
06581    if (!ast_adsi_available(chan))
06582       return;
06583 
06584    /* New meaning for keys */
06585    for (x = 0; x < 5; x++)
06586       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06587 
06588    keys[6] = 0x0;
06589    keys[7] = 0x0;
06590 
06591    if (!vms->curmsg) {
06592       /* No prev key, provide "Folder" instead */
06593       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06594    }
06595    if (vms->curmsg >= vms->lastmsg) {
06596       /* If last message ... */
06597       if (vms->curmsg) {
06598          /* but not only message, provide "Folder" instead */
06599          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06600       } else {
06601          /* Otherwise if only message, leave blank */
06602          keys[3] = 1;
06603       }
06604    }
06605 
06606    /* If deleted, show "undeleted" */
06607 #ifdef IMAP_STORAGE
06608    ast_mutex_lock(&vms->lock);
06609 #endif
06610    if (vms->deleted[vms->curmsg]) {
06611       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06612    }
06613 #ifdef IMAP_STORAGE
06614    ast_mutex_unlock(&vms->lock);
06615 #endif
06616 
06617    /* Except "Exit" */
06618    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06619    bytes += ast_adsi_set_keys(buf + bytes, keys);
06620    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06621 
06622    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06623 }
06624 
06625 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06626 {
06627    unsigned char buf[256] = "";
06628    char buf1[256] = "", buf2[256] = "";
06629    int bytes = 0;
06630    unsigned char keys[8];
06631    int x;
06632 
06633    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06634    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06635    if (!ast_adsi_available(chan))
06636       return;
06637    if (vms->newmessages) {
06638       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06639       if (vms->oldmessages) {
06640          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06641          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06642       } else {
06643          snprintf(buf2, sizeof(buf2), "%s.", newm);
06644       }
06645    } else if (vms->oldmessages) {
06646       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06647       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06648    } else {
06649       strcpy(buf1, "You have no messages.");
06650       buf2[0] = ' ';
06651       buf2[1] = '\0';
06652    }
06653    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06654    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06655    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06656 
06657    for (x = 0; x < 6; x++)
06658       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06659    keys[6] = 0;
06660    keys[7] = 0;
06661 
06662    /* Don't let them listen if there are none */
06663    if (vms->lastmsg < 0)
06664       keys[0] = 1;
06665    bytes += ast_adsi_set_keys(buf + bytes, keys);
06666 
06667    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06668 
06669    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06670 }
06671 
06672 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06673 {
06674    unsigned char buf[256] = "";
06675    char buf1[256] = "", buf2[256] = "";
06676    int bytes = 0;
06677    unsigned char keys[8];
06678    int x;
06679 
06680    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06681 
06682    if (!ast_adsi_available(chan))
06683       return;
06684 
06685    /* Original command keys */
06686    for (x = 0; x < 6; x++)
06687       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06688 
06689    keys[6] = 0;
06690    keys[7] = 0;
06691 
06692    if ((vms->lastmsg + 1) < 1)
06693       keys[0] = 0;
06694 
06695    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06696       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06697 
06698    if (vms->lastmsg + 1)
06699       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06700    else
06701       strcpy(buf2, "no messages.");
06702    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06703    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06704    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06705    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06706    bytes += ast_adsi_set_keys(buf + bytes, keys);
06707 
06708    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06709 
06710    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06711    
06712 }
06713 
06714 /*
06715 static void adsi_clear(struct ast_channel *chan)
06716 {
06717    char buf[256];
06718    int bytes=0;
06719    if (!ast_adsi_available(chan))
06720       return;
06721    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06722    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06723 
06724    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06725 }
06726 */
06727 
06728 static void adsi_goodbye(struct ast_channel *chan)
06729 {
06730    unsigned char buf[256];
06731    int bytes = 0;
06732 
06733    if (!ast_adsi_available(chan))
06734       return;
06735    bytes += adsi_logo(buf + bytes);
06736    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06737    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06738    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06739    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06740 
06741    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06742 }
06743 
06744 /*!\brief get_folder: Folder menu
06745  * Plays "press 1 for INBOX messages" etc.
06746  * Should possibly be internationalized
06747  */
06748 static int get_folder(struct ast_channel *chan, int start)
06749 {
06750    int x;
06751    int d;
06752    char fn[PATH_MAX];
06753    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06754    if (d)
06755       return d;
06756    for (x = start; x < 5; x++) { /* For all folders */
06757       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06758          return d;
06759       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06760       if (d)
06761          return d;
06762       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06763 
06764       /* The inbox folder can have its name changed under certain conditions
06765        * so this checks if the sound file exists for the inbox folder name and
06766        * if it doesn't, plays the default name instead. */
06767       if (x == 0) {
06768          if (ast_fileexists(fn, NULL, NULL)) {
06769             d = vm_play_folder_name(chan, fn);
06770          } else {
06771             ast_verb(1, "failed to find %s\n", fn);
06772             d = vm_play_folder_name(chan, "vm-INBOX");
06773          }
06774       } else {
06775          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06776          d = vm_play_folder_name(chan, fn);
06777       }
06778 
06779       if (d)
06780          return d;
06781       d = ast_waitfordigit(chan, 500);
06782       if (d)
06783          return d;
06784    }
06785 
06786    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06787    if (d)
06788       return d;
06789    d = ast_waitfordigit(chan, 4000);
06790    return d;
06791 }
06792 
06793 /*!
06794  * \brief plays a prompt and waits for a keypress.
06795  * \param chan
06796  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06797  * \param start Does not appear to be used at this time.
06798  *
06799  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06800  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06801  * prompting for the number inputs that correspond to the available folders.
06802  * 
06803  * \return zero on success, or -1 on error.
06804  */
06805 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06806 {
06807    int res = 0;
06808    int loops = 0;
06809 
06810    res = ast_play_and_wait(chan, fn);  /* Folder name */
06811    while (((res < '0') || (res > '9')) &&
06812          (res != '#') && (res >= 0) &&
06813          loops < 4) {
06814       res = get_folder(chan, 0);
06815       loops++;
06816    }
06817    if (loops == 4) { /* give up */
06818       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06819       return '#';
06820    }
06821    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06822    return res;
06823 }
06824 
06825 /*!
06826  * \brief presents the option to prepend to an existing message when forwarding it.
06827  * \param chan
06828  * \param vmu
06829  * \param curdir
06830  * \param curmsg
06831  * \param vm_fmts
06832  * \param context
06833  * \param record_gain
06834  * \param duration
06835  * \param vms
06836  * \param flag 
06837  *
06838  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06839  *
06840  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06841  * \return zero on success, -1 on error.
06842  */
06843 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06844          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06845 {
06846    int cmd = 0;
06847    int retries = 0, prepend_duration = 0, already_recorded = 0;
06848    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06849    char textfile[PATH_MAX];
06850    struct ast_config *msg_cfg;
06851    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06852 #ifndef IMAP_STORAGE
06853    signed char zero_gain = 0;
06854 #endif
06855    const char *duration_str;
06856 
06857    /* Must always populate duration correctly */
06858    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06859    strcpy(textfile, msgfile);
06860    strcpy(backup, msgfile);
06861    strcpy(backup_textfile, msgfile);
06862    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06863    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06864    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06865 
06866    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06867       *duration = atoi(duration_str);
06868    } else {
06869       *duration = 0;
06870    }
06871 
06872    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06873       if (cmd)
06874          retries = 0;
06875       switch (cmd) {
06876       case '1': 
06877 
06878 #ifdef IMAP_STORAGE
06879          /* Record new intro file */
06880          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06881          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06882          ast_play_and_wait(chan, INTRO);
06883          ast_play_and_wait(chan, "beep");
06884          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06885          if (cmd == -1) {
06886             break;
06887          }
06888          cmd = 't';
06889 #else
06890 
06891          /* prepend a message to the current message, update the metadata and return */
06892 
06893          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06894          strcpy(textfile, msgfile);
06895          strncat(textfile, ".txt", sizeof(textfile) - 1);
06896          *duration = 0;
06897 
06898          /* if we can't read the message metadata, stop now */
06899          if (!msg_cfg) {
06900             cmd = 0;
06901             break;
06902          }
06903 
06904          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06905 #ifndef IMAP_STORAGE
06906          if (already_recorded) {
06907             ast_filecopy(backup, msgfile, NULL);
06908             copy(backup_textfile, textfile);
06909          }
06910          else {
06911             ast_filecopy(msgfile, backup, NULL);
06912             copy(textfile, backup_textfile);
06913          }
06914 #endif
06915          already_recorded = 1;
06916 
06917          if (record_gain)
06918             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06919 
06920          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06921 
06922          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06923             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06924             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06925             ast_filerename(backup, msgfile, NULL);
06926          }
06927 
06928          if (record_gain)
06929             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06930 
06931          
06932          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06933             *duration = atoi(duration_str);
06934 
06935          if (prepend_duration) {
06936             struct ast_category *msg_cat;
06937             /* need enough space for a maximum-length message duration */
06938             char duration_buf[12];
06939 
06940             *duration += prepend_duration;
06941             msg_cat = ast_category_get(msg_cfg, "message");
06942             snprintf(duration_buf, 11, "%ld", *duration);
06943             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06944                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06945             }
06946          }
06947 
06948 #endif
06949          break;
06950       case '2': 
06951          /* NULL out introfile so we know there is no intro! */
06952 #ifdef IMAP_STORAGE
06953          *vms->introfn = '\0';
06954 #endif
06955          cmd = 't';
06956          break;
06957       case '*':
06958          cmd = '*';
06959          break;
06960       default: 
06961          /* If time_out and return to menu, reset already_recorded */
06962          already_recorded = 0;
06963 
06964          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06965             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06966          if (!cmd) {
06967             cmd = ast_play_and_wait(chan, "vm-starmain");
06968             /* "press star to return to the main menu" */
06969          }
06970          if (!cmd) {
06971             cmd = ast_waitfordigit(chan, 6000);
06972          }
06973          if (!cmd) {
06974             retries++;
06975          }
06976          if (retries > 3) {
06977             cmd = '*'; /* Let's cancel this beast */
06978          }
06979          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06980       }
06981    }
06982 
06983    if (msg_cfg)
06984       ast_config_destroy(msg_cfg);
06985    if (prepend_duration)
06986       *duration = prepend_duration;
06987 
06988    if (already_recorded && cmd == -1) {
06989       /* restore original message if prepention cancelled */
06990       ast_filerename(backup, msgfile, NULL);
06991       rename(backup_textfile, textfile);
06992    }
06993 
06994    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
06995       cmd = 0;
06996    return cmd;
06997 }
06998 
06999 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07000 {
07001    struct ast_event *event;
07002    char *mailbox, *context;
07003 
07004    /* Strip off @default */
07005    context = mailbox = ast_strdupa(box);
07006    strsep(&context, "@");
07007    if (ast_strlen_zero(context))
07008       context = "default";
07009 
07010    if (!(event = ast_event_new(AST_EVENT_MWI,
07011          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07012          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07013          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07014          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07015          AST_EVENT_IE_END))) {
07016       return;
07017    }
07018 
07019    ast_event_queue_and_cache(event);
07020 }
07021 
07022 /*!
07023  * \brief Sends email notification that a user has a new voicemail waiting for them.
07024  * \param chan
07025  * \param vmu
07026  * \param vms
07027  * \param msgnum
07028  * \param duration
07029  * \param fmt
07030  * \param cidnum The Caller ID phone number value.
07031  * \param cidname The Caller ID name value.
07032  * \param flag
07033  *
07034  * \return zero on success, -1 on error.
07035  */
07036 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)
07037 {
07038    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07039    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07040    const char *category;
07041    char *myserveremail = serveremail;
07042 
07043    ast_channel_lock(chan);
07044    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07045       category = ast_strdupa(category);
07046    }
07047    ast_channel_unlock(chan);
07048 
07049 #ifndef IMAP_STORAGE
07050    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07051 #else
07052    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07053 #endif
07054    make_file(fn, sizeof(fn), todir, msgnum);
07055    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07056 
07057    if (!ast_strlen_zero(vmu->attachfmt)) {
07058       if (strstr(fmt, vmu->attachfmt))
07059          fmt = vmu->attachfmt;
07060       else
07061          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);
07062    }
07063 
07064    /* Attach only the first format */
07065    fmt = ast_strdupa(fmt);
07066    stringp = fmt;
07067    strsep(&stringp, "|");
07068 
07069    if (!ast_strlen_zero(vmu->serveremail))
07070       myserveremail = vmu->serveremail;
07071 
07072    if (!ast_strlen_zero(vmu->email)) {
07073       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07074 
07075       if (attach_user_voicemail)
07076          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07077 
07078       /* XXX possible imap issue, should category be NULL XXX */
07079       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07080 
07081       if (attach_user_voicemail)
07082          DISPOSE(todir, msgnum);
07083    }
07084 
07085    if (!ast_strlen_zero(vmu->pager)) {
07086       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07087    }
07088 
07089    if (ast_test_flag(vmu, VM_DELETE))
07090       DELETE(todir, msgnum, fn, vmu);
07091 
07092    /* Leave voicemail for someone */
07093    if (ast_app_has_voicemail(ext_context, NULL)) 
07094       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07095 
07096    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07097 
07098    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);
07099    run_externnotify(vmu->context, vmu->mailbox, flag);
07100 
07101 #ifdef IMAP_STORAGE
07102    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07103    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07104       vm_imap_delete(NULL, vms->curmsg, vmu);
07105       vms->newmessages--;  /* Fix new message count */
07106    }
07107 #endif
07108 
07109    return 0;
07110 }
07111 
07112 /*!
07113  * \brief Sends a voicemail message to a mailbox recipient.
07114  * \param chan
07115  * \param context
07116  * \param vms
07117  * \param sender
07118  * \param fmt
07119  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07120  *             Will be 0 when called to forward an existing message (option 8)
07121  *             Will be 1 when called to leave a message (option 3->5)
07122  * \param record_gain 
07123  * \param urgent
07124  *
07125  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07126  * 
07127  * When in the leave message mode (is_new_message == 1):
07128  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07129  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07130  *
07131  * When in the forward message mode (is_new_message == 0):
07132  *   - retreives the current message to be forwarded
07133  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07134  *   - determines the target mailbox and folders
07135  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07136  *
07137  * \return zero on success, -1 on error.
07138  */
07139 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)
07140 {
07141 #ifdef IMAP_STORAGE
07142    int todircount = 0;
07143    struct vm_state *dstvms;
07144 #endif
07145    char username[70]="";
07146    char fn[PATH_MAX]; /* for playback of name greeting */
07147    char ecodes[16] = "#";
07148    int res = 0, cmd = 0;
07149    struct ast_vm_user *receiver = NULL, *vmtmp;
07150    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07151    char *stringp;
07152    const char *s;
07153    int saved_messages = 0;
07154    int valid_extensions = 0;
07155    char *dir;
07156    int curmsg;
07157    char urgent_str[7] = "";
07158    int prompt_played = 0;
07159 #ifndef IMAP_STORAGE
07160    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07161 #endif
07162    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07163       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07164    }
07165 
07166    if (vms == NULL) return -1;
07167    dir = vms->curdir;
07168    curmsg = vms->curmsg;
07169 
07170    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07171    while (!res && !valid_extensions) {
07172       int use_directory = 0;
07173       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07174          int done = 0;
07175          int retries = 0;
07176          cmd = 0;
07177          while ((cmd >= 0) && !done ){
07178             if (cmd)
07179                retries = 0;
07180             switch (cmd) {
07181             case '1': 
07182                use_directory = 0;
07183                done = 1;
07184                break;
07185             case '2': 
07186                use_directory = 1;
07187                done = 1;
07188                break;
07189             case '*': 
07190                cmd = 't';
07191                done = 1;
07192                break;
07193             default: 
07194                /* Press 1 to enter an extension press 2 to use the directory */
07195                cmd = ast_play_and_wait(chan, "vm-forward");
07196                if (!cmd) {
07197                   cmd = ast_waitfordigit(chan, 3000);
07198                }
07199                if (!cmd) {
07200                   retries++;
07201                }
07202                if (retries > 3) {
07203                   cmd = 't';
07204                   done = 1;
07205                }
07206                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07207             }
07208          }
07209          if (cmd < 0 || cmd == 't')
07210             break;
07211       }
07212       
07213       if (use_directory) {
07214          /* use app_directory */
07215          
07216          char old_context[sizeof(chan->context)];
07217          char old_exten[sizeof(chan->exten)];
07218          int old_priority;
07219          struct ast_app* directory_app;
07220 
07221          directory_app = pbx_findapp("Directory");
07222          if (directory_app) {
07223             char vmcontext[256];
07224             /* make backup copies */
07225             memcpy(old_context, chan->context, sizeof(chan->context));
07226             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07227             old_priority = chan->priority;
07228             
07229             /* call the the Directory, changes the channel */
07230             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07231             res = pbx_exec(chan, directory_app, vmcontext);
07232             
07233             ast_copy_string(username, chan->exten, sizeof(username));
07234             
07235             /* restore the old context, exten, and priority */
07236             memcpy(chan->context, old_context, sizeof(chan->context));
07237             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07238             chan->priority = old_priority;
07239          } else {
07240             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07241             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07242          }
07243       } else {
07244          /* Ask for an extension */
07245          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07246          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07247          prompt_played++;
07248          if (res || prompt_played > 4)
07249             break;
07250          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07251             break;
07252       }
07253       
07254       /* start all over if no username */
07255       if (ast_strlen_zero(username))
07256          continue;
07257       stringp = username;
07258       s = strsep(&stringp, "*");
07259       /* start optimistic */
07260       valid_extensions = 1;
07261       while (s) {
07262          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07263             int oldmsgs;
07264             int newmsgs;
07265             int capacity;
07266             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07267                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07268                /* Shouldn't happen, but allow trying another extension if it does */
07269                res = ast_play_and_wait(chan, "pbx-invalid");
07270                valid_extensions = 0;
07271                break;
07272             }
07273             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07274             if ((newmsgs + oldmsgs) >= capacity) {
07275                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07276                res = ast_play_and_wait(chan, "vm-mailboxfull");
07277                valid_extensions = 0;
07278                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07279                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07280                   free_user(vmtmp);
07281                }
07282                inprocess_count(receiver->mailbox, receiver->context, -1);
07283                break;
07284             }
07285             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07286          } else {
07287             /* XXX Optimization for the future.  When we encounter a single bad extension,
07288              * bailing out on all of the extensions may not be the way to go.  We should
07289              * probably just bail on that single extension, then allow the user to enter
07290              * several more. XXX
07291              */
07292             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07293                free_user(receiver);
07294             }
07295             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07296             /* "I am sorry, that's not a valid extension.  Please try again." */
07297             res = ast_play_and_wait(chan, "pbx-invalid");
07298             valid_extensions = 0;
07299             break;
07300          }
07301 
07302          /* play name if available, else play extension number */
07303          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07304          RETRIEVE(fn, -1, s, receiver->context);
07305          if (ast_fileexists(fn, NULL, NULL) > 0) {
07306             res = ast_stream_and_wait(chan, fn, ecodes);
07307             if (res) {
07308                DISPOSE(fn, -1);
07309                return res;
07310             }
07311          } else {
07312             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07313          }
07314          DISPOSE(fn, -1);
07315 
07316          s = strsep(&stringp, "*");
07317       }
07318       /* break from the loop of reading the extensions */
07319       if (valid_extensions)
07320          break;
07321    }
07322    /* check if we're clear to proceed */
07323    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07324       return res;
07325    if (is_new_message == 1) {
07326       struct leave_vm_options leave_options;
07327       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07328       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07329 
07330       /* Send VoiceMail */
07331       memset(&leave_options, 0, sizeof(leave_options));
07332       leave_options.record_gain = record_gain;
07333       cmd = leave_voicemail(chan, mailbox, &leave_options);
07334    } else {
07335       /* Forward VoiceMail */
07336       long duration = 0;
07337       struct vm_state vmstmp;
07338       int copy_msg_result = 0;
07339       memcpy(&vmstmp, vms, sizeof(vmstmp));
07340 
07341       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07342 
07343       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07344       if (!cmd) {
07345          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07346 #ifdef IMAP_STORAGE
07347             int attach_user_voicemail;
07348             char *myserveremail = serveremail;
07349             
07350             /* get destination mailbox */
07351             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07352             if (!dstvms) {
07353                dstvms = create_vm_state_from_user(vmtmp);
07354             }
07355             if (dstvms) {
07356                init_mailstream(dstvms, 0);
07357                if (!dstvms->mailstream) {
07358                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07359                } else {
07360                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07361                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07362                }
07363             } else {
07364                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07365             }
07366             if (!ast_strlen_zero(vmtmp->serveremail))
07367                myserveremail = vmtmp->serveremail;
07368             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07369             /* NULL category for IMAP storage */
07370             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07371                dstvms->curbox,
07372                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07373                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07374                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07375                NULL, urgent_str);
07376 #else
07377             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07378 #endif
07379             saved_messages++;
07380             AST_LIST_REMOVE_CURRENT(list);
07381             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07382             free_user(vmtmp);
07383             if (res)
07384                break;
07385          }
07386          AST_LIST_TRAVERSE_SAFE_END;
07387          if (saved_messages > 0 && !copy_msg_result) {
07388             /* give confirmation that the message was saved */
07389             /* commented out since we can't forward batches yet
07390             if (saved_messages == 1)
07391                res = ast_play_and_wait(chan, "vm-message");
07392             else
07393                res = ast_play_and_wait(chan, "vm-messages");
07394             if (!res)
07395                res = ast_play_and_wait(chan, "vm-saved"); */
07396 #ifdef IMAP_STORAGE
07397             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07398             if (ast_strlen_zero(vmstmp.introfn))
07399 #endif
07400             res = ast_play_and_wait(chan, "vm-msgsaved");
07401          }
07402 #ifndef IMAP_STORAGE
07403          else {
07404             /* with IMAP, mailbox full warning played by imap_check_limits */
07405             res = ast_play_and_wait(chan, "vm-mailboxfull");
07406          }
07407          /* Restore original message without prepended message if backup exists */
07408          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07409          strcpy(textfile, msgfile);
07410          strcpy(backup, msgfile);
07411          strcpy(backup_textfile, msgfile);
07412          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07413          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07414          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07415          if (ast_fileexists(backup, NULL, NULL) > 0) {
07416             ast_filerename(backup, msgfile, NULL);
07417             rename(backup_textfile, textfile);
07418          }
07419 #endif
07420       }
07421       DISPOSE(dir, curmsg);
07422 #ifndef IMAP_STORAGE
07423       if (cmd) { /* assuming hangup, cleanup backup file */
07424          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07425          strcpy(textfile, msgfile);
07426          strcpy(backup_textfile, msgfile);
07427          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07428          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07429          rename(backup_textfile, textfile);
07430       }
07431 #endif
07432    }
07433 
07434    /* If anything failed above, we still have this list to free */
07435    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07436       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07437       free_user(vmtmp);
07438    }
07439    return res ? res : cmd;
07440 }
07441 
07442 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07443 {
07444    int res;
07445    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07446       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07447    return res;
07448 }
07449 
07450 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07451 {
07452    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07453    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);
07454 }
07455 
07456 static int play_message_category(struct ast_channel *chan, const char *category)
07457 {
07458    int res = 0;
07459 
07460    if (!ast_strlen_zero(category))
07461       res = ast_play_and_wait(chan, category);
07462 
07463    if (res) {
07464       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07465       res = 0;
07466    }
07467 
07468    return res;
07469 }
07470 
07471 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07472 {
07473    int res = 0;
07474    struct vm_zone *the_zone = NULL;
07475    time_t t;
07476 
07477    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07478       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07479       return 0;
07480    }
07481 
07482    /* Does this user have a timezone specified? */
07483    if (!ast_strlen_zero(vmu->zonetag)) {
07484       /* Find the zone in the list */
07485       struct vm_zone *z;
07486       AST_LIST_LOCK(&zones);
07487       AST_LIST_TRAVERSE(&zones, z, list) {
07488          if (!strcmp(z->name, vmu->zonetag)) {
07489             the_zone = z;
07490             break;
07491          }
07492       }
07493       AST_LIST_UNLOCK(&zones);
07494    }
07495 
07496 /* No internal variable parsing for now, so we'll comment it out for the time being */
07497 #if 0
07498    /* Set the DIFF_* variables */
07499    ast_localtime(&t, &time_now, NULL);
07500    tv_now = ast_tvnow();
07501    ast_localtime(&tv_now, &time_then, NULL);
07502 
07503    /* Day difference */
07504    if (time_now.tm_year == time_then.tm_year)
07505       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07506    else
07507       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07508    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07509 
07510    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07511 #endif
07512    if (the_zone) {
07513       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07514    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07515       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07516    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07517       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07518    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07519       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);
07520    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07521       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07522    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07523       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07524    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07525       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07526    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07527       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);
07528    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07529       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07530    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07531       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07532    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07533       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);
07534    } else {
07535       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07536    }
07537 #if 0
07538    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07539 #endif
07540    return res;
07541 }
07542 
07543 
07544 
07545 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07546 {
07547    int res = 0;
07548    int i;
07549    char *callerid, *name;
07550    char prefile[PATH_MAX] = "";
07551    
07552 
07553    /* If voicemail cid is not enabled, or we didn't get cid or context from
07554     * the attribute file, leave now.
07555     *
07556     * TODO Still need to change this so that if this function is called by the
07557     * message envelope (and someone is explicitly requesting to hear the CID),
07558     * it does not check to see if CID is enabled in the config file.
07559     */
07560    if ((cid == NULL)||(context == NULL))
07561       return res;
07562 
07563    /* Strip off caller ID number from name */
07564    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07565    ast_callerid_parse(cid, &name, &callerid);
07566    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07567       /* Check for internal contexts and only */
07568       /* say extension when the call didn't come from an internal context in the list */
07569       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07570          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07571          if ((strcmp(cidinternalcontexts[i], context) == 0))
07572             break;
07573       }
07574       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07575          if (!res) {
07576             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07577             if (!ast_strlen_zero(prefile)) {
07578             /* See if we can find a recorded name for this person instead of their extension number */
07579                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07580                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07581                   if (!callback)
07582                      res = wait_file2(chan, vms, "vm-from");
07583                   res = ast_stream_and_wait(chan, prefile, "");
07584                } else {
07585                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07586                   /* Say "from extension" as one saying to sound smoother */
07587                   if (!callback)
07588                      res = wait_file2(chan, vms, "vm-from-extension");
07589                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07590                }
07591             }
07592          }
07593       } else if (!res) {
07594          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07595          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07596          if (!callback)
07597             res = wait_file2(chan, vms, "vm-from-phonenumber");
07598          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07599       }
07600    } else {
07601       /* Number unknown */
07602       ast_debug(1, "VM-CID: From an unknown number\n");
07603       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07604       res = wait_file2(chan, vms, "vm-unknown-caller");
07605    }
07606    return res;
07607 }
07608 
07609 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07610 {
07611    int res = 0;
07612    int durationm;
07613    int durations;
07614    /* Verify that we have a duration for the message */
07615    if (duration == NULL)
07616       return res;
07617 
07618    /* Convert from seconds to minutes */
07619    durations = atoi(duration);
07620    durationm = (durations / 60);
07621 
07622    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07623 
07624    if ((!res) && (durationm >= minduration)) {
07625       res = wait_file2(chan, vms, "vm-duration");
07626 
07627       /* POLISH syntax */
07628       if (!strncasecmp(chan->language, "pl", 2)) {
07629          div_t num = div(durationm, 10);
07630 
07631          if (durationm == 1) {
07632             res = ast_play_and_wait(chan, "digits/1z");
07633             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07634          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07635             if (num.rem == 2) {
07636                if (!num.quot) {
07637                   res = ast_play_and_wait(chan, "digits/2-ie");
07638                } else {
07639                   res = say_and_wait(chan, durationm - 2 , chan->language);
07640                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07641                }
07642             } else {
07643                res = say_and_wait(chan, durationm, chan->language);
07644             }
07645             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07646          } else {
07647             res = say_and_wait(chan, durationm, chan->language);
07648             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07649          }
07650       /* DEFAULT syntax */
07651       } else {
07652          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07653          res = wait_file2(chan, vms, "vm-minutes");
07654       }
07655    }
07656    return res;
07657 }
07658 
07659 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07660 {
07661    int res = 0;
07662    char filename[256], *cid;
07663    const char *origtime, *context, *category, *duration, *flag;
07664    struct ast_config *msg_cfg;
07665    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07666 
07667    vms->starting = 0;
07668    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07669    adsi_message(chan, vms);
07670    if (!vms->curmsg) {
07671       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07672    } else if (vms->curmsg == vms->lastmsg) {
07673       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07674    }
07675 
07676    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07677    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07678    msg_cfg = ast_config_load(filename, config_flags);
07679    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07680       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07681       return 0;
07682    }
07683    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07684 
07685    /* Play the word urgent if we are listening to urgent messages */
07686    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07687       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07688    }
07689 
07690    if (!res) {
07691       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07692       /* POLISH syntax */
07693       if (!strncasecmp(chan->language, "pl", 2)) {
07694          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07695             int ten, one;
07696             char nextmsg[256];
07697             ten = (vms->curmsg + 1) / 10;
07698             one = (vms->curmsg + 1) % 10;
07699 
07700             if (vms->curmsg < 20) {
07701                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07702                res = wait_file2(chan, vms, nextmsg);
07703             } else {
07704                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07705                res = wait_file2(chan, vms, nextmsg);
07706                if (one > 0) {
07707                   if (!res) {
07708                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07709                      res = wait_file2(chan, vms, nextmsg);
07710                   }
07711                }
07712             }
07713          }
07714          if (!res)
07715             res = wait_file2(chan, vms, "vm-message");
07716       /* HEBREW syntax */
07717       } else if (!strncasecmp(chan->language, "he", 2)) {
07718          if (!vms->curmsg) {
07719             res = wait_file2(chan, vms, "vm-message");
07720             res = wait_file2(chan, vms, "vm-first");
07721          } else if (vms->curmsg == vms->lastmsg) {
07722             res = wait_file2(chan, vms, "vm-message");
07723             res = wait_file2(chan, vms, "vm-last");
07724          } else {
07725             res = wait_file2(chan, vms, "vm-message");
07726             res = wait_file2(chan, vms, "vm-number");
07727             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07728          }
07729       /* VIETNAMESE syntax */
07730       } else if (!strncasecmp(chan->language, "vi", 2)) {
07731          if (!vms->curmsg) {
07732             res = wait_file2(chan, vms, "vm-message");
07733             res = wait_file2(chan, vms, "vm-first");
07734          } else if (vms->curmsg == vms->lastmsg) {
07735             res = wait_file2(chan, vms, "vm-message");
07736             res = wait_file2(chan, vms, "vm-last");
07737          } else {
07738             res = wait_file2(chan, vms, "vm-message");
07739             res = wait_file2(chan, vms, "vm-number");
07740             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07741          }
07742       } else {
07743          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07744             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07745          } else { /* DEFAULT syntax */
07746             res = wait_file2(chan, vms, "vm-message");
07747          }
07748          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07749             if (!res) {
07750                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07751                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07752             }
07753          }
07754       }
07755    }
07756 
07757    if (!msg_cfg) {
07758       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07759       return 0;
07760    }
07761 
07762    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07763       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07764       DISPOSE(vms->curdir, vms->curmsg);
07765       ast_config_destroy(msg_cfg);
07766       return 0;
07767    }
07768 
07769    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07770    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07771    category = ast_variable_retrieve(msg_cfg, "message", "category");
07772 
07773    context = ast_variable_retrieve(msg_cfg, "message", "context");
07774    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07775       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07776    if (!res) {
07777       res = play_message_category(chan, category);
07778    }
07779    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07780       res = play_message_datetime(chan, vmu, origtime, filename);
07781    }
07782    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07783       res = play_message_callerid(chan, vms, cid, context, 0);
07784    }
07785    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07786       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07787    }
07788    /* Allow pressing '1' to skip envelope / callerid */
07789    if (res == '1') {
07790       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07791       res = 0;
07792    }
07793    ast_config_destroy(msg_cfg);
07794 
07795    if (!res) {
07796       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07797 #ifdef IMAP_STORAGE
07798       ast_mutex_lock(&vms->lock);
07799 #endif
07800       vms->heard[vms->curmsg] = 1;
07801 #ifdef IMAP_STORAGE
07802       ast_mutex_unlock(&vms->lock);
07803       /*IMAP storage stores any prepended message from a forward
07804        * as a separate file from the rest of the message
07805        */
07806       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07807          wait_file(chan, vms, vms->introfn);
07808       }
07809 #endif
07810       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07811          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07812          res = 0;
07813       }
07814       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07815    }
07816    DISPOSE(vms->curdir, vms->curmsg);
07817    return res;
07818 }
07819 
07820 #ifdef IMAP_STORAGE
07821 static int imap_remove_file(char *dir, int msgnum)
07822 {
07823    char fn[PATH_MAX];
07824    char full_fn[PATH_MAX];
07825    char intro[PATH_MAX] = {0,};
07826    
07827    if (msgnum > -1) {
07828       make_file(fn, sizeof(fn), dir, msgnum);
07829       snprintf(intro, sizeof(intro), "%sintro", fn);
07830    } else
07831       ast_copy_string(fn, dir, sizeof(fn));
07832    
07833    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07834       ast_filedelete(fn, NULL);
07835       if (!ast_strlen_zero(intro)) {
07836          ast_filedelete(intro, NULL);
07837       }
07838       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07839       unlink(full_fn);
07840    }
07841    return 0;
07842 }
07843 
07844 
07845 
07846 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07847 {
07848    char *file, *filename;
07849    char *attachment;
07850    char arg[10];
07851    int i;
07852    BODY* body;
07853 
07854    file = strrchr(ast_strdupa(dir), '/');
07855    if (file) {
07856       *file++ = '\0';
07857    } else {
07858       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07859       return -1;
07860    }
07861 
07862    ast_mutex_lock(&vms->lock);
07863    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07864       mail_fetchstructure(vms->mailstream, i + 1, &body);
07865       /* We have the body, now we extract the file name of the first attachment. */
07866       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07867          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07868       } else {
07869          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07870          ast_mutex_unlock(&vms->lock);
07871          return -1;
07872       }
07873       filename = strsep(&attachment, ".");
07874       if (!strcmp(filename, file)) {
07875          sprintf(arg, "%d", i + 1);
07876          mail_setflag(vms->mailstream, arg, "\\DELETED");
07877       }
07878    }
07879    mail_expunge(vms->mailstream);
07880    ast_mutex_unlock(&vms->lock);
07881    return 0;
07882 }
07883 
07884 #elif !defined(IMAP_STORAGE)
07885 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07886 {
07887    int count_msg, last_msg;
07888 
07889    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07890 
07891    /* Rename the member vmbox HERE so that we don't try to return before
07892     * we know what's going on.
07893     */
07894    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07895 
07896    /* Faster to make the directory than to check if it exists. */
07897    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07898 
07899    /* traverses directory using readdir (or select query for ODBC) */
07900    count_msg = count_messages(vmu, vms->curdir);
07901    if (count_msg < 0) {
07902       return count_msg;
07903    } else {
07904       vms->lastmsg = count_msg - 1;
07905    }
07906 
07907    if (vm_allocate_dh(vms, vmu, count_msg)) {
07908       return -1;
07909    }
07910 
07911    /*
07912    The following test is needed in case sequencing gets messed up.
07913    There appears to be more than one way to mess up sequence, so
07914    we will not try to find all of the root causes--just fix it when
07915    detected.
07916    */
07917 
07918    if (vm_lock_path(vms->curdir)) {
07919       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07920       return ERROR_LOCK_PATH;
07921    }
07922 
07923    /* for local storage, checks directory for messages up to maxmsg limit */
07924    last_msg = last_message_index(vmu, vms->curdir);
07925    ast_unlock_path(vms->curdir);
07926 
07927    if (last_msg < -1) {
07928       return last_msg;
07929    } else if (vms->lastmsg != last_msg) {
07930       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);
07931       resequence_mailbox(vmu, vms->curdir, count_msg);
07932    }
07933 
07934    return 0;
07935 }
07936 #endif
07937 
07938 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07939 {
07940    int x = 0;
07941    int last_msg_idx = 0;
07942 
07943 #ifndef IMAP_STORAGE
07944    int res = 0, nummsg;
07945    char fn2[PATH_MAX];
07946 #endif
07947 
07948    if (vms->lastmsg <= -1) {
07949       goto done;
07950    }
07951 
07952    vms->curmsg = -1;
07953 #ifndef IMAP_STORAGE
07954    /* Get the deleted messages fixed */
07955    if (vm_lock_path(vms->curdir)) {
07956       return ERROR_LOCK_PATH;
07957    }
07958 
07959    /* update count as message may have arrived while we've got mailbox open */
07960    last_msg_idx = last_message_index(vmu, vms->curdir);
07961    if (last_msg_idx != vms->lastmsg) {
07962       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07963    }
07964 
07965    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07966    for (x = 0; x < last_msg_idx + 1; x++) {
07967       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07968          /* Save this message.  It's not in INBOX or hasn't been heard */
07969          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07970          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07971             break;
07972          }
07973          vms->curmsg++;
07974          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07975          if (strcmp(vms->fn, fn2)) {
07976             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07977          }
07978       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07979          /* Move to old folder before deleting */
07980          res = save_to_folder(vmu, vms, x, 1);
07981          if (res == ERROR_LOCK_PATH) {
07982             /* If save failed do not delete the message */
07983             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07984             vms->deleted[x] = 0;
07985             vms->heard[x] = 0;
07986             --x;
07987          }
07988       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07989          /* Move to deleted folder */
07990          res = save_to_folder(vmu, vms, x, 10);
07991          if (res == ERROR_LOCK_PATH) {
07992             /* If save failed do not delete the message */
07993             vms->deleted[x] = 0;
07994             vms->heard[x] = 0;
07995             --x;
07996          }
07997       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07998          /* If realtime storage enabled - we should explicitly delete this message,
07999          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08000          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08001          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08002             DELETE(vms->curdir, x, vms->fn, vmu);
08003          }
08004       }
08005    }
08006 
08007    /* Delete ALL remaining messages */
08008    nummsg = x - 1;
08009    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08010       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08011       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08012          DELETE(vms->curdir, x, vms->fn, vmu);
08013       }
08014    }
08015    ast_unlock_path(vms->curdir);
08016 #else /* defined(IMAP_STORAGE) */
08017    ast_mutex_lock(&vms->lock);
08018    if (vms->deleted) {
08019       /* Since we now expunge after each delete, deleting in reverse order
08020        * ensures that no reordering occurs between each step. */
08021       last_msg_idx = vms->dh_arraysize;
08022       for (x = last_msg_idx - 1; x >= 0; x--) {
08023          if (vms->deleted[x]) {
08024             ast_debug(3, "IMAP delete of %d\n", x);
08025             DELETE(vms->curdir, x, vms->fn, vmu);
08026          }
08027       }
08028    }
08029 #endif
08030 
08031 done:
08032    if (vms->deleted) {
08033       ast_free(vms->deleted);
08034       vms->deleted = NULL;
08035    }
08036    if (vms->heard) {
08037       ast_free(vms->heard);
08038       vms->heard = NULL;
08039    }
08040    vms->dh_arraysize = 0;
08041 #ifdef IMAP_STORAGE
08042    ast_mutex_unlock(&vms->lock);
08043 #endif
08044 
08045    return 0;
08046 }
08047 
08048 /* In Greek even though we CAN use a syntax like "friends messages"
08049  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08050  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08051  * syntax for the above three categories which is more elegant.
08052  */
08053 
08054 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08055 {
08056    int cmd;
08057    char *buf;
08058 
08059    buf = alloca(strlen(box) + 2);
08060    strcpy(buf, box);
08061    strcat(buf, "s");
08062 
08063    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08064       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08065       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08066    } else {
08067       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08068       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08069    }
08070 }
08071 
08072 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08073 {
08074    int cmd;
08075 
08076    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08077       if (!strcasecmp(box, "vm-INBOX"))
08078          cmd = ast_play_and_wait(chan, "vm-new-e");
08079       else
08080          cmd = ast_play_and_wait(chan, "vm-old-e");
08081       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08082    } else {
08083       cmd = ast_play_and_wait(chan, "vm-messages");
08084       return cmd ? cmd : ast_play_and_wait(chan, box);
08085    }
08086 }
08087 
08088 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08089 {
08090    int cmd;
08091 
08092    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08093       cmd = ast_play_and_wait(chan, "vm-messages");
08094       return cmd ? cmd : ast_play_and_wait(chan, box);
08095    } else {
08096       cmd = ast_play_and_wait(chan, box);
08097       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08098    }
08099 }
08100 
08101 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08102 {
08103    int cmd;
08104 
08105    if (  !strncasecmp(chan->language, "it", 2) ||
08106         !strncasecmp(chan->language, "es", 2) ||
08107         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08108       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08109       return cmd ? cmd : ast_play_and_wait(chan, box);
08110    } else if (!strncasecmp(chan->language, "gr", 2)) {
08111       return vm_play_folder_name_gr(chan, box);
08112    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08113       return ast_play_and_wait(chan, box);
08114    } else if (!strncasecmp(chan->language, "pl", 2)) {
08115       return vm_play_folder_name_pl(chan, box);
08116    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08117       return vm_play_folder_name_ua(chan, box);
08118    } else if (!strncasecmp(chan->language, "vi", 2)) {
08119       return ast_play_and_wait(chan, box);
08120    } else {  /* Default English */
08121       cmd = ast_play_and_wait(chan, box);
08122       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08123    }
08124 }
08125 
08126 /* GREEK SYNTAX
08127    In greek the plural for old/new is
08128    different so we need the following files
08129    We also need vm-denExeteMynhmata because
08130    this syntax is different.
08131 
08132    -> vm-Olds.wav : "Palia"
08133    -> vm-INBOXs.wav : "Nea"
08134    -> vm-denExeteMynhmata : "den exete mynhmata"
08135 */
08136 
08137 
08138 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08139 {
08140    int res = 0;
08141 
08142    if (vms->newmessages) {
08143       res = ast_play_and_wait(chan, "vm-youhave");
08144       if (!res) 
08145          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08146       if (!res) {
08147          if ((vms->newmessages == 1)) {
08148             res = ast_play_and_wait(chan, "vm-INBOX");
08149             if (!res)
08150                res = ast_play_and_wait(chan, "vm-message");
08151          } else {
08152             res = ast_play_and_wait(chan, "vm-INBOXs");
08153             if (!res)
08154                res = ast_play_and_wait(chan, "vm-messages");
08155          }
08156       }
08157    } else if (vms->oldmessages){
08158       res = ast_play_and_wait(chan, "vm-youhave");
08159       if (!res)
08160          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08161       if ((vms->oldmessages == 1)){
08162          res = ast_play_and_wait(chan, "vm-Old");
08163          if (!res)
08164             res = ast_play_and_wait(chan, "vm-message");
08165       } else {
08166          res = ast_play_and_wait(chan, "vm-Olds");
08167          if (!res)
08168             res = ast_play_and_wait(chan, "vm-messages");
08169       }
08170    } else if (!vms->oldmessages && !vms->newmessages) 
08171       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08172    return res;
08173 }
08174 
08175 /* Version of vm_intro() designed to work for many languages.
08176  *
08177  * It is hoped that this function can prevent the proliferation of 
08178  * language-specific vm_intro() functions and in time replace the language-
08179  * specific functions which already exist.  An examination of the language-
08180  * specific functions revealed that they all corrected the same deficiencies
08181  * in vm_intro_en() (which was the default function). Namely:
08182  *
08183  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08184  *     wording of the voicemail greeting hides this problem.  For example,
08185  *     vm-INBOX contains only the word "new".  This means that both of these
08186  *     sequences produce valid utterances:
08187  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08188  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08189  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08190  *     in many languages) the first utterance becomes "you have 1 the new message".
08191  *  2) The function contains hardcoded rules for pluralizing the word "message".
08192  *     These rules are correct for English, but not for many other languages.
08193  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08194  *     required in many languages.
08195  *  4) The gender of the word for "message" is not specified. This is a problem
08196  *     because in many languages the gender of the number in phrases such
08197  *     as "you have one new message" must match the gender of the word
08198  *     meaning "message".
08199  *
08200  * Fixing these problems for each new language has meant duplication of effort.
08201  * This new function solves the problems in the following general ways:
08202  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08203  *     and vm-Old respectively for those languages where it makes sense.
08204  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08205  *     on vm-message.
08206  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08207  *     prefix on vm-new and vm-old (none for English).
08208  *  4) Pass the gender of the language's word for "message" as an agument to
08209  *     this function which is can in turn pass on to the functions which 
08210  *     say numbers and put endings on nounds and adjectives.
08211  *
08212  * All languages require these messages:
08213  *  vm-youhave    "You have..."
08214  *  vm-and     "and"
08215  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08216  *
08217  * To use it for English, you will need these additional sound files:
08218  *  vm-new     "new"
08219  *  vm-message    "message", singular
08220  *  vm-messages      "messages", plural
08221  *
08222  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08223  *
08224  *  vm-newn    "novoye" (singular, neuter)
08225  *  vm-newx    "novikh" (counting plural form, genative plural)
08226  *  vm-message    "sobsheniye" (singular form)
08227  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08228  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08229  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08230  *  digits/2n     "dva" (neuter singular)
08231  */
08232 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08233 {
08234    int res;
08235    int lastnum = 0;
08236 
08237    res = ast_play_and_wait(chan, "vm-youhave");
08238 
08239    if (!res && vms->newmessages) {
08240       lastnum = vms->newmessages;
08241 
08242       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08243          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08244       }
08245 
08246       if (!res && vms->oldmessages) {
08247          res = ast_play_and_wait(chan, "vm-and");
08248       }
08249    }
08250 
08251    if (!res && vms->oldmessages) {
08252       lastnum = vms->oldmessages;
08253 
08254       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08255          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08256       }
08257    }
08258 
08259    if (!res) {
08260       if (lastnum == 0) {
08261          res = ast_play_and_wait(chan, "vm-no");
08262       }
08263       if (!res) {
08264          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08265       }
08266    }
08267 
08268    return res;
08269 }
08270 
08271 /* Default Hebrew syntax */
08272 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08273 {
08274    int res = 0;
08275 
08276    /* Introduce messages they have */
08277    if (!res) {
08278       if ((vms->newmessages) || (vms->oldmessages)) {
08279          res = ast_play_and_wait(chan, "vm-youhave");
08280       }
08281       /*
08282        * The word "shtei" refers to the number 2 in hebrew when performing a count
08283        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08284        * an element, this is one of them.
08285        */
08286       if (vms->newmessages) {
08287          if (!res) {
08288             if (vms->newmessages == 1) {
08289                res = ast_play_and_wait(chan, "vm-INBOX1");
08290             } else {
08291                if (vms->newmessages == 2) {
08292                   res = ast_play_and_wait(chan, "vm-shtei");
08293                } else {
08294                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08295                }
08296                res = ast_play_and_wait(chan, "vm-INBOX");
08297             }
08298          }
08299          if (vms->oldmessages && !res) {
08300             res = ast_play_and_wait(chan, "vm-and");
08301             if (vms->oldmessages == 1) {
08302                res = ast_play_and_wait(chan, "vm-Old1");
08303             } else {
08304                if (vms->oldmessages == 2) {
08305                   res = ast_play_and_wait(chan, "vm-shtei");
08306                } else {
08307                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08308                }
08309                res = ast_play_and_wait(chan, "vm-Old");
08310             }
08311          }
08312       }
08313       if (!res && vms->oldmessages && !vms->newmessages) {
08314          if (!res) {
08315             if (vms->oldmessages == 1) {
08316                res = ast_play_and_wait(chan, "vm-Old1");
08317             } else {
08318                if (vms->oldmessages == 2) {
08319                   res = ast_play_and_wait(chan, "vm-shtei");
08320                } else {
08321                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08322                }
08323                res = ast_play_and_wait(chan, "vm-Old");
08324             }
08325          }
08326       }
08327       if (!res) {
08328          if (!vms->oldmessages && !vms->newmessages) {
08329             if (!res) {
08330                res = ast_play_and_wait(chan, "vm-nomessages");
08331             }
08332          }
08333       }
08334    }
08335    return res;
08336 }
08337    
08338 /* Default English syntax */
08339 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08340 {
08341    int res;
08342 
08343    /* Introduce messages they have */
08344    res = ast_play_and_wait(chan, "vm-youhave");
08345    if (!res) {
08346       if (vms->urgentmessages) {
08347          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08348          if (!res)
08349             res = ast_play_and_wait(chan, "vm-Urgent");
08350          if ((vms->oldmessages || vms->newmessages) && !res) {
08351             res = ast_play_and_wait(chan, "vm-and");
08352          } else if (!res) {
08353             if ((vms->urgentmessages == 1))
08354                res = ast_play_and_wait(chan, "vm-message");
08355             else
08356                res = ast_play_and_wait(chan, "vm-messages");
08357          }
08358       }
08359       if (vms->newmessages) {
08360          res = say_and_wait(chan, vms->newmessages, chan->language);
08361          if (!res)
08362             res = ast_play_and_wait(chan, "vm-INBOX");
08363          if (vms->oldmessages && !res)
08364             res = ast_play_and_wait(chan, "vm-and");
08365          else if (!res) {
08366             if ((vms->newmessages == 1))
08367                res = ast_play_and_wait(chan, "vm-message");
08368             else
08369                res = ast_play_and_wait(chan, "vm-messages");
08370          }
08371             
08372       }
08373       if (!res && vms->oldmessages) {
08374          res = say_and_wait(chan, vms->oldmessages, chan->language);
08375          if (!res)
08376             res = ast_play_and_wait(chan, "vm-Old");
08377          if (!res) {
08378             if (vms->oldmessages == 1)
08379                res = ast_play_and_wait(chan, "vm-message");
08380             else
08381                res = ast_play_and_wait(chan, "vm-messages");
08382          }
08383       }
08384       if (!res) {
08385          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08386             res = ast_play_and_wait(chan, "vm-no");
08387             if (!res)
08388                res = ast_play_and_wait(chan, "vm-messages");
08389          }
08390       }
08391    }
08392    return res;
08393 }
08394 
08395 /* ITALIAN syntax */
08396 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08397 {
08398    /* Introduce messages they have */
08399    int res;
08400    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08401       res = ast_play_and_wait(chan, "vm-no") ||
08402          ast_play_and_wait(chan, "vm-message");
08403    else
08404       res = ast_play_and_wait(chan, "vm-youhave");
08405    if (!res && vms->newmessages) {
08406       res = (vms->newmessages == 1) ?
08407          ast_play_and_wait(chan, "digits/un") ||
08408          ast_play_and_wait(chan, "vm-nuovo") ||
08409          ast_play_and_wait(chan, "vm-message") :
08410          /* 2 or more new messages */
08411          say_and_wait(chan, vms->newmessages, chan->language) ||
08412          ast_play_and_wait(chan, "vm-nuovi") ||
08413          ast_play_and_wait(chan, "vm-messages");
08414       if (!res && vms->oldmessages)
08415          res = ast_play_and_wait(chan, "vm-and");
08416    }
08417    if (!res && vms->oldmessages) {
08418       res = (vms->oldmessages == 1) ?
08419          ast_play_and_wait(chan, "digits/un") ||
08420          ast_play_and_wait(chan, "vm-vecchio") ||
08421          ast_play_and_wait(chan, "vm-message") :
08422          /* 2 or more old messages */
08423          say_and_wait(chan, vms->oldmessages, chan->language) ||
08424          ast_play_and_wait(chan, "vm-vecchi") ||
08425          ast_play_and_wait(chan, "vm-messages");
08426    }
08427    return res;
08428 }
08429 
08430 /* POLISH syntax */
08431 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08432 {
08433    /* Introduce messages they have */
08434    int res;
08435    div_t num;
08436 
08437    if (!vms->oldmessages && !vms->newmessages) {
08438       res = ast_play_and_wait(chan, "vm-no");
08439       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08440       return res;
08441    } else {
08442       res = ast_play_and_wait(chan, "vm-youhave");
08443    }
08444 
08445    if (vms->newmessages) {
08446       num = div(vms->newmessages, 10);
08447       if (vms->newmessages == 1) {
08448          res = ast_play_and_wait(chan, "digits/1-a");
08449          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08450          res = res ? res : ast_play_and_wait(chan, "vm-message");
08451       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08452          if (num.rem == 2) {
08453             if (!num.quot) {
08454                res = ast_play_and_wait(chan, "digits/2-ie");
08455             } else {
08456                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08457                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08458             }
08459          } else {
08460             res = say_and_wait(chan, vms->newmessages, chan->language);
08461          }
08462          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08463          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08464       } else {
08465          res = say_and_wait(chan, vms->newmessages, chan->language);
08466          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08467          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08468       }
08469       if (!res && vms->oldmessages)
08470          res = ast_play_and_wait(chan, "vm-and");
08471    }
08472    if (!res && vms->oldmessages) {
08473       num = div(vms->oldmessages, 10);
08474       if (vms->oldmessages == 1) {
08475          res = ast_play_and_wait(chan, "digits/1-a");
08476          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08477          res = res ? res : ast_play_and_wait(chan, "vm-message");
08478       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08479          if (num.rem == 2) {
08480             if (!num.quot) {
08481                res = ast_play_and_wait(chan, "digits/2-ie");
08482             } else {
08483                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08484                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08485             }
08486          } else {
08487             res = say_and_wait(chan, vms->oldmessages, chan->language);
08488          }
08489          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08490          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08491       } else {
08492          res = say_and_wait(chan, vms->oldmessages, chan->language);
08493          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08494          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08495       }
08496    }
08497 
08498    return res;
08499 }
08500 
08501 /* SWEDISH syntax */
08502 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08503 {
08504    /* Introduce messages they have */
08505    int res;
08506 
08507    res = ast_play_and_wait(chan, "vm-youhave");
08508    if (res)
08509       return res;
08510 
08511    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08512       res = ast_play_and_wait(chan, "vm-no");
08513       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08514       return res;
08515    }
08516 
08517    if (vms->newmessages) {
08518       if ((vms->newmessages == 1)) {
08519          res = ast_play_and_wait(chan, "digits/ett");
08520          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08521          res = res ? res : ast_play_and_wait(chan, "vm-message");
08522       } else {
08523          res = say_and_wait(chan, vms->newmessages, chan->language);
08524          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08525          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08526       }
08527       if (!res && vms->oldmessages)
08528          res = ast_play_and_wait(chan, "vm-and");
08529    }
08530    if (!res && vms->oldmessages) {
08531       if (vms->oldmessages == 1) {
08532          res = ast_play_and_wait(chan, "digits/ett");
08533          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08534          res = res ? res : ast_play_and_wait(chan, "vm-message");
08535       } else {
08536          res = say_and_wait(chan, vms->oldmessages, chan->language);
08537          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08538          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08539       }
08540    }
08541 
08542    return res;
08543 }
08544 
08545 /* NORWEGIAN syntax */
08546 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08547 {
08548    /* Introduce messages they have */
08549    int res;
08550 
08551    res = ast_play_and_wait(chan, "vm-youhave");
08552    if (res)
08553       return res;
08554 
08555    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08556       res = ast_play_and_wait(chan, "vm-no");
08557       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08558       return res;
08559    }
08560 
08561    if (vms->newmessages) {
08562       if ((vms->newmessages == 1)) {
08563          res = ast_play_and_wait(chan, "digits/1");
08564          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08565          res = res ? res : ast_play_and_wait(chan, "vm-message");
08566       } else {
08567          res = say_and_wait(chan, vms->newmessages, chan->language);
08568          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08569          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08570       }
08571       if (!res && vms->oldmessages)
08572          res = ast_play_and_wait(chan, "vm-and");
08573    }
08574    if (!res && vms->oldmessages) {
08575       if (vms->oldmessages == 1) {
08576          res = ast_play_and_wait(chan, "digits/1");
08577          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08578          res = res ? res : ast_play_and_wait(chan, "vm-message");
08579       } else {
08580          res = say_and_wait(chan, vms->oldmessages, chan->language);
08581          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08582          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08583       }
08584    }
08585 
08586    return res;
08587 }
08588 
08589 /* GERMAN syntax */
08590 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08591 {
08592    /* Introduce messages they have */
08593    int res;
08594    res = ast_play_and_wait(chan, "vm-youhave");
08595    if (!res) {
08596       if (vms->newmessages) {
08597          if ((vms->newmessages == 1))
08598             res = ast_play_and_wait(chan, "digits/1F");
08599          else
08600             res = say_and_wait(chan, vms->newmessages, chan->language);
08601          if (!res)
08602             res = ast_play_and_wait(chan, "vm-INBOX");
08603          if (vms->oldmessages && !res)
08604             res = ast_play_and_wait(chan, "vm-and");
08605          else if (!res) {
08606             if ((vms->newmessages == 1))
08607                res = ast_play_and_wait(chan, "vm-message");
08608             else
08609                res = ast_play_and_wait(chan, "vm-messages");
08610          }
08611             
08612       }
08613       if (!res && vms->oldmessages) {
08614          if (vms->oldmessages == 1)
08615             res = ast_play_and_wait(chan, "digits/1F");
08616          else
08617             res = say_and_wait(chan, vms->oldmessages, chan->language);
08618          if (!res)
08619             res = ast_play_and_wait(chan, "vm-Old");
08620          if (!res) {
08621             if (vms->oldmessages == 1)
08622                res = ast_play_and_wait(chan, "vm-message");
08623             else
08624                res = ast_play_and_wait(chan, "vm-messages");
08625          }
08626       }
08627       if (!res) {
08628          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08629             res = ast_play_and_wait(chan, "vm-no");
08630             if (!res)
08631                res = ast_play_and_wait(chan, "vm-messages");
08632          }
08633       }
08634    }
08635    return res;
08636 }
08637 
08638 /* SPANISH syntax */
08639 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08640 {
08641    /* Introduce messages they have */
08642    int res;
08643    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08644       res = ast_play_and_wait(chan, "vm-youhaveno");
08645       if (!res)
08646          res = ast_play_and_wait(chan, "vm-messages");
08647    } else {
08648       res = ast_play_and_wait(chan, "vm-youhave");
08649    }
08650    if (!res) {
08651       if (vms->newmessages) {
08652          if (!res) {
08653             if ((vms->newmessages == 1)) {
08654                res = ast_play_and_wait(chan, "digits/1M");
08655                if (!res)
08656                   res = ast_play_and_wait(chan, "vm-message");
08657                if (!res)
08658                   res = ast_play_and_wait(chan, "vm-INBOXs");
08659             } else {
08660                res = say_and_wait(chan, vms->newmessages, chan->language);
08661                if (!res)
08662                   res = ast_play_and_wait(chan, "vm-messages");
08663                if (!res)
08664                   res = ast_play_and_wait(chan, "vm-INBOX");
08665             }
08666          }
08667          if (vms->oldmessages && !res)
08668             res = ast_play_and_wait(chan, "vm-and");
08669       }
08670       if (vms->oldmessages) {
08671          if (!res) {
08672             if (vms->oldmessages == 1) {
08673                res = ast_play_and_wait(chan, "digits/1M");
08674                if (!res)
08675                   res = ast_play_and_wait(chan, "vm-message");
08676                if (!res)
08677                   res = ast_play_and_wait(chan, "vm-Olds");
08678             } else {
08679                res = say_and_wait(chan, vms->oldmessages, chan->language);
08680                if (!res)
08681                   res = ast_play_and_wait(chan, "vm-messages");
08682                if (!res)
08683                   res = ast_play_and_wait(chan, "vm-Old");
08684             }
08685          }
08686       }
08687    }
08688 return res;
08689 }
08690 
08691 /* BRAZILIAN PORTUGUESE syntax */
08692 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08693    /* Introduce messages they have */
08694    int res;
08695    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08696       res = ast_play_and_wait(chan, "vm-nomessages");
08697       return res;
08698    } else {
08699       res = ast_play_and_wait(chan, "vm-youhave");
08700    }
08701    if (vms->newmessages) {
08702       if (!res)
08703          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08704       if ((vms->newmessages == 1)) {
08705          if (!res)
08706             res = ast_play_and_wait(chan, "vm-message");
08707          if (!res)
08708             res = ast_play_and_wait(chan, "vm-INBOXs");
08709       } else {
08710          if (!res)
08711             res = ast_play_and_wait(chan, "vm-messages");
08712          if (!res)
08713             res = ast_play_and_wait(chan, "vm-INBOX");
08714       }
08715       if (vms->oldmessages && !res)
08716          res = ast_play_and_wait(chan, "vm-and");
08717    }
08718    if (vms->oldmessages) {
08719       if (!res)
08720          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08721       if (vms->oldmessages == 1) {
08722          if (!res)
08723             res = ast_play_and_wait(chan, "vm-message");
08724          if (!res)
08725             res = ast_play_and_wait(chan, "vm-Olds");
08726       } else {
08727          if (!res)
08728             res = ast_play_and_wait(chan, "vm-messages");
08729          if (!res)
08730             res = ast_play_and_wait(chan, "vm-Old");
08731       }
08732    }
08733    return res;
08734 }
08735 
08736 /* FRENCH syntax */
08737 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08738 {
08739    /* Introduce messages they have */
08740    int res;
08741    res = ast_play_and_wait(chan, "vm-youhave");
08742    if (!res) {
08743       if (vms->newmessages) {
08744          res = say_and_wait(chan, vms->newmessages, chan->language);
08745          if (!res)
08746             res = ast_play_and_wait(chan, "vm-INBOX");
08747          if (vms->oldmessages && !res)
08748             res = ast_play_and_wait(chan, "vm-and");
08749          else if (!res) {
08750             if ((vms->newmessages == 1))
08751                res = ast_play_and_wait(chan, "vm-message");
08752             else
08753                res = ast_play_and_wait(chan, "vm-messages");
08754          }
08755             
08756       }
08757       if (!res && vms->oldmessages) {
08758          res = say_and_wait(chan, vms->oldmessages, chan->language);
08759          if (!res)
08760             res = ast_play_and_wait(chan, "vm-Old");
08761          if (!res) {
08762             if (vms->oldmessages == 1)
08763                res = ast_play_and_wait(chan, "vm-message");
08764             else
08765                res = ast_play_and_wait(chan, "vm-messages");
08766          }
08767       }
08768       if (!res) {
08769          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08770             res = ast_play_and_wait(chan, "vm-no");
08771             if (!res)
08772                res = ast_play_and_wait(chan, "vm-messages");
08773          }
08774       }
08775    }
08776    return res;
08777 }
08778 
08779 /* DUTCH syntax */
08780 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08781 {
08782    /* Introduce messages they have */
08783    int res;
08784    res = ast_play_and_wait(chan, "vm-youhave");
08785    if (!res) {
08786       if (vms->newmessages) {
08787          res = say_and_wait(chan, vms->newmessages, chan->language);
08788          if (!res) {
08789             if (vms->newmessages == 1)
08790                res = ast_play_and_wait(chan, "vm-INBOXs");
08791             else
08792                res = ast_play_and_wait(chan, "vm-INBOX");
08793          }
08794          if (vms->oldmessages && !res)
08795             res = ast_play_and_wait(chan, "vm-and");
08796          else if (!res) {
08797             if ((vms->newmessages == 1))
08798                res = ast_play_and_wait(chan, "vm-message");
08799             else
08800                res = ast_play_and_wait(chan, "vm-messages");
08801          }
08802             
08803       }
08804       if (!res && vms->oldmessages) {
08805          res = say_and_wait(chan, vms->oldmessages, chan->language);
08806          if (!res) {
08807             if (vms->oldmessages == 1)
08808                res = ast_play_and_wait(chan, "vm-Olds");
08809             else
08810                res = ast_play_and_wait(chan, "vm-Old");
08811          }
08812          if (!res) {
08813             if (vms->oldmessages == 1)
08814                res = ast_play_and_wait(chan, "vm-message");
08815             else
08816                res = ast_play_and_wait(chan, "vm-messages");
08817          }
08818       }
08819       if (!res) {
08820          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08821             res = ast_play_and_wait(chan, "vm-no");
08822             if (!res)
08823                res = ast_play_and_wait(chan, "vm-messages");
08824          }
08825       }
08826    }
08827    return res;
08828 }
08829 
08830 /* PORTUGUESE syntax */
08831 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08832 {
08833    /* Introduce messages they have */
08834    int res;
08835    res = ast_play_and_wait(chan, "vm-youhave");
08836    if (!res) {
08837       if (vms->newmessages) {
08838          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08839          if (!res) {
08840             if ((vms->newmessages == 1)) {
08841                res = ast_play_and_wait(chan, "vm-message");
08842                if (!res)
08843                   res = ast_play_and_wait(chan, "vm-INBOXs");
08844             } else {
08845                res = ast_play_and_wait(chan, "vm-messages");
08846                if (!res)
08847                   res = ast_play_and_wait(chan, "vm-INBOX");
08848             }
08849          }
08850          if (vms->oldmessages && !res)
08851             res = ast_play_and_wait(chan, "vm-and");
08852       }
08853       if (!res && vms->oldmessages) {
08854          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08855          if (!res) {
08856             if (vms->oldmessages == 1) {
08857                res = ast_play_and_wait(chan, "vm-message");
08858                if (!res)
08859                   res = ast_play_and_wait(chan, "vm-Olds");
08860             } else {
08861                res = ast_play_and_wait(chan, "vm-messages");
08862                if (!res)
08863                   res = ast_play_and_wait(chan, "vm-Old");
08864             }
08865          }
08866       }
08867       if (!res) {
08868          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08869             res = ast_play_and_wait(chan, "vm-no");
08870             if (!res)
08871                res = ast_play_and_wait(chan, "vm-messages");
08872          }
08873       }
08874    }
08875    return res;
08876 }
08877 
08878 
08879 /* CZECH syntax */
08880 /* in czech there must be declension of word new and message
08881  * czech        : english        : czech      : english
08882  * --------------------------------------------------------
08883  * vm-youhave   : you have 
08884  * vm-novou     : one new        : vm-zpravu  : message
08885  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08886  * vm-novych    : 5-infinite new : vm-zprav   : messages
08887  * vm-starou   : one old
08888  * vm-stare     : 2-4 old 
08889  * vm-starych   : 5-infinite old
08890  * jednu        : one   - falling 4. 
08891  * vm-no        : no  ( no messages )
08892  */
08893 
08894 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08895 {
08896    int res;
08897    res = ast_play_and_wait(chan, "vm-youhave");
08898    if (!res) {
08899       if (vms->newmessages) {
08900          if (vms->newmessages == 1) {
08901             res = ast_play_and_wait(chan, "digits/jednu");
08902          } else {
08903             res = say_and_wait(chan, vms->newmessages, chan->language);
08904          }
08905          if (!res) {
08906             if ((vms->newmessages == 1))
08907                res = ast_play_and_wait(chan, "vm-novou");
08908             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08909                res = ast_play_and_wait(chan, "vm-nove");
08910             if (vms->newmessages > 4)
08911                res = ast_play_and_wait(chan, "vm-novych");
08912          }
08913          if (vms->oldmessages && !res)
08914             res = ast_play_and_wait(chan, "vm-and");
08915          else if (!res) {
08916             if ((vms->newmessages == 1))
08917                res = ast_play_and_wait(chan, "vm-zpravu");
08918             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08919                res = ast_play_and_wait(chan, "vm-zpravy");
08920             if (vms->newmessages > 4)
08921                res = ast_play_and_wait(chan, "vm-zprav");
08922          }
08923       }
08924       if (!res && vms->oldmessages) {
08925          res = say_and_wait(chan, vms->oldmessages, chan->language);
08926          if (!res) {
08927             if ((vms->oldmessages == 1))
08928                res = ast_play_and_wait(chan, "vm-starou");
08929             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08930                res = ast_play_and_wait(chan, "vm-stare");
08931             if (vms->oldmessages > 4)
08932                res = ast_play_and_wait(chan, "vm-starych");
08933          }
08934          if (!res) {
08935             if ((vms->oldmessages == 1))
08936                res = ast_play_and_wait(chan, "vm-zpravu");
08937             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08938                res = ast_play_and_wait(chan, "vm-zpravy");
08939             if (vms->oldmessages > 4)
08940                res = ast_play_and_wait(chan, "vm-zprav");
08941          }
08942       }
08943       if (!res) {
08944          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08945             res = ast_play_and_wait(chan, "vm-no");
08946             if (!res)
08947                res = ast_play_and_wait(chan, "vm-zpravy");
08948          }
08949       }
08950    }
08951    return res;
08952 }
08953 
08954 /* CHINESE (Taiwan) syntax */
08955 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08956 {
08957    int res;
08958    /* Introduce messages they have */
08959    res = ast_play_and_wait(chan, "vm-you");
08960 
08961    if (!res && vms->newmessages) {
08962       res = ast_play_and_wait(chan, "vm-have");
08963       if (!res)
08964          res = say_and_wait(chan, vms->newmessages, chan->language);
08965       if (!res)
08966          res = ast_play_and_wait(chan, "vm-tong");
08967       if (!res)
08968          res = ast_play_and_wait(chan, "vm-INBOX");
08969       if (vms->oldmessages && !res)
08970          res = ast_play_and_wait(chan, "vm-and");
08971       else if (!res) 
08972          res = ast_play_and_wait(chan, "vm-messages");
08973    }
08974    if (!res && vms->oldmessages) {
08975       res = ast_play_and_wait(chan, "vm-have");
08976       if (!res)
08977          res = say_and_wait(chan, vms->oldmessages, chan->language);
08978       if (!res)
08979          res = ast_play_and_wait(chan, "vm-tong");
08980       if (!res)
08981          res = ast_play_and_wait(chan, "vm-Old");
08982       if (!res)
08983          res = ast_play_and_wait(chan, "vm-messages");
08984    }
08985    if (!res && !vms->oldmessages && !vms->newmessages) {
08986       res = ast_play_and_wait(chan, "vm-haveno");
08987       if (!res)
08988          res = ast_play_and_wait(chan, "vm-messages");
08989    }
08990    return res;
08991 }
08992 
08993 /* Vietnamese syntax */
08994 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08995 {
08996    int res;
08997 
08998    /* Introduce messages they have */
08999    res = ast_play_and_wait(chan, "vm-youhave");
09000    if (!res) {
09001       if (vms->newmessages) {
09002          res = say_and_wait(chan, vms->newmessages, chan->language);
09003          if (!res)
09004             res = ast_play_and_wait(chan, "vm-INBOX");
09005          if (vms->oldmessages && !res)
09006             res = ast_play_and_wait(chan, "vm-and");
09007       }
09008       if (!res && vms->oldmessages) {
09009          res = say_and_wait(chan, vms->oldmessages, chan->language);
09010          if (!res)
09011             res = ast_play_and_wait(chan, "vm-Old");        
09012       }
09013       if (!res) {
09014          if (!vms->oldmessages && !vms->newmessages) {
09015             res = ast_play_and_wait(chan, "vm-no");
09016             if (!res)
09017                res = ast_play_and_wait(chan, "vm-message");
09018          }
09019       }
09020    }
09021    return res;
09022 }
09023 
09024 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09025 {
09026    char prefile[256];
09027    
09028    /* Notify the user that the temp greeting is set and give them the option to remove it */
09029    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09030    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09031       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09032       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09033          ast_play_and_wait(chan, "vm-tempgreetactive");
09034       }
09035       DISPOSE(prefile, -1);
09036    }
09037 
09038    /* Play voicemail intro - syntax is different for different languages */
09039    if (0) {
09040       return 0;
09041    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09042       return vm_intro_cs(chan, vms);
09043    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09044       static int deprecation_warning = 0;
09045       if (deprecation_warning++ % 10 == 0) {
09046          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09047       }
09048       return vm_intro_cs(chan, vms);
09049    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09050       return vm_intro_de(chan, vms);
09051    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09052       return vm_intro_es(chan, vms);
09053    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09054       return vm_intro_fr(chan, vms);
09055    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09056       return vm_intro_gr(chan, vms);
09057    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09058       return vm_intro_he(chan, vms);
09059    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09060       return vm_intro_it(chan, vms);
09061    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09062       return vm_intro_nl(chan, vms);
09063    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09064       return vm_intro_no(chan, vms);
09065    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09066       return vm_intro_pl(chan, vms);
09067    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09068       return vm_intro_pt_BR(chan, vms);
09069    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09070       return vm_intro_pt(chan, vms);
09071    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09072       return vm_intro_multilang(chan, vms, "n");
09073    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09074       return vm_intro_se(chan, vms);
09075    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09076       return vm_intro_multilang(chan, vms, "n");
09077    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09078       return vm_intro_vi(chan, vms);
09079    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09080       return vm_intro_zh(chan, vms);
09081    } else {                                             /* Default to ENGLISH */
09082       return vm_intro_en(chan, vms);
09083    }
09084 }
09085 
09086 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09087 {
09088    int res = 0;
09089    /* Play instructions and wait for new command */
09090    while (!res) {
09091       if (vms->starting) {
09092          if (vms->lastmsg > -1) {
09093             if (skipadvanced)
09094                res = ast_play_and_wait(chan, "vm-onefor-full");
09095             else
09096                res = ast_play_and_wait(chan, "vm-onefor");
09097             if (!res)
09098                res = vm_play_folder_name(chan, vms->vmbox);
09099          }
09100          if (!res) {
09101             if (skipadvanced)
09102                res = ast_play_and_wait(chan, "vm-opts-full");
09103             else
09104                res = ast_play_and_wait(chan, "vm-opts");
09105          }
09106       } else {
09107          /* Added for additional help */
09108          if (skipadvanced) {
09109             res = ast_play_and_wait(chan, "vm-onefor-full");
09110             if (!res)
09111                res = vm_play_folder_name(chan, vms->vmbox);
09112             res = ast_play_and_wait(chan, "vm-opts-full");
09113          }
09114          /* Logic:
09115           * If the current message is not the first OR
09116           * if we're listening to the first new message and there are
09117           * also urgent messages, then prompt for navigation to the
09118           * previous message
09119           */
09120          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09121             res = ast_play_and_wait(chan, "vm-prev");
09122          }
09123          if (!res && !skipadvanced)
09124             res = ast_play_and_wait(chan, "vm-advopts");
09125          if (!res)
09126             res = ast_play_and_wait(chan, "vm-repeat");
09127          /* Logic:
09128           * If we're not listening to the last message OR
09129           * we're listening to the last urgent message and there are
09130           * also new non-urgent messages, then prompt for navigation
09131           * to the next message
09132           */
09133          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09134             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09135             res = ast_play_and_wait(chan, "vm-next");
09136          }
09137          if (!res) {
09138             int curmsg_deleted;
09139 #ifdef IMAP_STORAGE
09140             ast_mutex_lock(&vms->lock);
09141 #endif
09142             curmsg_deleted = vms->deleted[vms->curmsg];
09143 #ifdef IMAP_STORAGE
09144             ast_mutex_unlock(&vms->lock);
09145 #endif
09146             if (!curmsg_deleted) {
09147                res = ast_play_and_wait(chan, "vm-delete");
09148             } else {
09149                res = ast_play_and_wait(chan, "vm-undelete");
09150             }
09151             if (!res) {
09152                res = ast_play_and_wait(chan, "vm-toforward");
09153             }
09154             if (!res) {
09155                res = ast_play_and_wait(chan, "vm-savemessage");
09156             }
09157          }
09158       }
09159       if (!res) {
09160          res = ast_play_and_wait(chan, "vm-helpexit");
09161       }
09162       if (!res)
09163          res = ast_waitfordigit(chan, 6000);
09164       if (!res) {
09165          vms->repeats++;
09166          if (vms->repeats > 2) {
09167             res = 't';
09168          }
09169       }
09170    }
09171    return res;
09172 }
09173 
09174 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09175 {
09176    int res = 0;
09177    /* Play instructions and wait for new command */
09178    while (!res) {
09179       if (vms->lastmsg > -1) {
09180          res = ast_play_and_wait(chan, "vm-listen");
09181          if (!res)
09182             res = vm_play_folder_name(chan, vms->vmbox);
09183          if (!res)
09184             res = ast_play_and_wait(chan, "press");
09185          if (!res)
09186             res = ast_play_and_wait(chan, "digits/1");
09187       }
09188       if (!res)
09189          res = ast_play_and_wait(chan, "vm-opts");
09190       if (!res) {
09191          vms->starting = 0;
09192          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09193       }
09194    }
09195    return res;
09196 }
09197 
09198 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09199 {
09200    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09201       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09202    } else {             /* Default to ENGLISH */
09203       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09204    }
09205 }
09206 
09207 
09208 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09209 {
09210    int cmd = 0;
09211    int duration = 0;
09212    int tries = 0;
09213    char newpassword[80] = "";
09214    char newpassword2[80] = "";
09215    char prefile[PATH_MAX] = "";
09216    unsigned char buf[256];
09217    int bytes = 0;
09218 
09219    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09220    if (ast_adsi_available(chan)) {
09221       bytes += adsi_logo(buf + bytes);
09222       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09223       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09224       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09225       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09226       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09227    }
09228 
09229    /* If forcename is set, have the user record their name */
09230    if (ast_test_flag(vmu, VM_FORCENAME)) {
09231       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09232       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09233          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09234          if (cmd < 0 || cmd == 't' || cmd == '#')
09235             return cmd;
09236       }
09237    }
09238 
09239    /* If forcegreetings is set, have the user record their greetings */
09240    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09241       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09242       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09243          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09244          if (cmd < 0 || cmd == 't' || cmd == '#')
09245             return cmd;
09246       }
09247 
09248       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09249       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09250          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09251          if (cmd < 0 || cmd == 't' || cmd == '#')
09252             return cmd;
09253       }
09254    }
09255 
09256    /*
09257     * Change the password last since new users will be able to skip over any steps this one comes before
09258     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09259     */
09260    for (;;) {
09261       newpassword[1] = '\0';
09262       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09263       if (cmd == '#')
09264          newpassword[0] = '\0';
09265       if (cmd < 0 || cmd == 't' || cmd == '#')
09266          return cmd;
09267       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09268       if (cmd < 0 || cmd == 't' || cmd == '#')
09269          return cmd;
09270       cmd = check_password(vmu, newpassword); /* perform password validation */
09271       if (cmd != 0) {
09272          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09273          cmd = ast_play_and_wait(chan, vm_invalid_password);
09274       } else {
09275          newpassword2[1] = '\0';
09276          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09277          if (cmd == '#')
09278             newpassword2[0] = '\0';
09279          if (cmd < 0 || cmd == 't' || cmd == '#')
09280             return cmd;
09281          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09282          if (cmd < 0 || cmd == 't' || cmd == '#')
09283             return cmd;
09284          if (!strcmp(newpassword, newpassword2))
09285             break;
09286          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09287          cmd = ast_play_and_wait(chan, vm_mismatch);
09288       }
09289       if (++tries == 3)
09290          return -1;
09291       if (cmd != 0) {
09292          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09293       }
09294    }
09295    if (pwdchange & PWDCHANGE_INTERNAL)
09296       vm_change_password(vmu, newpassword);
09297    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09298       vm_change_password_shell(vmu, newpassword);
09299 
09300    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09301    cmd = ast_play_and_wait(chan, vm_passchanged);
09302 
09303    return cmd;
09304 }
09305 
09306 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09307 {
09308    int cmd = 0;
09309    int retries = 0;
09310    int duration = 0;
09311    char newpassword[80] = "";
09312    char newpassword2[80] = "";
09313    char prefile[PATH_MAX] = "";
09314    unsigned char buf[256];
09315    int bytes = 0;
09316 
09317    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09318    if (ast_adsi_available(chan)) {
09319       bytes += adsi_logo(buf + bytes);
09320       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09321       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09322       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09323       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09324       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09325    }
09326    while ((cmd >= 0) && (cmd != 't')) {
09327       if (cmd)
09328          retries = 0;
09329       switch (cmd) {
09330       case '1': /* Record your unavailable message */
09331          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09332          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09333          break;
09334       case '2':  /* Record your busy message */
09335          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09336          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09337          break;
09338       case '3': /* Record greeting */
09339          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09340          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09341          break;
09342       case '4':  /* manage the temporary greeting */
09343          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09344          break;
09345       case '5': /* change password */
09346          if (vmu->password[0] == '-') {
09347             cmd = ast_play_and_wait(chan, "vm-no");
09348             break;
09349          }
09350          newpassword[1] = '\0';
09351          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09352          if (cmd == '#')
09353             newpassword[0] = '\0';
09354          else {
09355             if (cmd < 0)
09356                break;
09357             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09358                break;
09359             }
09360          }
09361          cmd = check_password(vmu, newpassword); /* perform password validation */
09362          if (cmd != 0) {
09363             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09364             cmd = ast_play_and_wait(chan, vm_invalid_password);
09365             if (!cmd) {
09366                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09367             }
09368             break;
09369          }
09370          newpassword2[1] = '\0';
09371          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09372          if (cmd == '#')
09373             newpassword2[0] = '\0';
09374          else {
09375             if (cmd < 0)
09376                break;
09377 
09378             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09379                break;
09380             }
09381          }
09382          if (strcmp(newpassword, newpassword2)) {
09383             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09384             cmd = ast_play_and_wait(chan, vm_mismatch);
09385             if (!cmd) {
09386                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09387             }
09388             break;
09389          }
09390 
09391          if (pwdchange & PWDCHANGE_INTERNAL) {
09392             vm_change_password(vmu, newpassword);
09393          }
09394          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09395             vm_change_password_shell(vmu, newpassword);
09396          }
09397 
09398          ast_debug(1, "User %s set password to %s of length %d\n",
09399             vms->username, newpassword, (int) strlen(newpassword));
09400          cmd = ast_play_and_wait(chan, vm_passchanged);
09401          break;
09402       case '*': 
09403          cmd = 't';
09404          break;
09405       default: 
09406          cmd = 0;
09407          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09408          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09409          if (ast_fileexists(prefile, NULL, NULL)) {
09410             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09411          }
09412          DISPOSE(prefile, -1);
09413          if (!cmd) {
09414             cmd = ast_play_and_wait(chan, "vm-options");
09415          }
09416          if (!cmd) {
09417             cmd = ast_waitfordigit(chan, 6000);
09418          }
09419          if (!cmd) {
09420             retries++;
09421          }
09422          if (retries > 3) {
09423             cmd = 't';
09424          }
09425          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09426       }
09427    }
09428    if (cmd == 't')
09429       cmd = 0;
09430    return cmd;
09431 }
09432 
09433 /*!
09434  * \brief The handler for 'record a temporary greeting'. 
09435  * \param chan
09436  * \param vmu
09437  * \param vms
09438  * \param fmtc
09439  * \param record_gain
09440  *
09441  * This is option 4 from the mailbox options menu.
09442  * This function manages the following promptings:
09443  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09444  * 2: remove (delete) the temporary greeting.
09445  * *: return to the main menu.
09446  *
09447  * \return zero on success, -1 on error.
09448  */
09449 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09450 {
09451    int cmd = 0;
09452    int retries = 0;
09453    int duration = 0;
09454    char prefile[PATH_MAX] = "";
09455    unsigned char buf[256];
09456    int bytes = 0;
09457 
09458    if (ast_adsi_available(chan)) {
09459       bytes += adsi_logo(buf + bytes);
09460       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09461       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09462       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09463       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09464       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09465    }
09466 
09467    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09468    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09469    while ((cmd >= 0) && (cmd != 't')) {
09470       if (cmd)
09471          retries = 0;
09472       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09473       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09474          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09475          if (cmd == -1) {
09476             break;
09477          }
09478          cmd = 't';  
09479       } else {
09480          switch (cmd) {
09481          case '1':
09482             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09483             break;
09484          case '2':
09485             DELETE(prefile, -1, prefile, vmu);
09486             ast_play_and_wait(chan, "vm-tempremoved");
09487             cmd = 't';  
09488             break;
09489          case '*': 
09490             cmd = 't';
09491             break;
09492          default:
09493             cmd = ast_play_and_wait(chan,
09494                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09495                   "vm-tempgreeting2" : "vm-tempgreeting");
09496             if (!cmd) {
09497                cmd = ast_waitfordigit(chan, 6000);
09498             }
09499             if (!cmd) {
09500                retries++;
09501             }
09502             if (retries > 3) {
09503                cmd = 't';
09504             }
09505             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09506          }
09507       }
09508       DISPOSE(prefile, -1);
09509    }
09510    if (cmd == 't')
09511       cmd = 0;
09512    return cmd;
09513 }
09514 
09515 /*!
09516  * \brief Greek syntax for 'You have N messages' greeting.
09517  * \param chan
09518  * \param vms
09519  * \param vmu
09520  *
09521  * \return zero on success, -1 on error.
09522  */   
09523 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09524 {
09525    int cmd = 0;
09526 
09527    if (vms->lastmsg > -1) {
09528       cmd = play_message(chan, vmu, vms);
09529    } else {
09530       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09531       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09532          if (!cmd) {
09533             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09534             cmd = ast_play_and_wait(chan, vms->fn);
09535          }
09536          if (!cmd)
09537             cmd = ast_play_and_wait(chan, "vm-messages");
09538       } else {
09539          if (!cmd)
09540             cmd = ast_play_and_wait(chan, "vm-messages");
09541          if (!cmd) {
09542             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09543             cmd = ast_play_and_wait(chan, vms->fn);
09544          }
09545       }
09546    } 
09547    return cmd;
09548 }
09549 
09550 /* Hebrew Syntax */
09551 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09552 {
09553    int cmd = 0;
09554 
09555    if (vms->lastmsg > -1) {
09556       cmd = play_message(chan, vmu, vms);
09557    } else {
09558       if (!strcasecmp(vms->fn, "INBOX")) {
09559          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09560       } else {
09561          cmd = ast_play_and_wait(chan, "vm-nomessages");
09562       }
09563    }
09564    return cmd;
09565 }
09566 
09567 /*! 
09568  * \brief Default English syntax for 'You have N messages' greeting.
09569  * \param chan
09570  * \param vms
09571  * \param vmu
09572  *
09573  * \return zero on success, -1 on error.
09574  */
09575 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09576 {
09577    int cmd = 0;
09578 
09579    if (vms->lastmsg > -1) {
09580       cmd = play_message(chan, vmu, vms);
09581    } else {
09582       cmd = ast_play_and_wait(chan, "vm-youhave");
09583       if (!cmd) 
09584          cmd = ast_play_and_wait(chan, "vm-no");
09585       if (!cmd) {
09586          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09587          cmd = ast_play_and_wait(chan, vms->fn);
09588       }
09589       if (!cmd)
09590          cmd = ast_play_and_wait(chan, "vm-messages");
09591    }
09592    return cmd;
09593 }
09594 
09595 /*! 
09596  *\brief Italian syntax for 'You have N messages' greeting.
09597  * \param chan
09598  * \param vms
09599  * \param vmu
09600  *
09601  * \return zero on success, -1 on error.
09602  */
09603 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09604 {
09605    int cmd;
09606 
09607    if (vms->lastmsg > -1) {
09608       cmd = play_message(chan, vmu, vms);
09609    } else {
09610       cmd = ast_play_and_wait(chan, "vm-no");
09611       if (!cmd)
09612          cmd = ast_play_and_wait(chan, "vm-message");
09613       if (!cmd) {
09614          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09615          cmd = ast_play_and_wait(chan, vms->fn);
09616       }
09617    }
09618    return cmd;
09619 }
09620 
09621 /*! 
09622  * \brief Spanish syntax for 'You have N messages' greeting.
09623  * \param chan
09624  * \param vms
09625  * \param vmu
09626  *
09627  * \return zero on success, -1 on error.
09628  */
09629 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09630 {
09631    int cmd;
09632 
09633    if (vms->lastmsg > -1) {
09634       cmd = play_message(chan, vmu, vms);
09635    } else {
09636       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09637       if (!cmd)
09638          cmd = ast_play_and_wait(chan, "vm-messages");
09639       if (!cmd) {
09640          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09641          cmd = ast_play_and_wait(chan, vms->fn);
09642       }
09643    }
09644    return cmd;
09645 }
09646 
09647 /*! 
09648  * \brief Portuguese syntax for 'You have N messages' greeting.
09649  * \param chan
09650  * \param vms
09651  * \param vmu
09652  *
09653  * \return zero on success, -1 on error.
09654  */
09655 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09656 {
09657    int cmd;
09658 
09659    if (vms->lastmsg > -1) {
09660       cmd = play_message(chan, vmu, vms);
09661    } else {
09662       cmd = ast_play_and_wait(chan, "vm-no");
09663       if (!cmd) {
09664          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09665          cmd = ast_play_and_wait(chan, vms->fn);
09666       }
09667       if (!cmd)
09668          cmd = ast_play_and_wait(chan, "vm-messages");
09669    }
09670    return cmd;
09671 }
09672 
09673 /*! 
09674  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09675  * \param chan
09676  * \param vms
09677  * \param vmu
09678  *
09679  * \return zero on success, -1 on error.
09680  */
09681 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09682 {
09683    int cmd;
09684 
09685    if (vms->lastmsg > -1) {
09686       cmd = play_message(chan, vmu, vms);
09687    } else {
09688       cmd = ast_play_and_wait(chan, "vm-you");
09689       if (!cmd) 
09690          cmd = ast_play_and_wait(chan, "vm-haveno");
09691       if (!cmd)
09692          cmd = ast_play_and_wait(chan, "vm-messages");
09693       if (!cmd) {
09694          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09695          cmd = ast_play_and_wait(chan, vms->fn);
09696       }
09697    }
09698    return cmd;
09699 }
09700 
09701 /*! 
09702  * \brief Vietnamese syntax for 'You have N messages' greeting.
09703  * \param chan
09704  * \param vms
09705  * \param vmu
09706  *
09707  * \return zero on success, -1 on error.
09708  */
09709 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09710 {
09711    int cmd = 0;
09712 
09713    if (vms->lastmsg > -1) {
09714       cmd = play_message(chan, vmu, vms);
09715    } else {
09716       cmd = ast_play_and_wait(chan, "vm-no");
09717       if (!cmd) {
09718          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09719          cmd = ast_play_and_wait(chan, vms->fn);
09720       }
09721    }
09722    return cmd;
09723 }
09724 
09725 /*!
09726  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09727  * \param chan The channel for the current user. We read the language property from this.
09728  * \param vms passed into the language-specific vm_browse_messages function.
09729  * \param vmu passed into the language-specific vm_browse_messages function.
09730  * 
09731  * The method to be invoked is determined by the value of language code property in the user's channel.
09732  * The default (when unable to match) is to use english.
09733  *
09734  * \return zero on success, -1 on error.
09735  */
09736 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09737 {
09738    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09739       return vm_browse_messages_es(chan, vms, vmu);
09740    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09741       return vm_browse_messages_gr(chan, vms, vmu);
09742    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09743       return vm_browse_messages_he(chan, vms, vmu);
09744    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09745       return vm_browse_messages_it(chan, vms, vmu);
09746    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09747       return vm_browse_messages_pt(chan, vms, vmu);
09748    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09749       return vm_browse_messages_vi(chan, vms, vmu);
09750    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09751       return vm_browse_messages_zh(chan, vms, vmu);
09752    } else {                                             /* Default to English syntax */
09753       return vm_browse_messages_en(chan, vms, vmu);
09754    }
09755 }
09756 
09757 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09758          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09759          int skipuser, int max_logins, int silent)
09760 {
09761    int useadsi = 0, valid = 0, logretries = 0;
09762    char password[AST_MAX_EXTENSION]="", *passptr;
09763    struct ast_vm_user vmus, *vmu = NULL;
09764 
09765    /* If ADSI is supported, setup login screen */
09766    adsi_begin(chan, &useadsi);
09767    if (!skipuser && useadsi)
09768       adsi_login(chan);
09769    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09770    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09771       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09772       return -1;
09773    }
09774 
09775    /* Authenticate them and get their mailbox/password */
09776 
09777    while (!valid && (logretries < max_logins)) {
09778       /* Prompt for, and read in the username */
09779       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09780          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09781          return -1;
09782       }
09783       if (ast_strlen_zero(mailbox)) {
09784          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09785             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09786          } else {
09787             ast_verb(3, "Username not entered\n"); 
09788             return -1;
09789          }
09790       } else if (mailbox[0] == '*') {
09791          /* user entered '*' */
09792          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09793          if (ast_exists_extension(chan, chan->context, "a", 1,
09794             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09795             return -1;
09796          }
09797          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09798          mailbox[0] = '\0';
09799       }
09800 
09801       if (useadsi)
09802          adsi_password(chan);
09803 
09804       if (!ast_strlen_zero(prefix)) {
09805          char fullusername[80] = "";
09806          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09807          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09808          ast_copy_string(mailbox, fullusername, mailbox_size);
09809       }
09810 
09811       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09812       vmu = find_user(&vmus, context, mailbox);
09813       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09814          /* saved password is blank, so don't bother asking */
09815          password[0] = '\0';
09816       } else {
09817          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09818          if (ast_streamfile(chan, vm_password, chan->language)) {
09819             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09820             return -1;
09821          }
09822          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09823             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09824             return -1;
09825          } else if (password[0] == '*') {
09826             /* user entered '*' */
09827             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09828             if (ast_exists_extension(chan, chan->context, "a", 1,
09829                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09830                mailbox[0] = '*';
09831                return -1;
09832             }
09833             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09834             mailbox[0] = '\0';
09835             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09836             vmu = NULL;
09837          }
09838       }
09839 
09840       if (vmu) {
09841          passptr = vmu->password;
09842          if (passptr[0] == '-') passptr++;
09843       }
09844       if (vmu && !strcmp(passptr, password))
09845          valid++;
09846       else {
09847          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09848          if (!ast_strlen_zero(prefix))
09849             mailbox[0] = '\0';
09850       }
09851       logretries++;
09852       if (!valid) {
09853          if (skipuser || logretries >= max_logins) {
09854             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09855             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09856                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09857                return -1;
09858             }
09859          } else {
09860             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09861             if (useadsi)
09862                adsi_login(chan);
09863             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09864                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09865                return -1;
09866             }
09867          }
09868          if (ast_waitstream(chan, "")) /* Channel is hung up */
09869             return -1;
09870       }
09871    }
09872    if (!valid && (logretries >= max_logins)) {
09873       ast_stopstream(chan);
09874       ast_play_and_wait(chan, "vm-goodbye");
09875       return -1;
09876    }
09877    if (vmu && !skipuser) {
09878       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09879    }
09880    return 0;
09881 }
09882 
09883 static int vm_execmain(struct ast_channel *chan, const char *data)
09884 {
09885    /* XXX This is, admittedly, some pretty horrendous code.  For some
09886       reason it just seemed a lot easier to do with GOTO's.  I feel
09887       like I'm back in my GWBASIC days. XXX */
09888    int res = -1;
09889    int cmd = 0;
09890    int valid = 0;
09891    char prefixstr[80] ="";
09892    char ext_context[256]="";
09893    int box;
09894    int useadsi = 0;
09895    int skipuser = 0;
09896    struct vm_state vms;
09897    struct ast_vm_user *vmu = NULL, vmus;
09898    char *context = NULL;
09899    int silentexit = 0;
09900    struct ast_flags flags = { 0 };
09901    signed char record_gain = 0;
09902    int play_auto = 0;
09903    int play_folder = 0;
09904    int in_urgent = 0;
09905 #ifdef IMAP_STORAGE
09906    int deleted = 0;
09907 #endif
09908 
09909    /* Add the vm_state to the active list and keep it active */
09910    memset(&vms, 0, sizeof(vms));
09911 
09912    vms.lastmsg = -1;
09913 
09914    memset(&vmus, 0, sizeof(vmus));
09915 
09916    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09917    if (chan->_state != AST_STATE_UP) {
09918       ast_debug(1, "Before ast_answer\n");
09919       ast_answer(chan);
09920    }
09921 
09922    if (!ast_strlen_zero(data)) {
09923       char *opts[OPT_ARG_ARRAY_SIZE];
09924       char *parse;
09925       AST_DECLARE_APP_ARGS(args,
09926          AST_APP_ARG(argv0);
09927          AST_APP_ARG(argv1);
09928       );
09929 
09930       parse = ast_strdupa(data);
09931 
09932       AST_STANDARD_APP_ARGS(args, parse);
09933 
09934       if (args.argc == 2) {
09935          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09936             return -1;
09937          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09938             int gain;
09939             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09940                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09941                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09942                   return -1;
09943                } else {
09944                   record_gain = (signed char) gain;
09945                }
09946             } else {
09947                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09948             }
09949          }
09950          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09951             play_auto = 1;
09952             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09953                /* See if it is a folder name first */
09954                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09955                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09956                      play_folder = -1;
09957                   }
09958                } else {
09959                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09960                }
09961             } else {
09962                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09963             }
09964             if (play_folder > 9 || play_folder < 0) {
09965                ast_log(AST_LOG_WARNING,
09966                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09967                   opts[OPT_ARG_PLAYFOLDER]);
09968                play_folder = 0;
09969             }
09970          }
09971       } else {
09972          /* old style options parsing */
09973          while (*(args.argv0)) {
09974             if (*(args.argv0) == 's')
09975                ast_set_flag(&flags, OPT_SILENT);
09976             else if (*(args.argv0) == 'p')
09977                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09978             else 
09979                break;
09980             (args.argv0)++;
09981          }
09982 
09983       }
09984 
09985       valid = ast_test_flag(&flags, OPT_SILENT);
09986 
09987       if ((context = strchr(args.argv0, '@')))
09988          *context++ = '\0';
09989 
09990       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09991          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09992       else
09993          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09994 
09995       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09996          skipuser++;
09997       else
09998          valid = 0;
09999    }
10000 
10001    if (!valid)
10002       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10003 
10004    ast_debug(1, "After vm_authenticate\n");
10005 
10006    if (vms.username[0] == '*') {
10007       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10008 
10009       /* user entered '*' */
10010       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10011          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10012          res = 0; /* prevent hangup */
10013          goto out;
10014       }
10015    }
10016 
10017    if (!res) {
10018       valid = 1;
10019       if (!skipuser)
10020          vmu = &vmus;
10021    } else {
10022       res = 0;
10023    }
10024 
10025    /* If ADSI is supported, setup login screen */
10026    adsi_begin(chan, &useadsi);
10027 
10028    ast_test_suite_assert(valid);
10029    if (!valid) {
10030       goto out;
10031    }
10032    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10033 
10034 #ifdef IMAP_STORAGE
10035    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10036    pthread_setspecific(ts_vmstate.key, &vms);
10037 
10038    vms.interactive = 1;
10039    vms.updated = 1;
10040    if (vmu)
10041       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10042    vmstate_insert(&vms);
10043    init_vm_state(&vms);
10044 #endif
10045    
10046    /* Set language from config to override channel language */
10047    if (!ast_strlen_zero(vmu->language))
10048       ast_string_field_set(chan, language, vmu->language);
10049 
10050    /* Retrieve urgent, old and new message counts */
10051    ast_debug(1, "Before open_mailbox\n");
10052    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10053    if (res < 0)
10054       goto out;
10055    vms.oldmessages = vms.lastmsg + 1;
10056    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10057    /* check INBOX */
10058    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10059    if (res < 0)
10060       goto out;
10061    vms.newmessages = vms.lastmsg + 1;
10062    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10063    /* Start in Urgent */
10064    in_urgent = 1;
10065    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10066    if (res < 0)
10067       goto out;
10068    vms.urgentmessages = vms.lastmsg + 1;
10069    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10070 
10071    /* Select proper mailbox FIRST!! */
10072    if (play_auto) {
10073       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10074       if (vms.urgentmessages) {
10075          in_urgent = 1;
10076          res = open_mailbox(&vms, vmu, 11);
10077       } else {
10078          in_urgent = 0;
10079          res = open_mailbox(&vms, vmu, play_folder);
10080       }
10081       if (res < 0)
10082          goto out;
10083 
10084       /* If there are no new messages, inform the user and hangup */
10085       if (vms.lastmsg == -1) {
10086          in_urgent = 0;
10087          cmd = vm_browse_messages(chan, &vms, vmu);
10088          res = 0;
10089          goto out;
10090       }
10091    } else {
10092       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10093          /* If we only have old messages start here */
10094          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10095          in_urgent = 0;
10096          play_folder = 1;
10097          if (res < 0)
10098             goto out;
10099       } else if (!vms.urgentmessages && vms.newmessages) {
10100          /* If we have new messages but none are urgent */
10101          in_urgent = 0;
10102          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10103          if (res < 0)
10104             goto out;
10105       }
10106    }
10107 
10108    if (useadsi)
10109       adsi_status(chan, &vms);
10110    res = 0;
10111 
10112    /* Check to see if this is a new user */
10113    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10114       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10115       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10116          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10117       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10118       if ((cmd == 't') || (cmd == '#')) {
10119          /* Timeout */
10120          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10121          res = 0;
10122          goto out;
10123       } else if (cmd < 0) {
10124          /* Hangup */
10125          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10126          res = -1;
10127          goto out;
10128       }
10129    }
10130 #ifdef IMAP_STORAGE
10131       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10132       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10133          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10134          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10135       }
10136       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10137       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10138          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10139          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10140       }
10141 #endif
10142 
10143    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10144    if (play_auto) {
10145       cmd = '1';
10146    } else {
10147       cmd = vm_intro(chan, vmu, &vms);
10148    }
10149    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10150 
10151    vms.repeats = 0;
10152    vms.starting = 1;
10153    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10154       /* Run main menu */
10155       switch (cmd) {
10156       case '1': /* First message */
10157          vms.curmsg = 0;
10158          /* Fall through */
10159       case '5': /* Play current message */
10160          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10161          cmd = vm_browse_messages(chan, &vms, vmu);
10162          break;
10163       case '2': /* Change folders */
10164          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10165          if (useadsi)
10166             adsi_folders(chan, 0, "Change to folder...");
10167 
10168          cmd = get_folder2(chan, "vm-changeto", 0);
10169          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10170          if (cmd == '#') {
10171             cmd = 0;
10172          } else if (cmd > 0) {
10173             cmd = cmd - '0';
10174             res = close_mailbox(&vms, vmu);
10175             if (res == ERROR_LOCK_PATH)
10176                goto out;
10177             /* If folder is not urgent, set in_urgent to zero! */
10178             if (cmd != 11) in_urgent = 0;
10179             res = open_mailbox(&vms, vmu, cmd);
10180             if (res < 0)
10181                goto out;
10182             play_folder = cmd;
10183             cmd = 0;
10184          }
10185          if (useadsi)
10186             adsi_status2(chan, &vms);
10187 
10188          if (!cmd) {
10189             cmd = vm_play_folder_name(chan, vms.vmbox);
10190          }
10191 
10192          vms.starting = 1;
10193          vms.curmsg = 0;
10194          break;
10195       case '3': /* Advanced options */
10196          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10197          cmd = 0;
10198          vms.repeats = 0;
10199          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10200             switch (cmd) {
10201             case '1': /* Reply */
10202                if (vms.lastmsg > -1 && !vms.starting) {
10203                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10204                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10205                      res = cmd;
10206                      goto out;
10207                   }
10208                } else {
10209                   cmd = ast_play_and_wait(chan, "vm-sorry");
10210                }
10211                cmd = 't';
10212                break;
10213             case '2': /* Callback */
10214                if (!vms.starting)
10215                   ast_verb(3, "Callback Requested\n");
10216                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10217                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10218                   if (cmd == 9) {
10219                      silentexit = 1;
10220                      goto out;
10221                   } else if (cmd == ERROR_LOCK_PATH) {
10222                      res = cmd;
10223                      goto out;
10224                   }
10225                } else {
10226                   cmd = ast_play_and_wait(chan, "vm-sorry");
10227                }
10228                cmd = 't';
10229                break;
10230             case '3': /* Envelope */
10231                if (vms.lastmsg > -1 && !vms.starting) {
10232                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10233                   if (cmd == ERROR_LOCK_PATH) {
10234                      res = cmd;
10235                      goto out;
10236                   }
10237                } else {
10238                   cmd = ast_play_and_wait(chan, "vm-sorry");
10239                }
10240                cmd = 't';
10241                break;
10242             case '4': /* Dialout */
10243                if (!ast_strlen_zero(vmu->dialout)) {
10244                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10245                   if (cmd == 9) {
10246                      silentexit = 1;
10247                      goto out;
10248                   }
10249                } else {
10250                   cmd = ast_play_and_wait(chan, "vm-sorry");
10251                }
10252                cmd = 't';
10253                break;
10254 
10255             case '5': /* Leave VoiceMail */
10256                if (ast_test_flag(vmu, VM_SVMAIL)) {
10257                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10258                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10259                      res = cmd;
10260                      goto out;
10261                   }
10262                } else {
10263                   cmd = ast_play_and_wait(chan, "vm-sorry");
10264                }
10265                cmd = 't';
10266                break;
10267 
10268             case '*': /* Return to main menu */
10269                cmd = 't';
10270                break;
10271 
10272             default:
10273                cmd = 0;
10274                if (!vms.starting) {
10275                   cmd = ast_play_and_wait(chan, "vm-toreply");
10276                }
10277                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10278                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10279                }
10280                if (!cmd && !vms.starting) {
10281                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10282                }
10283                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10284                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10285                }
10286                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10287                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10288                }
10289                if (!cmd) {
10290                   cmd = ast_play_and_wait(chan, "vm-starmain");
10291                }
10292                if (!cmd) {
10293                   cmd = ast_waitfordigit(chan, 6000);
10294                }
10295                if (!cmd) {
10296                   vms.repeats++;
10297                }
10298                if (vms.repeats > 3) {
10299                   cmd = 't';
10300                }
10301                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10302             }
10303          }
10304          if (cmd == 't') {
10305             cmd = 0;
10306             vms.repeats = 0;
10307          }
10308          break;
10309       case '4': /* Go to the previous message */
10310          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10311          if (vms.curmsg > 0) {
10312             vms.curmsg--;
10313             cmd = play_message(chan, vmu, &vms);
10314          } else {
10315             /* Check if we were listening to new
10316                messages.  If so, go to Urgent messages
10317                instead of saying "no more messages"
10318             */
10319             if (in_urgent == 0 && vms.urgentmessages > 0) {
10320                /* Check for Urgent messages */
10321                in_urgent = 1;
10322                res = close_mailbox(&vms, vmu);
10323                if (res == ERROR_LOCK_PATH)
10324                   goto out;
10325                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10326                if (res < 0)
10327                   goto out;
10328                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10329                vms.curmsg = vms.lastmsg;
10330                if (vms.lastmsg < 0) {
10331                   cmd = ast_play_and_wait(chan, "vm-nomore");
10332                }
10333             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10334                vms.curmsg = vms.lastmsg;
10335                cmd = play_message(chan, vmu, &vms);
10336             } else {
10337                cmd = ast_play_and_wait(chan, "vm-nomore");
10338             }
10339          }
10340          break;
10341       case '6': /* Go to the next message */
10342          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10343          if (vms.curmsg < vms.lastmsg) {
10344             vms.curmsg++;
10345             cmd = play_message(chan, vmu, &vms);
10346          } else {
10347             if (in_urgent && vms.newmessages > 0) {
10348                /* Check if we were listening to urgent
10349                 * messages.  If so, go to regular new messages
10350                 * instead of saying "no more messages"
10351                 */
10352                in_urgent = 0;
10353                res = close_mailbox(&vms, vmu);
10354                if (res == ERROR_LOCK_PATH)
10355                   goto out;
10356                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10357                if (res < 0)
10358                   goto out;
10359                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10360                vms.curmsg = -1;
10361                if (vms.lastmsg < 0) {
10362                   cmd = ast_play_and_wait(chan, "vm-nomore");
10363                }
10364             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10365                vms.curmsg = 0;
10366                cmd = play_message(chan, vmu, &vms);
10367             } else {
10368                cmd = ast_play_and_wait(chan, "vm-nomore");
10369             }
10370          }
10371          break;
10372       case '7': /* Delete the current message */
10373          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10374             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10375             if (useadsi)
10376                adsi_delete(chan, &vms);
10377             if (vms.deleted[vms.curmsg]) {
10378                if (play_folder == 0) {
10379                   if (in_urgent) {
10380                      vms.urgentmessages--;
10381                   } else {
10382                      vms.newmessages--;
10383                   }
10384                }
10385                else if (play_folder == 1)
10386                   vms.oldmessages--;
10387                cmd = ast_play_and_wait(chan, "vm-deleted");
10388             } else {
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-undeleted");
10399             }
10400             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10401                if (vms.curmsg < vms.lastmsg) {
10402                   vms.curmsg++;
10403                   cmd = play_message(chan, vmu, &vms);
10404                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10405                   vms.curmsg = 0;
10406                   cmd = play_message(chan, vmu, &vms);
10407                } else {
10408                   /* Check if we were listening to urgent
10409                      messages.  If so, go to regular new messages
10410                      instead of saying "no more messages"
10411                   */
10412                   if (in_urgent == 1) {
10413                      /* Check for new messages */
10414                      in_urgent = 0;
10415                      res = close_mailbox(&vms, vmu);
10416                      if (res == ERROR_LOCK_PATH)
10417                         goto out;
10418                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10419                      if (res < 0)
10420                         goto out;
10421                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10422                      vms.curmsg = -1;
10423                      if (vms.lastmsg < 0) {
10424                         cmd = ast_play_and_wait(chan, "vm-nomore");
10425                      }
10426                   } else {
10427                      cmd = ast_play_and_wait(chan, "vm-nomore");
10428                   }
10429                }
10430             }
10431          } else /* Delete not valid if we haven't selected a message */
10432             cmd = 0;
10433 #ifdef IMAP_STORAGE
10434          deleted = 1;
10435 #endif
10436          break;
10437    
10438       case '8': /* Forward the current message */
10439          if (vms.lastmsg > -1) {
10440             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10441             if (cmd == ERROR_LOCK_PATH) {
10442                res = cmd;
10443                goto out;
10444             }
10445          } else {
10446             /* Check if we were listening to urgent
10447                messages.  If so, go to regular new messages
10448                instead of saying "no more messages"
10449             */
10450             if (in_urgent == 1 && vms.newmessages > 0) {
10451                /* Check for new messages */
10452                in_urgent = 0;
10453                res = close_mailbox(&vms, vmu);
10454                if (res == ERROR_LOCK_PATH)
10455                   goto out;
10456                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10457                if (res < 0)
10458                   goto out;
10459                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10460                vms.curmsg = -1;
10461                if (vms.lastmsg < 0) {
10462                   cmd = ast_play_and_wait(chan, "vm-nomore");
10463                }
10464             } else {
10465                cmd = ast_play_and_wait(chan, "vm-nomore");
10466             }
10467          }
10468          break;
10469       case '9': /* Save message to folder */
10470          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10471          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10472             /* No message selected */
10473             cmd = 0;
10474             break;
10475          }
10476          if (useadsi)
10477             adsi_folders(chan, 1, "Save to folder...");
10478          cmd = get_folder2(chan, "vm-savefolder", 1);
10479          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10480          box = 0; /* Shut up compiler */
10481          if (cmd == '#') {
10482             cmd = 0;
10483             break;
10484          } else if (cmd > 0) {
10485             box = cmd = cmd - '0';
10486             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10487             if (cmd == ERROR_LOCK_PATH) {
10488                res = cmd;
10489                goto out;
10490 #ifndef IMAP_STORAGE
10491             } else if (!cmd) {
10492                vms.deleted[vms.curmsg] = 1;
10493 #endif
10494             } else {
10495                vms.deleted[vms.curmsg] = 0;
10496                vms.heard[vms.curmsg] = 0;
10497             }
10498          }
10499          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10500          if (useadsi)
10501             adsi_message(chan, &vms);
10502          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10503          if (!cmd) {
10504             cmd = ast_play_and_wait(chan, "vm-message");
10505             if (!cmd) 
10506                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10507             if (!cmd)
10508                cmd = ast_play_and_wait(chan, "vm-savedto");
10509             if (!cmd)
10510                cmd = vm_play_folder_name(chan, vms.fn);
10511          } else {
10512             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10513          }
10514          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10515             if (vms.curmsg < vms.lastmsg) {
10516                vms.curmsg++;
10517                cmd = play_message(chan, vmu, &vms);
10518             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10519                vms.curmsg = 0;
10520                cmd = play_message(chan, vmu, &vms);
10521             } else {
10522                /* Check if we were listening to urgent
10523                   messages.  If so, go to regular new messages
10524                   instead of saying "no more messages"
10525                */
10526                if (in_urgent == 1 && vms.newmessages > 0) {
10527                   /* Check for new messages */
10528                   in_urgent = 0;
10529                   res = close_mailbox(&vms, vmu);
10530                   if (res == ERROR_LOCK_PATH)
10531                      goto out;
10532                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10533                   if (res < 0)
10534                      goto out;
10535                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10536                   vms.curmsg = -1;
10537                   if (vms.lastmsg < 0) {
10538                      cmd = ast_play_and_wait(chan, "vm-nomore");
10539                   }
10540                } else {
10541                   cmd = ast_play_and_wait(chan, "vm-nomore");
10542                }
10543             }
10544          }
10545          break;
10546       case '*': /* Help */
10547          if (!vms.starting) {
10548             cmd = ast_play_and_wait(chan, "vm-onefor");
10549             if (!strncasecmp(chan->language, "he", 2)) {
10550                cmd = ast_play_and_wait(chan, "vm-for");
10551             }
10552             if (!cmd)
10553                cmd = vm_play_folder_name(chan, vms.vmbox);
10554             if (!cmd)
10555                cmd = ast_play_and_wait(chan, "vm-opts");
10556             if (!cmd)
10557                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10558          } else
10559             cmd = 0;
10560          break;
10561       case '0': /* Mailbox options */
10562          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10563          if (useadsi)
10564             adsi_status(chan, &vms);
10565          break;
10566       default: /* Nothing */
10567          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10568          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10569          break;
10570       }
10571    }
10572    if ((cmd == 't') || (cmd == '#')) {
10573       /* Timeout */
10574       res = 0;
10575    } else {
10576       /* Hangup */
10577       res = -1;
10578    }
10579 
10580 out:
10581    if (res > -1) {
10582       ast_stopstream(chan);
10583       adsi_goodbye(chan);
10584       if (valid && res != OPERATOR_EXIT) {
10585          if (silentexit)
10586             res = ast_play_and_wait(chan, "vm-dialout");
10587          else 
10588             res = ast_play_and_wait(chan, "vm-goodbye");
10589       }
10590       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10591          res = 0;
10592       }
10593       if (useadsi)
10594          ast_adsi_unload_session(chan);
10595    }
10596    if (vmu)
10597       close_mailbox(&vms, vmu);
10598    if (valid) {
10599       int new = 0, old = 0, urgent = 0;
10600       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10601       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10602       /* Urgent flag not passwd to externnotify here */
10603       run_externnotify(vmu->context, vmu->mailbox, NULL);
10604       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10605       queue_mwi_event(ext_context, urgent, new, old);
10606    }
10607 #ifdef IMAP_STORAGE
10608    /* expunge message - use UID Expunge if supported on IMAP server*/
10609    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10610    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10611       ast_mutex_lock(&vms.lock);
10612 #ifdef HAVE_IMAP_TK2006
10613       if (LEVELUIDPLUS (vms.mailstream)) {
10614          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10615       } else 
10616 #endif
10617          mail_expunge(vms.mailstream);
10618       ast_mutex_unlock(&vms.lock);
10619    }
10620    /*  before we delete the state, we should copy pertinent info
10621     *  back to the persistent model */
10622    if (vmu) {
10623       vmstate_delete(&vms);
10624    }
10625 #endif
10626    if (vmu)
10627       free_user(vmu);
10628 
10629 #ifdef IMAP_STORAGE
10630    pthread_setspecific(ts_vmstate.key, NULL);
10631 #endif
10632    return res;
10633 }
10634 
10635 static int vm_exec(struct ast_channel *chan, const char *data)
10636 {
10637    int res = 0;
10638    char *tmp;
10639    struct leave_vm_options leave_options;
10640    struct ast_flags flags = { 0 };
10641    char *opts[OPT_ARG_ARRAY_SIZE];
10642    AST_DECLARE_APP_ARGS(args,
10643       AST_APP_ARG(argv0);
10644       AST_APP_ARG(argv1);
10645    );
10646    
10647    memset(&leave_options, 0, sizeof(leave_options));
10648 
10649    if (chan->_state != AST_STATE_UP)
10650       ast_answer(chan);
10651 
10652    if (!ast_strlen_zero(data)) {
10653       tmp = ast_strdupa(data);
10654       AST_STANDARD_APP_ARGS(args, tmp);
10655       if (args.argc == 2) {
10656          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10657             return -1;
10658          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10659          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10660             int gain;
10661 
10662             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10663                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10664                return -1;
10665             } else {
10666                leave_options.record_gain = (signed char) gain;
10667             }
10668          }
10669          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10670             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10671                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10672          }
10673       }
10674    } else {
10675       char temp[256];
10676       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10677       if (res < 0)
10678          return res;
10679       if (ast_strlen_zero(temp))
10680          return 0;
10681       args.argv0 = ast_strdupa(temp);
10682    }
10683 
10684    res = leave_voicemail(chan, args.argv0, &leave_options);
10685    if (res == 't') {
10686       ast_play_and_wait(chan, "vm-goodbye");
10687       res = 0;
10688    }
10689 
10690    if (res == OPERATOR_EXIT) {
10691       res = 0;
10692    }
10693 
10694    if (res == ERROR_LOCK_PATH) {
10695       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10696       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10697       res = 0;
10698    }
10699 
10700    return res;
10701 }
10702 
10703 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10704 {
10705    struct ast_vm_user *vmu;
10706 
10707    if (!ast_strlen_zero(box) && box[0] == '*') {
10708       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10709             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10710             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10711             "\n\tand will be ignored.\n", box, context);
10712       return NULL;
10713    }
10714 
10715    AST_LIST_TRAVERSE(&users, vmu, list) {
10716       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10717          if (strcasecmp(vmu->context, context)) {
10718             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10719                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10720                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10721                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10722          }
10723          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10724          return NULL;
10725       }
10726       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10727          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10728          return NULL;
10729       }
10730    }
10731    
10732    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10733       return NULL;
10734    
10735    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10736    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10737 
10738    AST_LIST_INSERT_TAIL(&users, vmu, list);
10739    
10740    return vmu;
10741 }
10742 
10743 static int append_mailbox(const char *context, const char *box, const char *data)
10744 {
10745    /* Assumes lock is already held */
10746    char *tmp;
10747    char *stringp;
10748    char *s;
10749    struct ast_vm_user *vmu;
10750    char *mailbox_full;
10751    int new = 0, old = 0, urgent = 0;
10752    char secretfn[PATH_MAX] = "";
10753 
10754    tmp = ast_strdupa(data);
10755 
10756    if (!(vmu = find_or_create(context, box)))
10757       return -1;
10758 
10759    populate_defaults(vmu);
10760 
10761    stringp = tmp;
10762    if ((s = strsep(&stringp, ","))) {
10763       if (!ast_strlen_zero(s) && s[0] == '*') {
10764          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10765             "\n\tmust be reset in voicemail.conf.\n", box);
10766       }
10767       /* assign password regardless of validity to prevent NULL password from being assigned */
10768       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10769    }
10770    if (stringp && (s = strsep(&stringp, ","))) {
10771       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10772    }
10773    if (stringp && (s = strsep(&stringp, ","))) {
10774       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10775    }
10776    if (stringp && (s = strsep(&stringp, ","))) {
10777       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10778    }
10779    if (stringp && (s = strsep(&stringp, ","))) {
10780       apply_options(vmu, s);
10781    }
10782 
10783    switch (vmu->passwordlocation) {
10784    case OPT_PWLOC_SPOOLDIR:
10785       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10786       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10787    }
10788 
10789    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10790    strcpy(mailbox_full, box);
10791    strcat(mailbox_full, "@");
10792    strcat(mailbox_full, context);
10793 
10794    inboxcount2(mailbox_full, &urgent, &new, &old);
10795    queue_mwi_event(mailbox_full, urgent, new, old);
10796 
10797    return 0;
10798 }
10799 
10800 AST_TEST_DEFINE(test_voicemail_vmuser)
10801 {
10802    int res = 0;
10803    struct ast_vm_user *vmu;
10804    /* language parameter seems to only be used for display in manager action */
10805    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10806       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10807       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10808       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10809       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10810       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10811       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10812       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10813       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10814 #ifdef IMAP_STORAGE
10815    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10816       "imapfolder=INBOX|imapvmshareid=6000";
10817 #endif
10818 
10819    switch (cmd) {
10820    case TEST_INIT:
10821       info->name = "vmuser";
10822       info->category = "/apps/app_voicemail/";
10823       info->summary = "Vmuser unit test";
10824       info->description =
10825          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10826       return AST_TEST_NOT_RUN;
10827    case TEST_EXECUTE:
10828       break;
10829    }
10830 
10831    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10832       return AST_TEST_NOT_RUN;
10833    }
10834    ast_set_flag(vmu, VM_ALLOCED);
10835    populate_defaults(vmu);
10836 
10837    apply_options(vmu, options_string);
10838 
10839    if (!ast_test_flag(vmu, VM_ATTACH)) {
10840       ast_test_status_update(test, "Parse failure for attach option\n");
10841       res = 1;
10842    }
10843    if (strcasecmp(vmu->attachfmt, "wav49")) {
10844       ast_test_status_update(test, "Parse failure for attachftm option\n");
10845       res = 1;
10846    }
10847    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10848       ast_test_status_update(test, "Parse failure for serveremail option\n");
10849       res = 1;
10850    }
10851    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10852       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10853       res = 1;
10854    }
10855    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10856       ast_test_status_update(test, "Parse failure for emailbody option\n");
10857       res = 1;
10858    }
10859    if (strcasecmp(vmu->zonetag, "central")) {
10860       ast_test_status_update(test, "Parse failure for tz option\n");
10861       res = 1;
10862    }
10863    if (!ast_test_flag(vmu, VM_DELETE)) {
10864       ast_test_status_update(test, "Parse failure for delete option\n");
10865       res = 1;
10866    }
10867    if (!ast_test_flag(vmu, VM_SAYCID)) {
10868       ast_test_status_update(test, "Parse failure for saycid option\n");
10869       res = 1;
10870    }
10871    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10872       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10873       res = 1;
10874    }
10875    if (!ast_test_flag(vmu, VM_REVIEW)) {
10876       ast_test_status_update(test, "Parse failure for review option\n");
10877       res = 1;
10878    }
10879    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10880       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10881       res = 1;
10882    }
10883    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10884       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10885       res = 1;
10886    }
10887    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10888       ast_test_status_update(test, "Parse failure for operator option\n");
10889       res = 1;
10890    }
10891    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10892       ast_test_status_update(test, "Parse failure for envelope option\n");
10893       res = 1;
10894    }
10895    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10896       ast_test_status_update(test, "Parse failure for moveheard option\n");
10897       res = 1;
10898    }
10899    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10900       ast_test_status_update(test, "Parse failure for sayduration option\n");
10901       res = 1;
10902    }
10903    if (vmu->saydurationm != 5) {
10904       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10905       res = 1;
10906    }
10907    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10908       ast_test_status_update(test, "Parse failure for forcename option\n");
10909       res = 1;
10910    }
10911    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10912       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10913       res = 1;
10914    }
10915    if (strcasecmp(vmu->callback, "somecontext")) {
10916       ast_test_status_update(test, "Parse failure for callbacks option\n");
10917       res = 1;
10918    }
10919    if (strcasecmp(vmu->dialout, "somecontext2")) {
10920       ast_test_status_update(test, "Parse failure for dialout option\n");
10921       res = 1;
10922    }
10923    if (strcasecmp(vmu->exit, "somecontext3")) {
10924       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10925       res = 1;
10926    }
10927    if (vmu->minsecs != 10) {
10928       ast_test_status_update(test, "Parse failure for minsecs option\n");
10929       res = 1;
10930    }
10931    if (vmu->maxsecs != 100) {
10932       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10933       res = 1;
10934    }
10935    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10936       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10937       res = 1;
10938    }
10939    if (vmu->maxdeletedmsg != 50) {
10940       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10941       res = 1;
10942    }
10943    if (vmu->volgain != 1.3) {
10944       ast_test_status_update(test, "Parse failure for volgain option\n");
10945       res = 1;
10946    }
10947    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10948       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10949       res = 1;
10950    }
10951 #ifdef IMAP_STORAGE
10952    apply_options(vmu, option_string2);
10953 
10954    if (strcasecmp(vmu->imapuser, "imapuser")) {
10955       ast_test_status_update(test, "Parse failure for imapuser option\n");
10956       res = 1;
10957    }
10958    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10959       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10960       res = 1;
10961    }
10962    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10963       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10964       res = 1;
10965    }
10966    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10967       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10968       res = 1;
10969    }
10970 #endif
10971 
10972    free_user(vmu);
10973    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10974 }
10975 
10976 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10977 {
10978    struct ast_vm_user svm;
10979    char *context, *box;
10980    AST_DECLARE_APP_ARGS(args,
10981       AST_APP_ARG(mbox);
10982       AST_APP_ARG(options);
10983    );
10984    static int dep_warning = 0;
10985 
10986    if (ast_strlen_zero(data)) {
10987       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10988       return -1;
10989    }
10990 
10991    if (!dep_warning) {
10992       dep_warning = 1;
10993       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10994    }
10995 
10996    box = ast_strdupa(data);
10997 
10998    AST_STANDARD_APP_ARGS(args, box);
10999 
11000    if (args.options) {
11001    }
11002 
11003    if ((context = strchr(args.mbox, '@'))) {
11004       *context = '\0';
11005       context++;
11006    }
11007 
11008    if (find_user(&svm, context, args.mbox)) {
11009       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11010    } else
11011       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11012 
11013    return 0;
11014 }
11015 
11016 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11017 {
11018    struct ast_vm_user svm;
11019    AST_DECLARE_APP_ARGS(arg,
11020       AST_APP_ARG(mbox);
11021       AST_APP_ARG(context);
11022    );
11023 
11024    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11025 
11026    if (ast_strlen_zero(arg.mbox)) {
11027       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11028       return -1;
11029    }
11030 
11031    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11032    return 0;
11033 }
11034 
11035 static struct ast_custom_function mailbox_exists_acf = {
11036    .name = "MAILBOX_EXISTS",
11037    .read = acf_mailbox_exists,
11038 };
11039 
11040 static int vmauthenticate(struct ast_channel *chan, const char *data)
11041 {
11042    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11043    struct ast_vm_user vmus;
11044    char *options = NULL;
11045    int silent = 0, skipuser = 0;
11046    int res = -1;
11047    
11048    if (data) {
11049       s = ast_strdupa(data);
11050       user = strsep(&s, ",");
11051       options = strsep(&s, ",");
11052       if (user) {
11053          s = user;
11054          user = strsep(&s, "@");
11055          context = strsep(&s, "");
11056          if (!ast_strlen_zero(user))
11057             skipuser++;
11058          ast_copy_string(mailbox, user, sizeof(mailbox));
11059       }
11060    }
11061 
11062    if (options) {
11063       silent = (strchr(options, 's')) != NULL;
11064    }
11065 
11066    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11067       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11068       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11069       ast_play_and_wait(chan, "auth-thankyou");
11070       res = 0;
11071    } else if (mailbox[0] == '*') {
11072       /* user entered '*' */
11073       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11074          res = 0; /* prevent hangup */
11075       }
11076    }
11077 
11078    return res;
11079 }
11080 
11081 static char *show_users_realtime(int fd, const char *context)
11082 {
11083    struct ast_config *cfg;
11084    const char *cat = NULL;
11085 
11086    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11087       "context", context, SENTINEL))) {
11088       return CLI_FAILURE;
11089    }
11090 
11091    ast_cli(fd,
11092       "\n"
11093       "=============================================================\n"
11094       "=== Configured Voicemail Users ==============================\n"
11095       "=============================================================\n"
11096       "===\n");
11097 
11098    while ((cat = ast_category_browse(cfg, cat))) {
11099       struct ast_variable *var = NULL;
11100       ast_cli(fd,
11101          "=== Mailbox ...\n"
11102          "===\n");
11103       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11104          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11105       ast_cli(fd,
11106          "===\n"
11107          "=== ---------------------------------------------------------\n"
11108          "===\n");
11109    }
11110 
11111    ast_cli(fd,
11112       "=============================================================\n"
11113       "\n");
11114 
11115    ast_config_destroy(cfg);
11116 
11117    return CLI_SUCCESS;
11118 }
11119 
11120 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11121 {
11122    int which = 0;
11123    int wordlen;
11124    struct ast_vm_user *vmu;
11125    const char *context = "";
11126 
11127    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11128    if (pos > 4)
11129       return NULL;
11130    if (pos == 3)
11131       return (state == 0) ? ast_strdup("for") : NULL;
11132    wordlen = strlen(word);
11133    AST_LIST_TRAVERSE(&users, vmu, list) {
11134       if (!strncasecmp(word, vmu->context, wordlen)) {
11135          if (context && strcmp(context, vmu->context) && ++which > state)
11136             return ast_strdup(vmu->context);
11137          /* ignore repeated contexts ? */
11138          context = vmu->context;
11139       }
11140    }
11141    return NULL;
11142 }
11143 
11144 /*! \brief Show a list of voicemail users in the CLI */
11145 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11146 {
11147    struct ast_vm_user *vmu;
11148 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11149    const char *context = NULL;
11150    int users_counter = 0;
11151 
11152    switch (cmd) {
11153    case CLI_INIT:
11154       e->command = "voicemail show users";
11155       e->usage =
11156          "Usage: voicemail show users [for <context>]\n"
11157          "       Lists all mailboxes currently set up\n";
11158       return NULL;
11159    case CLI_GENERATE:
11160       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11161    }  
11162 
11163    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11164       return CLI_SHOWUSAGE;
11165    if (a->argc == 5) {
11166       if (strcmp(a->argv[3],"for"))
11167          return CLI_SHOWUSAGE;
11168       context = a->argv[4];
11169    }
11170 
11171    if (ast_check_realtime("voicemail")) {
11172       if (!context) {
11173          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11174          return CLI_SHOWUSAGE;
11175       }
11176       return show_users_realtime(a->fd, context);
11177    }
11178 
11179    AST_LIST_LOCK(&users);
11180    if (AST_LIST_EMPTY(&users)) {
11181       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11182       AST_LIST_UNLOCK(&users);
11183       return CLI_FAILURE;
11184    }
11185    if (!context) {
11186       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11187    } else {
11188       int count = 0;
11189       AST_LIST_TRAVERSE(&users, vmu, list) {
11190          if (!strcmp(context, vmu->context)) {
11191             count++;
11192             break;
11193          }
11194       }
11195       if (count) {
11196          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11197       } else {
11198          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11199          AST_LIST_UNLOCK(&users);
11200          return CLI_FAILURE;
11201       }
11202    }
11203    AST_LIST_TRAVERSE(&users, vmu, list) {
11204       int newmsgs = 0, oldmsgs = 0;
11205       char count[12], tmp[256] = "";
11206 
11207       if (!context || !strcmp(context, vmu->context)) {
11208          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11209          inboxcount(tmp, &newmsgs, &oldmsgs);
11210          snprintf(count, sizeof(count), "%d", newmsgs);
11211          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11212          users_counter++;
11213       }
11214    }
11215    AST_LIST_UNLOCK(&users);
11216    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11217    return CLI_SUCCESS;
11218 }
11219 
11220 /*! \brief Show a list of voicemail zones in the CLI */
11221 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11222 {
11223    struct vm_zone *zone;
11224 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11225    char *res = CLI_SUCCESS;
11226 
11227    switch (cmd) {
11228    case CLI_INIT:
11229       e->command = "voicemail show zones";
11230       e->usage =
11231          "Usage: voicemail show zones\n"
11232          "       Lists zone message formats\n";
11233       return NULL;
11234    case CLI_GENERATE:
11235       return NULL;
11236    }
11237 
11238    if (a->argc != 3)
11239       return CLI_SHOWUSAGE;
11240 
11241    AST_LIST_LOCK(&zones);
11242    if (!AST_LIST_EMPTY(&zones)) {
11243       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11244       AST_LIST_TRAVERSE(&zones, zone, list) {
11245          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11246       }
11247    } else {
11248       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11249       res = CLI_FAILURE;
11250    }
11251    AST_LIST_UNLOCK(&zones);
11252 
11253    return res;
11254 }
11255 
11256 /*! \brief Reload voicemail configuration from the CLI */
11257 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11258 {
11259    switch (cmd) {
11260    case CLI_INIT:
11261       e->command = "voicemail reload";
11262       e->usage =
11263          "Usage: voicemail reload\n"
11264          "       Reload voicemail configuration\n";
11265       return NULL;
11266    case CLI_GENERATE:
11267       return NULL;
11268    }
11269 
11270    if (a->argc != 2)
11271       return CLI_SHOWUSAGE;
11272 
11273    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11274    load_config(1);
11275    
11276    return CLI_SUCCESS;
11277 }
11278 
11279 static struct ast_cli_entry cli_voicemail[] = {
11280    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11281    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11282    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11283 };
11284 
11285 #ifdef IMAP_STORAGE
11286    #define DATA_EXPORT_VM_USERS(USER)              \
11287       USER(ast_vm_user, context, AST_DATA_STRING)        \
11288       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11289       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11290       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11291       USER(ast_vm_user, email, AST_DATA_STRING)       \
11292       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11293       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11294       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11295       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11296       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11297       USER(ast_vm_user, language, AST_DATA_STRING)       \
11298       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11299       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11300       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11301       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11302       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11303       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11304       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11305       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11306       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11307       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11308       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11309       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11310       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11311       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11312       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11313 #else
11314    #define DATA_EXPORT_VM_USERS(USER)              \
11315       USER(ast_vm_user, context, AST_DATA_STRING)        \
11316       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11317       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11318       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11319       USER(ast_vm_user, email, AST_DATA_STRING)       \
11320       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11321       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11322       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11323       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11324       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11325       USER(ast_vm_user, language, AST_DATA_STRING)       \
11326       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11327       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11328       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11329       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11330       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11331       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11332       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11333       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11334       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11335       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11336       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11337       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11338 #endif
11339 
11340 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11341 
11342 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11343    ZONE(vm_zone, name, AST_DATA_STRING)      \
11344    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11345    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11346 
11347 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11348 
11349 /*!
11350  * \internal
11351  * \brief Add voicemail user to the data_root.
11352  * \param[in] search The search tree.
11353  * \param[in] data_root The main result node.
11354  * \param[in] user The voicemail user.
11355  */
11356 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11357     struct ast_data *data_root, struct ast_vm_user *user)
11358 {
11359    struct ast_data *data_user, *data_zone;
11360    struct ast_data *data_state;
11361    struct vm_zone *zone = NULL;
11362    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11363    char ext_context[256] = "";
11364 
11365    data_user = ast_data_add_node(data_root, "user");
11366    if (!data_user) {
11367       return -1;
11368    }
11369 
11370    ast_data_add_structure(ast_vm_user, data_user, user);
11371 
11372    AST_LIST_LOCK(&zones);
11373    AST_LIST_TRAVERSE(&zones, zone, list) {
11374       if (!strcmp(zone->name, user->zonetag)) {
11375          break;
11376       }
11377    }
11378    AST_LIST_UNLOCK(&zones);
11379 
11380    /* state */
11381    data_state = ast_data_add_node(data_user, "state");
11382    if (!data_state) {
11383       return -1;
11384    }
11385    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11386    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11387    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11388    ast_data_add_int(data_state, "newmsg", newmsg);
11389    ast_data_add_int(data_state, "oldmsg", oldmsg);
11390 
11391    if (zone) {
11392       data_zone = ast_data_add_node(data_user, "zone");
11393       ast_data_add_structure(vm_zone, data_zone, zone);
11394    }
11395 
11396    if (!ast_data_search_match(search, data_user)) {
11397       ast_data_remove_node(data_root, data_user);
11398    }
11399 
11400    return 0;
11401 }
11402 
11403 static int vm_users_data_provider_get(const struct ast_data_search *search,
11404    struct ast_data *data_root)
11405 {
11406    struct ast_vm_user *user;
11407 
11408    AST_LIST_LOCK(&users);
11409    AST_LIST_TRAVERSE(&users, user, list) {
11410       vm_users_data_provider_get_helper(search, data_root, user);
11411    }
11412    AST_LIST_UNLOCK(&users);
11413 
11414    return 0;
11415 }
11416 
11417 static const struct ast_data_handler vm_users_data_provider = {
11418    .version = AST_DATA_HANDLER_VERSION,
11419    .get = vm_users_data_provider_get
11420 };
11421 
11422 static const struct ast_data_entry vm_data_providers[] = {
11423    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11424 };
11425 
11426 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11427 {
11428    int new = 0, old = 0, urgent = 0;
11429 
11430    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11431 
11432    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11433       mwi_sub->old_urgent = urgent;
11434       mwi_sub->old_new = new;
11435       mwi_sub->old_old = old;
11436       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11437       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11438    }
11439 }
11440 
11441 static void poll_subscribed_mailboxes(void)
11442 {
11443    struct mwi_sub *mwi_sub;
11444 
11445    AST_RWLIST_RDLOCK(&mwi_subs);
11446    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11447       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11448          poll_subscribed_mailbox(mwi_sub);
11449       }
11450    }
11451    AST_RWLIST_UNLOCK(&mwi_subs);
11452 }
11453 
11454 static void *mb_poll_thread(void *data)
11455 {
11456    while (poll_thread_run) {
11457       struct timespec ts = { 0, };
11458       struct timeval wait;
11459 
11460       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11461       ts.tv_sec = wait.tv_sec;
11462       ts.tv_nsec = wait.tv_usec * 1000;
11463 
11464       ast_mutex_lock(&poll_lock);
11465       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11466       ast_mutex_unlock(&poll_lock);
11467 
11468       if (!poll_thread_run)
11469          break;
11470 
11471       poll_subscribed_mailboxes();
11472    }
11473 
11474    return NULL;
11475 }
11476 
11477 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11478 {
11479    ast_free(mwi_sub);
11480 }
11481 
11482 static int handle_unsubscribe(void *datap)
11483 {
11484    struct mwi_sub *mwi_sub;
11485    uint32_t *uniqueid = datap;
11486    
11487    AST_RWLIST_WRLOCK(&mwi_subs);
11488    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11489       if (mwi_sub->uniqueid == *uniqueid) {
11490          AST_LIST_REMOVE_CURRENT(entry);
11491          break;
11492       }
11493    }
11494    AST_RWLIST_TRAVERSE_SAFE_END
11495    AST_RWLIST_UNLOCK(&mwi_subs);
11496 
11497    if (mwi_sub)
11498       mwi_sub_destroy(mwi_sub);
11499 
11500    ast_free(uniqueid);  
11501    return 0;
11502 }
11503 
11504 static int handle_subscribe(void *datap)
11505 {
11506    unsigned int len;
11507    struct mwi_sub *mwi_sub;
11508    struct mwi_sub_task *p = datap;
11509 
11510    len = sizeof(*mwi_sub);
11511    if (!ast_strlen_zero(p->mailbox))
11512       len += strlen(p->mailbox);
11513 
11514    if (!ast_strlen_zero(p->context))
11515       len += strlen(p->context) + 1; /* Allow for seperator */
11516 
11517    if (!(mwi_sub = ast_calloc(1, len)))
11518       return -1;
11519 
11520    mwi_sub->uniqueid = p->uniqueid;
11521    if (!ast_strlen_zero(p->mailbox))
11522       strcpy(mwi_sub->mailbox, p->mailbox);
11523 
11524    if (!ast_strlen_zero(p->context)) {
11525       strcat(mwi_sub->mailbox, "@");
11526       strcat(mwi_sub->mailbox, p->context);
11527    }
11528 
11529    AST_RWLIST_WRLOCK(&mwi_subs);
11530    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11531    AST_RWLIST_UNLOCK(&mwi_subs);
11532    ast_free((void *) p->mailbox);
11533    ast_free((void *) p->context);
11534    ast_free(p);
11535    poll_subscribed_mailbox(mwi_sub);
11536    return 0;
11537 }
11538 
11539 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11540 {
11541    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11542 
11543    if (!uniqueid) {
11544       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11545       return;
11546    }
11547 
11548    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11549       ast_free(uniqueid);
11550       return;
11551    }
11552 
11553    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11554       ast_free(uniqueid);
11555       return;
11556    }
11557 
11558    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11559    *uniqueid = u;
11560    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11561       ast_free(uniqueid);
11562    }
11563 }
11564 
11565 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11566 {
11567    struct mwi_sub_task *mwist;
11568    
11569    if (ast_event_get_type(event) != AST_EVENT_SUB)
11570       return;
11571 
11572    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11573       return;
11574 
11575    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11576       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11577       return;
11578    }
11579    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11580    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11581    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11582    
11583    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11584       ast_free(mwist);
11585    }
11586 }
11587 
11588 static void start_poll_thread(void)
11589 {
11590    int errcode;
11591    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11592       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11593       AST_EVENT_IE_END);
11594 
11595    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11596       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11597       AST_EVENT_IE_END);
11598 
11599    if (mwi_sub_sub)
11600       ast_event_report_subs(mwi_sub_sub);
11601 
11602    poll_thread_run = 1;
11603 
11604    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11605       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11606    }
11607 }
11608 
11609 static void stop_poll_thread(void)
11610 {
11611    poll_thread_run = 0;
11612 
11613    if (mwi_sub_sub) {
11614       ast_event_unsubscribe(mwi_sub_sub);
11615       mwi_sub_sub = NULL;
11616    }
11617 
11618    if (mwi_unsub_sub) {
11619       ast_event_unsubscribe(mwi_unsub_sub);
11620       mwi_unsub_sub = NULL;
11621    }
11622 
11623    ast_mutex_lock(&poll_lock);
11624    ast_cond_signal(&poll_cond);
11625    ast_mutex_unlock(&poll_lock);
11626 
11627    pthread_join(poll_thread, NULL);
11628 
11629    poll_thread = AST_PTHREADT_NULL;
11630 }
11631 
11632 /*! \brief Manager list voicemail users command */
11633 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11634 {
11635    struct ast_vm_user *vmu = NULL;
11636    const char *id = astman_get_header(m, "ActionID");
11637    char actionid[128] = "";
11638 
11639    if (!ast_strlen_zero(id))
11640       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11641 
11642    AST_LIST_LOCK(&users);
11643 
11644    if (AST_LIST_EMPTY(&users)) {
11645       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11646       AST_LIST_UNLOCK(&users);
11647       return RESULT_SUCCESS;
11648    }
11649    
11650    astman_send_ack(s, m, "Voicemail user list will follow");
11651    
11652    AST_LIST_TRAVERSE(&users, vmu, list) {
11653       char dirname[256];
11654 
11655 #ifdef IMAP_STORAGE
11656       int new, old;
11657       inboxcount(vmu->mailbox, &new, &old);
11658 #endif
11659       
11660       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11661       astman_append(s,
11662          "%s"
11663          "Event: VoicemailUserEntry\r\n"
11664          "VMContext: %s\r\n"
11665          "VoiceMailbox: %s\r\n"
11666          "Fullname: %s\r\n"
11667          "Email: %s\r\n"
11668          "Pager: %s\r\n"
11669          "ServerEmail: %s\r\n"
11670          "MailCommand: %s\r\n"
11671          "Language: %s\r\n"
11672          "TimeZone: %s\r\n"
11673          "Callback: %s\r\n"
11674          "Dialout: %s\r\n"
11675          "UniqueID: %s\r\n"
11676          "ExitContext: %s\r\n"
11677          "SayDurationMinimum: %d\r\n"
11678          "SayEnvelope: %s\r\n"
11679          "SayCID: %s\r\n"
11680          "AttachMessage: %s\r\n"
11681          "AttachmentFormat: %s\r\n"
11682          "DeleteMessage: %s\r\n"
11683          "VolumeGain: %.2f\r\n"
11684          "CanReview: %s\r\n"
11685          "CallOperator: %s\r\n"
11686          "MaxMessageCount: %d\r\n"
11687          "MaxMessageLength: %d\r\n"
11688          "NewMessageCount: %d\r\n"
11689 #ifdef IMAP_STORAGE
11690          "OldMessageCount: %d\r\n"
11691          "IMAPUser: %s\r\n"
11692 #endif
11693          "\r\n",
11694          actionid,
11695          vmu->context,
11696          vmu->mailbox,
11697          vmu->fullname,
11698          vmu->email,
11699          vmu->pager,
11700          vmu->serveremail,
11701          vmu->mailcmd,
11702          vmu->language,
11703          vmu->zonetag,
11704          vmu->callback,
11705          vmu->dialout,
11706          vmu->uniqueid,
11707          vmu->exit,
11708          vmu->saydurationm,
11709          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11710          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11711          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11712          vmu->attachfmt,
11713          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11714          vmu->volgain,
11715          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11716          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11717          vmu->maxmsg,
11718          vmu->maxsecs,
11719 #ifdef IMAP_STORAGE
11720          new, old, vmu->imapuser
11721 #else
11722          count_messages(vmu, dirname)
11723 #endif
11724          );
11725    }     
11726    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11727 
11728    AST_LIST_UNLOCK(&users);
11729 
11730    return RESULT_SUCCESS;
11731 }
11732 
11733 /*! \brief Free the users structure. */
11734 static void free_vm_users(void) 
11735 {
11736    struct ast_vm_user *current;
11737    AST_LIST_LOCK(&users);
11738    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11739       ast_set_flag(current, VM_ALLOCED);
11740       free_user(current);
11741    }
11742    AST_LIST_UNLOCK(&users);
11743 }
11744 
11745 /*! \brief Free the zones structure. */
11746 static void free_vm_zones(void)
11747 {
11748    struct vm_zone *zcur;
11749    AST_LIST_LOCK(&zones);
11750    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11751       free_zone(zcur);
11752    AST_LIST_UNLOCK(&zones);
11753 }
11754 
11755 static const char *substitute_escapes(const char *value)
11756 {
11757    char *current;
11758 
11759    /* Add 16 for fudge factor */
11760    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11761 
11762    ast_str_reset(str);
11763    
11764    /* Substitute strings \r, \n, and \t into the appropriate characters */
11765    for (current = (char *) value; *current; current++) {
11766       if (*current == '\\') {
11767          current++;
11768          if (!*current) {
11769             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11770             break;
11771          }
11772          switch (*current) {
11773          case '\\':
11774             ast_str_append(&str, 0, "\\");
11775             break;
11776          case 'r':
11777             ast_str_append(&str, 0, "\r");
11778             break;
11779          case 'n':
11780 #ifdef IMAP_STORAGE
11781             if (!str->used || str->str[str->used - 1] != '\r') {
11782                ast_str_append(&str, 0, "\r");
11783             }
11784 #endif
11785             ast_str_append(&str, 0, "\n");
11786             break;
11787          case 't':
11788             ast_str_append(&str, 0, "\t");
11789             break;
11790          default:
11791             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11792             break;
11793          }
11794       } else {
11795          ast_str_append(&str, 0, "%c", *current);
11796       }
11797    }
11798 
11799    return ast_str_buffer(str);
11800 }
11801 
11802 static int load_config(int reload)
11803 {
11804    struct ast_config *cfg, *ucfg;
11805    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11806    int res;
11807 
11808    ast_unload_realtime("voicemail");
11809    ast_unload_realtime("voicemail_data");
11810 
11811    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11812       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11813          return 0;
11814       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11815          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11816          ucfg = NULL;
11817       }
11818       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11819       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11820          ast_config_destroy(ucfg);
11821          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11822          return 0;
11823       }
11824    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11825       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11826       return 0;
11827    } else {
11828       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11829       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11830          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11831          ucfg = NULL;
11832       }
11833    }
11834 
11835    res = actual_load_config(reload, cfg, ucfg);
11836 
11837    ast_config_destroy(cfg);
11838    ast_config_destroy(ucfg);
11839 
11840    return res;
11841 }
11842 
11843 #ifdef TEST_FRAMEWORK
11844 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11845 {
11846    ast_unload_realtime("voicemail");
11847    ast_unload_realtime("voicemail_data");
11848    return actual_load_config(reload, cfg, ucfg);
11849 }
11850 #endif
11851 
11852 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11853 {
11854    struct ast_vm_user *current;
11855    char *cat;
11856    struct ast_variable *var;
11857    const char *val;
11858    char *q, *stringp, *tmp;
11859    int x;
11860    int tmpadsi[4];
11861    char secretfn[PATH_MAX] = "";
11862 
11863 #ifdef IMAP_STORAGE
11864    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11865 #endif
11866    /* set audio control prompts */
11867    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11868    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11869    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11870    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11871    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11872 
11873    /* Free all the users structure */  
11874    free_vm_users();
11875 
11876    /* Free all the zones structure */
11877    free_vm_zones();
11878 
11879    AST_LIST_LOCK(&users);  
11880 
11881    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11882    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11883 
11884    if (cfg) {
11885       /* General settings */
11886 
11887       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11888          val = "default";
11889       ast_copy_string(userscontext, val, sizeof(userscontext));
11890       /* Attach voice message to mail message ? */
11891       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11892          val = "yes";
11893       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11894 
11895       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11896          val = "no";
11897       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11898 
11899       volgain = 0.0;
11900       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11901          sscanf(val, "%30lf", &volgain);
11902 
11903 #ifdef ODBC_STORAGE
11904       strcpy(odbc_database, "asterisk");
11905       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11906          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11907       }
11908       strcpy(odbc_table, "voicemessages");
11909       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11910          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11911       }
11912 #endif      
11913       /* Mail command */
11914       strcpy(mailcmd, SENDMAIL);
11915       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11916          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11917 
11918       maxsilence = 0;
11919       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11920          maxsilence = atoi(val);
11921          if (maxsilence > 0)
11922             maxsilence *= 1000;
11923       }
11924       
11925       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11926          maxmsg = MAXMSG;
11927       } else {
11928          maxmsg = atoi(val);
11929          if (maxmsg < 0) {
11930             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11931             maxmsg = MAXMSG;
11932          } else if (maxmsg > MAXMSGLIMIT) {
11933             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11934             maxmsg = MAXMSGLIMIT;
11935          }
11936       }
11937 
11938       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11939          maxdeletedmsg = 0;
11940       } else {
11941          if (sscanf(val, "%30d", &x) == 1)
11942             maxdeletedmsg = x;
11943          else if (ast_true(val))
11944             maxdeletedmsg = MAXMSG;
11945          else
11946             maxdeletedmsg = 0;
11947 
11948          if (maxdeletedmsg < 0) {
11949             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11950             maxdeletedmsg = MAXMSG;
11951          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11952             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11953             maxdeletedmsg = MAXMSGLIMIT;
11954          }
11955       }
11956 
11957       /* Load date format config for voicemail mail */
11958       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11959          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11960       }
11961 
11962       /* Load date format config for voicemail pager mail */
11963       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11964          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11965       }
11966 
11967       /* External password changing command */
11968       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11969          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11970          pwdchange = PWDCHANGE_EXTERNAL;
11971       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11972          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11973          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11974       }
11975  
11976       /* External password validation command */
11977       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11978          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11979          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11980       }
11981 
11982 #ifdef IMAP_STORAGE
11983       /* IMAP server address */
11984       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11985          ast_copy_string(imapserver, val, sizeof(imapserver));
11986       } else {
11987          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11988       }
11989       /* IMAP server port */
11990       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11991          ast_copy_string(imapport, val, sizeof(imapport));
11992       } else {
11993          ast_copy_string(imapport, "143", sizeof(imapport));
11994       }
11995       /* IMAP server flags */
11996       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11997          ast_copy_string(imapflags, val, sizeof(imapflags));
11998       }
11999       /* IMAP server master username */
12000       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12001          ast_copy_string(authuser, val, sizeof(authuser));
12002       }
12003       /* IMAP server master password */
12004       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12005          ast_copy_string(authpassword, val, sizeof(authpassword));
12006       }
12007       /* Expunge on exit */
12008       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12009          if (ast_false(val))
12010             expungeonhangup = 0;
12011          else
12012             expungeonhangup = 1;
12013       } else {
12014          expungeonhangup = 1;
12015       }
12016       /* IMAP voicemail folder */
12017       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12018          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12019       } else {
12020          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12021       }
12022       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12023          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12024       }
12025       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12026          imapgreetings = ast_true(val);
12027       } else {
12028          imapgreetings = 0;
12029       }
12030       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12031          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12032       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12033          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12034          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12035       } else {
12036          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12037       }
12038 
12039       /* There is some very unorthodox casting done here. This is due
12040        * to the way c-client handles the argument passed in. It expects a 
12041        * void pointer and casts the pointer directly to a long without
12042        * first dereferencing it. */
12043       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12044          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12045       } else {
12046          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12047       }
12048 
12049       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12050          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12051       } else {
12052          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12053       }
12054 
12055       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12056          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12057       } else {
12058          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12059       }
12060 
12061       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12062          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12063       } else {
12064          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12065       }
12066 
12067       /* Increment configuration version */
12068       imapversion++;
12069 #endif
12070       /* External voicemail notify application */
12071       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12072          ast_copy_string(externnotify, val, sizeof(externnotify));
12073          ast_debug(1, "found externnotify: %s\n", externnotify);
12074       } else {
12075          externnotify[0] = '\0';
12076       }
12077 
12078       /* SMDI voicemail notification */
12079       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12080          ast_debug(1, "Enabled SMDI voicemail notification\n");
12081          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12082             smdi_iface = ast_smdi_interface_find(val);
12083          } else {
12084             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12085             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12086          }
12087          if (!smdi_iface) {
12088             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12089          } 
12090       }
12091 
12092       /* Silence treshold */
12093       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12094       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12095          silencethreshold = atoi(val);
12096       
12097       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12098          val = ASTERISK_USERNAME;
12099       ast_copy_string(serveremail, val, sizeof(serveremail));
12100       
12101       vmmaxsecs = 0;
12102       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12103          if (sscanf(val, "%30d", &x) == 1) {
12104             vmmaxsecs = x;
12105          } else {
12106             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12107          }
12108       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12109          static int maxmessage_deprecate = 0;
12110          if (maxmessage_deprecate == 0) {
12111             maxmessage_deprecate = 1;
12112             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12113          }
12114          if (sscanf(val, "%30d", &x) == 1) {
12115             vmmaxsecs = x;
12116          } else {
12117             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12118          }
12119       }
12120 
12121       vmminsecs = 0;
12122       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12123          if (sscanf(val, "%30d", &x) == 1) {
12124             vmminsecs = x;
12125             if (maxsilence / 1000 >= vmminsecs) {
12126                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12127             }
12128          } else {
12129             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12130          }
12131       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12132          static int maxmessage_deprecate = 0;
12133          if (maxmessage_deprecate == 0) {
12134             maxmessage_deprecate = 1;
12135             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12136          }
12137          if (sscanf(val, "%30d", &x) == 1) {
12138             vmminsecs = x;
12139             if (maxsilence / 1000 >= vmminsecs) {
12140                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12141             }
12142          } else {
12143             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12144          }
12145       }
12146 
12147       val = ast_variable_retrieve(cfg, "general", "format");
12148       if (!val) {
12149          val = "wav";   
12150       } else {
12151          tmp = ast_strdupa(val);
12152          val = ast_format_str_reduce(tmp);
12153          if (!val) {
12154             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12155             val = "wav";
12156          }
12157       }
12158       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12159 
12160       skipms = 3000;
12161       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12162          if (sscanf(val, "%30d", &x) == 1) {
12163             maxgreet = x;
12164          } else {
12165             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12166          }
12167       }
12168 
12169       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12170          if (sscanf(val, "%30d", &x) == 1) {
12171             skipms = x;
12172          } else {
12173             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12174          }
12175       }
12176 
12177       maxlogins = 3;
12178       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12179          if (sscanf(val, "%30d", &x) == 1) {
12180             maxlogins = x;
12181          } else {
12182             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12183          }
12184       }
12185 
12186       minpassword = MINPASSWORD;
12187       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12188          if (sscanf(val, "%30d", &x) == 1) {
12189             minpassword = x;
12190          } else {
12191             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12192          }
12193       }
12194 
12195       /* Force new user to record name ? */
12196       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12197          val = "no";
12198       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12199 
12200       /* Force new user to record greetings ? */
12201       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12202          val = "no";
12203       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12204 
12205       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12206          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12207          stringp = ast_strdupa(val);
12208          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12209             if (!ast_strlen_zero(stringp)) {
12210                q = strsep(&stringp, ",");
12211                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12212                   q++;
12213                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12214                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12215             } else {
12216                cidinternalcontexts[x][0] = '\0';
12217             }
12218          }
12219       }
12220       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12221          ast_debug(1, "VM Review Option disabled globally\n");
12222          val = "no";
12223       }
12224       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12225 
12226       /* Temporary greeting reminder */
12227       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12228          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12229          val = "no";
12230       } else {
12231          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12232       }
12233       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12234       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12235          ast_debug(1, "VM next message wrap disabled globally\n");
12236          val = "no";
12237       }
12238       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12239 
12240       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12241          ast_debug(1, "VM Operator break disabled globally\n");
12242          val = "no";
12243       }
12244       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12245 
12246       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12247          ast_debug(1, "VM CID Info before msg disabled globally\n");
12248          val = "no";
12249       } 
12250       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12251 
12252       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12253          ast_debug(1, "Send Voicemail msg disabled globally\n");
12254          val = "no";
12255       }
12256       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12257    
12258       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12259          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12260          val = "yes";
12261       }
12262       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12263 
12264       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12265          ast_debug(1, "Move Heard enabled globally\n");
12266          val = "yes";
12267       }
12268       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12269 
12270       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12271          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12272          val = "no";
12273       }
12274       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12275 
12276       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12277          ast_debug(1, "Duration info before msg enabled globally\n");
12278          val = "yes";
12279       }
12280       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12281 
12282       saydurationminfo = 2;
12283       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12284          if (sscanf(val, "%30d", &x) == 1) {
12285             saydurationminfo = x;
12286          } else {
12287             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12288          }
12289       }
12290 
12291       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12292          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12293          val = "no";
12294       }
12295       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12296 
12297       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12298          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12299          ast_debug(1, "found dialout context: %s\n", dialcontext);
12300       } else {
12301          dialcontext[0] = '\0';  
12302       }
12303       
12304       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12305          ast_copy_string(callcontext, val, sizeof(callcontext));
12306          ast_debug(1, "found callback context: %s\n", callcontext);
12307       } else {
12308          callcontext[0] = '\0';
12309       }
12310 
12311       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12312          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12313          ast_debug(1, "found operator context: %s\n", exitcontext);
12314       } else {
12315          exitcontext[0] = '\0';
12316       }
12317       
12318       /* load password sounds configuration */
12319       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12320          ast_copy_string(vm_password, val, sizeof(vm_password));
12321       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12322          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12323       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12324          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12325       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12326          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12327       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12328          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12329       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12330          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12331       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12332          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12333       }
12334       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12335          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12336       }
12337       /* load configurable audio prompts */
12338       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12339          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12340       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12341          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12342       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12343          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12344       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12345          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12346       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12347          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12348 
12349       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12350          val = "no";
12351       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12352 
12353       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12354          val = "voicemail.conf";
12355       }
12356       if (!(strcmp(val, "spooldir"))) {
12357          passwordlocation = OPT_PWLOC_SPOOLDIR;
12358       } else {
12359          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12360       }
12361 
12362       poll_freq = DEFAULT_POLL_FREQ;
12363       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12364          if (sscanf(val, "%30u", &poll_freq) != 1) {
12365             poll_freq = DEFAULT_POLL_FREQ;
12366             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12367          }
12368       }
12369 
12370       poll_mailboxes = 0;
12371       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12372          poll_mailboxes = ast_true(val);
12373 
12374       memset(fromstring, 0, sizeof(fromstring));
12375       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12376       strcpy(charset, "ISO-8859-1");
12377       if (emailbody) {
12378          ast_free(emailbody);
12379          emailbody = NULL;
12380       }
12381       if (emailsubject) {
12382          ast_free(emailsubject);
12383          emailsubject = NULL;
12384       }
12385       if (pagerbody) {
12386          ast_free(pagerbody);
12387          pagerbody = NULL;
12388       }
12389       if (pagersubject) {
12390          ast_free(pagersubject);
12391          pagersubject = NULL;
12392       }
12393       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12394          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12395       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12396          ast_copy_string(fromstring, val, sizeof(fromstring));
12397       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12398          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12399       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12400          ast_copy_string(charset, val, sizeof(charset));
12401       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12402          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12403          for (x = 0; x < 4; x++) {
12404             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12405          }
12406       }
12407       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12408          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12409          for (x = 0; x < 4; x++) {
12410             memcpy(&adsisec[x], &tmpadsi[x], 1);
12411          }
12412       }
12413       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12414          if (atoi(val)) {
12415             adsiver = atoi(val);
12416          }
12417       }
12418       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12419          ast_copy_string(zonetag, val, sizeof(zonetag));
12420       }
12421       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12422          ast_copy_string(locale, val, sizeof(locale));
12423       }
12424       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12425          emailsubject = ast_strdup(substitute_escapes(val));
12426       }
12427       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12428          emailbody = ast_strdup(substitute_escapes(val));
12429       }
12430       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12431          pagersubject = ast_strdup(substitute_escapes(val));
12432       }
12433       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12434          pagerbody = ast_strdup(substitute_escapes(val));
12435       }
12436 
12437       /* load mailboxes from users.conf */
12438       if (ucfg) { 
12439          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12440             if (!strcasecmp(cat, "general")) {
12441                continue;
12442             }
12443             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12444                continue;
12445             if ((current = find_or_create(userscontext, cat))) {
12446                populate_defaults(current);
12447                apply_options_full(current, ast_variable_browse(ucfg, cat));
12448                ast_copy_string(current->context, userscontext, sizeof(current->context));
12449                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12450                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12451                }
12452 
12453                switch (current->passwordlocation) {
12454                case OPT_PWLOC_SPOOLDIR:
12455                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12456                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12457                }
12458             }
12459          }
12460       }
12461 
12462       /* load mailboxes from voicemail.conf */
12463       cat = ast_category_browse(cfg, NULL);
12464       while (cat) {
12465          if (strcasecmp(cat, "general")) {
12466             var = ast_variable_browse(cfg, cat);
12467             if (strcasecmp(cat, "zonemessages")) {
12468                /* Process mailboxes in this context */
12469                while (var) {
12470                   append_mailbox(cat, var->name, var->value);
12471                   var = var->next;
12472                }
12473             } else {
12474                /* Timezones in this context */
12475                while (var) {
12476                   struct vm_zone *z;
12477                   if ((z = ast_malloc(sizeof(*z)))) {
12478                      char *msg_format, *tzone;
12479                      msg_format = ast_strdupa(var->value);
12480                      tzone = strsep(&msg_format, "|,");
12481                      if (msg_format) {
12482                         ast_copy_string(z->name, var->name, sizeof(z->name));
12483                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12484                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12485                         AST_LIST_LOCK(&zones);
12486                         AST_LIST_INSERT_HEAD(&zones, z, list);
12487                         AST_LIST_UNLOCK(&zones);
12488                      } else {
12489                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12490                         ast_free(z);
12491                      }
12492                   } else {
12493                      AST_LIST_UNLOCK(&users);
12494                      return -1;
12495                   }
12496                   var = var->next;
12497                }
12498             }
12499          }
12500          cat = ast_category_browse(cfg, cat);
12501       }
12502 
12503       AST_LIST_UNLOCK(&users);
12504 
12505       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12506          start_poll_thread();
12507       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12508          stop_poll_thread();;
12509 
12510       return 0;
12511    } else {
12512       AST_LIST_UNLOCK(&users);
12513       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12514       return 0;
12515    }
12516 }
12517 
12518 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12519 {
12520    int res = -1;
12521    char dir[PATH_MAX];
12522    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12523    ast_debug(2, "About to try retrieving name file %s\n", dir);
12524    RETRIEVE(dir, -1, mailbox, context);
12525    if (ast_fileexists(dir, NULL, NULL)) {
12526       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12527    }
12528    DISPOSE(dir, -1);
12529    return res;
12530 }
12531 
12532 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12533    struct ast_config *pwconf;
12534    struct ast_flags config_flags = { 0 };
12535 
12536    pwconf = ast_config_load(secretfn, config_flags);
12537    if (pwconf) {
12538       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12539       if (val) {
12540          ast_copy_string(password, val, passwordlen);
12541          ast_config_destroy(pwconf);
12542          return;
12543       }
12544       ast_config_destroy(pwconf);
12545    }
12546    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12547 }
12548 
12549 static int write_password_to_file(const char *secretfn, const char *password) {
12550    struct ast_config *conf;
12551    struct ast_category *cat;
12552    struct ast_variable *var;
12553    int res = -1;
12554 
12555    if (!(conf = ast_config_new())) {
12556       ast_log(LOG_ERROR, "Error creating new config structure\n");
12557       return res;
12558    }
12559    if (!(cat = ast_category_new("general", "", 1))) {
12560       ast_log(LOG_ERROR, "Error creating new category structure\n");
12561       ast_config_destroy(conf);
12562       return res;
12563    }
12564    if (!(var = ast_variable_new("password", password, ""))) {
12565       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12566       ast_config_destroy(conf);
12567       ast_category_destroy(cat);
12568       return res;
12569    }
12570    ast_category_append(conf, cat);
12571    ast_variable_append(cat, var);
12572    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12573       res = 0;
12574    } else {
12575       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12576    }
12577 
12578    ast_config_destroy(conf);
12579    return res;
12580 }
12581 
12582 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12583 {
12584    char *context;
12585    char *args_copy;
12586    int res;
12587 
12588    if (ast_strlen_zero(data)) {
12589       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12590       return -1;
12591    }
12592 
12593    args_copy = ast_strdupa(data);
12594    if ((context = strchr(args_copy, '@'))) {
12595       *context++ = '\0';
12596    } else {
12597       context = "default";
12598    }
12599 
12600    if ((res = sayname(chan, args_copy, context) < 0)) {
12601       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12602       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12603       if (!res) {
12604          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12605       }
12606    }
12607 
12608    return res;
12609 }
12610 
12611 #ifdef TEST_FRAMEWORK
12612 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12613 {
12614    return 0;
12615 }
12616 
12617 static struct ast_frame *fake_read(struct ast_channel *ast)
12618 {
12619    return &ast_null_frame;
12620 }
12621 
12622 AST_TEST_DEFINE(test_voicemail_vmsayname)
12623 {
12624    char dir[PATH_MAX];
12625    char dir2[PATH_MAX];
12626    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12627    static const char TEST_EXTENSION[] = "1234";
12628 
12629    struct ast_channel *test_channel1 = NULL;
12630    int res = -1;
12631 
12632    static const struct ast_channel_tech fake_tech = {
12633       .write = fake_write,
12634       .read = fake_read,
12635    };
12636 
12637    switch (cmd) {
12638    case TEST_INIT:
12639       info->name = "vmsayname_exec";
12640       info->category = "/apps/app_voicemail/";
12641       info->summary = "Vmsayname unit test";
12642       info->description =
12643          "This tests passing various parameters to vmsayname";
12644       return AST_TEST_NOT_RUN;
12645    case TEST_EXECUTE:
12646       break;
12647    }
12648 
12649    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12650         NULL, NULL, 0, 0, "TestChannel1"))) {
12651       goto exit_vmsayname_test;
12652    }
12653 
12654    /* normally this is done in the channel driver */
12655    test_channel1->nativeformats = AST_FORMAT_GSM;
12656    test_channel1->writeformat = AST_FORMAT_GSM;
12657    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12658    test_channel1->readformat = AST_FORMAT_GSM;
12659    test_channel1->rawreadformat = AST_FORMAT_GSM;
12660    test_channel1->tech = &fake_tech;
12661 
12662    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12663    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12664    if (!(res = vmsayname_exec(test_channel1, dir))) {
12665       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12666       if (ast_fileexists(dir, NULL, NULL)) {
12667          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12668          res = -1;
12669          goto exit_vmsayname_test;
12670       } else {
12671          /* no greeting already exists as expected, let's create one to fully test sayname */
12672          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12673             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12674             goto exit_vmsayname_test;
12675          }
12676          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12677          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12678          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12679          if ((res = symlink(dir, dir2))) {
12680             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12681             goto exit_vmsayname_test;
12682          }
12683          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12684          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12685          res = vmsayname_exec(test_channel1, dir);
12686 
12687          /* TODO: there may be a better way to do this */
12688          unlink(dir2);
12689          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12690          rmdir(dir2);
12691          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12692          rmdir(dir2);
12693       }
12694    }
12695 
12696 exit_vmsayname_test:
12697 
12698    if (test_channel1) {
12699       ast_hangup(test_channel1);
12700    }
12701 
12702    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12703 }
12704 
12705 AST_TEST_DEFINE(test_voicemail_msgcount)
12706 {
12707    int i, j, res = AST_TEST_PASS, syserr;
12708    struct ast_vm_user *vmu;
12709    struct vm_state vms;
12710 #ifdef IMAP_STORAGE
12711    struct ast_channel *chan = NULL;
12712 #endif
12713    struct {
12714       char dir[256];
12715       char file[256];
12716       char txtfile[256];
12717    } tmp[3];
12718    char syscmd[256];
12719    const char origweasels[] = "tt-weasels";
12720    const char testcontext[] = "test";
12721    const char testmailbox[] = "00000000";
12722    const char testspec[] = "00000000@test";
12723    FILE *txt;
12724    int new, old, urgent;
12725    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12726    const int folder2mbox[3] = { 1, 11, 0 };
12727    const int expected_results[3][12] = {
12728       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12729       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12730       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12731       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12732    };
12733 
12734    switch (cmd) {
12735    case TEST_INIT:
12736       info->name = "test_voicemail_msgcount";
12737       info->category = "/apps/app_voicemail/";
12738       info->summary = "Test Voicemail status checks";
12739       info->description =
12740          "Verify that message counts are correct when retrieved through the public API";
12741       return AST_TEST_NOT_RUN;
12742    case TEST_EXECUTE:
12743       break;
12744    }
12745 
12746    /* Make sure the original path was completely empty */
12747    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12748    if ((syserr = ast_safe_system(syscmd))) {
12749       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12750          syserr > 0 ? strerror(syserr) : "unable to fork()");
12751       return AST_TEST_FAIL;
12752    }
12753 
12754 #ifdef IMAP_STORAGE
12755    if (!(chan = ast_dummy_channel_alloc())) {
12756       ast_test_status_update(test, "Unable to create dummy channel\n");
12757       return AST_TEST_FAIL;
12758    }
12759 #endif
12760 
12761    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12762       !(vmu = find_or_create(testcontext, testmailbox))) {
12763       ast_test_status_update(test, "Cannot create vmu structure\n");
12764       ast_unreplace_sigchld();
12765 #ifdef IMAP_STORAGE
12766       chan = ast_channel_unref(chan);
12767 #endif
12768       return AST_TEST_FAIL;
12769    }
12770 
12771    populate_defaults(vmu);
12772    memset(&vms, 0, sizeof(vms));
12773 
12774    /* Create temporary voicemail */
12775    for (i = 0; i < 3; i++) {
12776       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12777       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12778       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12779 
12780       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12781          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12782             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12783          if ((syserr = ast_safe_system(syscmd))) {
12784             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12785                syserr > 0 ? strerror(syserr) : "unable to fork()");
12786             ast_unreplace_sigchld();
12787 #ifdef IMAP_STORAGE
12788             chan = ast_channel_unref(chan);
12789 #endif
12790             return AST_TEST_FAIL;
12791          }
12792       }
12793 
12794       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12795          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12796          fclose(txt);
12797       } else {
12798          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12799          res = AST_TEST_FAIL;
12800          break;
12801       }
12802       open_mailbox(&vms, vmu, folder2mbox[i]);
12803       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12804 
12805       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12806       for (j = 0; j < 3; j++) {
12807          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12808          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12809             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12810                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12811             res = AST_TEST_FAIL;
12812          }
12813       }
12814 
12815       new = old = urgent = 0;
12816       if (ast_app_inboxcount(testspec, &new, &old)) {
12817          ast_test_status_update(test, "inboxcount returned failure\n");
12818          res = AST_TEST_FAIL;
12819       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12820          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12821             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12822          res = AST_TEST_FAIL;
12823       }
12824 
12825       new = old = urgent = 0;
12826       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12827          ast_test_status_update(test, "inboxcount2 returned failure\n");
12828          res = AST_TEST_FAIL;
12829       } else if (old != expected_results[i][6 + 0] ||
12830             urgent != expected_results[i][6 + 1] ||
12831                new != expected_results[i][6 + 2]    ) {
12832          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12833             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12834          res = AST_TEST_FAIL;
12835       }
12836 
12837       new = old = urgent = 0;
12838       for (j = 0; j < 3; j++) {
12839          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12840             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12841                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12842             res = AST_TEST_FAIL;
12843          }
12844       }
12845    }
12846 
12847    for (i = 0; i < 3; i++) {
12848       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12849        * server, in which case, the rm below will not affect the
12850        * voicemails. */
12851       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12852       DISPOSE(tmp[i].dir, 0);
12853    }
12854 
12855    if (vms.deleted) {
12856       ast_free(vms.deleted);
12857    }
12858    if (vms.heard) {
12859       ast_free(vms.heard);
12860    }
12861 
12862 #ifdef IMAP_STORAGE
12863    chan = ast_channel_unref(chan);
12864 #endif
12865 
12866    /* And remove test directory */
12867    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12868    if ((syserr = ast_safe_system(syscmd))) {
12869       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12870          syserr > 0 ? strerror(syserr) : "unable to fork()");
12871    }
12872 
12873    return res;
12874 }
12875 
12876 AST_TEST_DEFINE(test_voicemail_notify_endl)
12877 {
12878    int res = AST_TEST_PASS;
12879    char testcontext[] = "test";
12880    char testmailbox[] = "00000000";
12881    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12882    char attach[256], attach2[256];
12883    char buf[256] = ""; /* No line should actually be longer than 80 */
12884    struct ast_channel *chan = NULL;
12885    struct ast_vm_user *vmu, vmus = {
12886       .flags = 0,
12887    };
12888    FILE *file;
12889    struct {
12890       char *name;
12891       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12892       void *location;
12893       union {
12894          int intval;
12895          char *strval;
12896       } u;
12897    } test_items[] = {
12898       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12899       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12900       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12901       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12902       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12903       { "attach2", STRPTR, attach2, .u.strval = "" },
12904       { "attach", STRPTR, attach, .u.strval = "" },
12905    };
12906    int which;
12907 
12908    switch (cmd) {
12909    case TEST_INIT:
12910       info->name = "test_voicemail_notify_endl";
12911       info->category = "/apps/app_voicemail/";
12912       info->summary = "Test Voicemail notification end-of-line";
12913       info->description =
12914          "Verify that notification emails use a consistent end-of-line character";
12915       return AST_TEST_NOT_RUN;
12916    case TEST_EXECUTE:
12917       break;
12918    }
12919 
12920    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12921    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12922 
12923    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12924       !(vmu = find_or_create(testcontext, testmailbox))) {
12925       ast_test_status_update(test, "Cannot create vmu structure\n");
12926       return AST_TEST_NOT_RUN;
12927    }
12928 
12929    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12930       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12931       return AST_TEST_NOT_RUN;
12932    }
12933 
12934    populate_defaults(vmu);
12935    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12936 #ifdef IMAP_STORAGE
12937    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12938 #endif
12939 
12940    file = tmpfile();
12941    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12942       /* Kill previous test, if any */
12943       rewind(file);
12944       if (ftruncate(fileno(file), 0)) {
12945          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12946          res = AST_TEST_FAIL;
12947          break;
12948       }
12949 
12950       /* Make each change, in order, to the test mailbox */
12951       if (test_items[which].type == INT) {
12952          *((int *) test_items[which].location) = test_items[which].u.intval;
12953       } else if (test_items[which].type == FLAGVAL) {
12954          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12955             ast_clear_flag(vmu, test_items[which].u.intval);
12956          } else {
12957             ast_set_flag(vmu, test_items[which].u.intval);
12958          }
12959       } else if (test_items[which].type == STATIC) {
12960          strcpy(test_items[which].location, test_items[which].u.strval);
12961       } else if (test_items[which].type == STRPTR) {
12962          test_items[which].location = test_items[which].u.strval;
12963       }
12964 
12965       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12966       rewind(file);
12967       while (fgets(buf, sizeof(buf), file)) {
12968          if (
12969 #ifdef IMAP_STORAGE
12970          buf[strlen(buf) - 2] != '\r'
12971 #else
12972          buf[strlen(buf) - 2] == '\r'
12973 #endif
12974          || buf[strlen(buf) - 1] != '\n') {
12975             res = AST_TEST_FAIL;
12976          }
12977       }
12978    }
12979    fclose(file);
12980    return res;
12981 }
12982 
12983 AST_TEST_DEFINE(test_voicemail_load_config)
12984 {
12985    int res = AST_TEST_PASS;
12986    struct ast_vm_user *vmu;
12987    struct ast_config *cfg;
12988    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12989    int fd;
12990    FILE *file;
12991    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12992 
12993    switch (cmd) {
12994    case TEST_INIT:
12995       info->name = "test_voicemail_load_config";
12996       info->category = "/apps/app_voicemail/";
12997       info->summary = "Test loading Voicemail config";
12998       info->description =
12999          "Verify that configuration is loaded consistently. "
13000          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13001          "some options were loaded after the mailboxes were instantiated, causing "
13002          "those options not to be set correctly.";
13003       return AST_TEST_NOT_RUN;
13004    case TEST_EXECUTE:
13005       break;
13006    }
13007 
13008    /* build a config file by hand... */
13009    if ((fd = mkstemp(config_filename)) < 0) {
13010       return AST_TEST_FAIL;
13011    }
13012    if (!(file = fdopen(fd, "w"))) {
13013       close(fd);
13014       unlink(config_filename);
13015       return AST_TEST_FAIL;
13016    }
13017    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13018    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13019    fputs("00000002 => 9999,Mrs. Test\n", file);
13020    fclose(file);
13021 
13022    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13023       res = AST_TEST_FAIL;
13024       goto cleanup;
13025    }
13026 
13027    load_config_from_memory(1, cfg, NULL);
13028    ast_config_destroy(cfg);
13029 
13030 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13031    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13032    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13033 
13034    AST_LIST_LOCK(&users);
13035    AST_LIST_TRAVERSE(&users, vmu, list) {
13036       if (!strcmp(vmu->mailbox, "00000001")) {
13037          if (0); /* trick to get CHECK to work */
13038          CHECK(vmu, callback, "othercontext")
13039          CHECK(vmu, locale, "nl_NL.UTF-8")
13040          CHECK(vmu, zonetag, "central")
13041       } else if (!strcmp(vmu->mailbox, "00000002")) {
13042          if (0); /* trick to get CHECK to work */
13043          CHECK(vmu, callback, "somecontext")
13044          CHECK(vmu, locale, "de_DE.UTF-8")
13045          CHECK(vmu, zonetag, "european")
13046       }
13047    }
13048    AST_LIST_UNLOCK(&users);
13049 
13050 #undef CHECK
13051 
13052    /* restore config */
13053    load_config(1); /* this might say "Failed to load configuration file." */
13054 
13055 cleanup:
13056    unlink(config_filename);
13057    return res;
13058 }
13059 
13060 #endif /* defined(TEST_FRAMEWORK) */
13061 
13062 static int reload(void)
13063 {
13064    return load_config(1);
13065 }
13066 
13067 static int unload_module(void)
13068 {
13069    int res;
13070 
13071    res = ast_unregister_application(app);
13072    res |= ast_unregister_application(app2);
13073    res |= ast_unregister_application(app3);
13074    res |= ast_unregister_application(app4);
13075    res |= ast_unregister_application(sayname_app);
13076    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13077    res |= ast_manager_unregister("VoicemailUsersList");
13078    res |= ast_data_unregister(NULL);
13079 #ifdef TEST_FRAMEWORK
13080    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13081    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13082    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13083    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13084    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13085 #endif
13086    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13087    ast_uninstall_vm_functions();
13088    ao2_ref(inprocess_container, -1);
13089 
13090    if (poll_thread != AST_PTHREADT_NULL)
13091       stop_poll_thread();
13092 
13093    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13094    ast_unload_realtime("voicemail");
13095    ast_unload_realtime("voicemail_data");
13096 
13097    free_vm_users();
13098    free_vm_zones();
13099    return res;
13100 }
13101 
13102 static int load_module(void)
13103 {
13104    int res;
13105    my_umask = umask(0);
13106    umask(my_umask);
13107 
13108    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13109       return AST_MODULE_LOAD_DECLINE;
13110    }
13111 
13112    /* compute the location of the voicemail spool directory */
13113    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13114    
13115    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13116       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13117    }
13118 
13119    if ((res = load_config(0)))
13120       return res;
13121 
13122    res = ast_register_application_xml(app, vm_exec);
13123    res |= ast_register_application_xml(app2, vm_execmain);
13124    res |= ast_register_application_xml(app3, vm_box_exists);
13125    res |= ast_register_application_xml(app4, vmauthenticate);
13126    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13127    res |= ast_custom_function_register(&mailbox_exists_acf);
13128    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13129 #ifdef TEST_FRAMEWORK
13130    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13131    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13132    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13133    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13134    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13135 #endif
13136 
13137    if (res)
13138       return res;
13139 
13140    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13141    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13142 
13143    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13144    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13145    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13146 
13147    return res;
13148 }
13149 
13150 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13151 {
13152    int cmd = 0;
13153    char destination[80] = "";
13154    int retries = 0;
13155 
13156    if (!num) {
13157       ast_verb(3, "Destination number will be entered manually\n");
13158       while (retries < 3 && cmd != 't') {
13159          destination[1] = '\0';
13160          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13161          if (!cmd)
13162             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13163          if (!cmd)
13164             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13165          if (!cmd) {
13166             cmd = ast_waitfordigit(chan, 6000);
13167             if (cmd)
13168                destination[0] = cmd;
13169          }
13170          if (!cmd) {
13171             retries++;
13172          } else {
13173 
13174             if (cmd < 0)
13175                return 0;
13176             if (cmd == '*') {
13177                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13178                return 0;
13179             }
13180             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13181                retries++;
13182             else
13183                cmd = 't';
13184          }
13185          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13186       }
13187       if (retries >= 3) {
13188          return 0;
13189       }
13190       
13191    } else {
13192       if (option_verbose > 2)
13193          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13194       ast_copy_string(destination, num, sizeof(destination));
13195    }
13196 
13197    if (!ast_strlen_zero(destination)) {
13198       if (destination[strlen(destination) -1 ] == '*')
13199          return 0; 
13200       if (option_verbose > 2)
13201          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13202       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13203       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13204       chan->priority = 0;
13205       return 9;
13206    }
13207    return 0;
13208 }
13209 
13210 /*!
13211  * \brief The advanced options within a message.
13212  * \param chan
13213  * \param vmu 
13214  * \param vms
13215  * \param msg
13216  * \param option
13217  * \param record_gain
13218  *
13219  * Provides handling for the play message envelope, call the person back, or reply to message. 
13220  *
13221  * \return zero on success, -1 on error.
13222  */
13223 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)
13224 {
13225    int res = 0;
13226    char filename[PATH_MAX];
13227    struct ast_config *msg_cfg = NULL;
13228    const char *origtime, *context;
13229    char *name, *num;
13230    int retries = 0;
13231    char *cid;
13232    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13233 
13234    vms->starting = 0; 
13235 
13236    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13237 
13238    /* Retrieve info from VM attribute file */
13239    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13240    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13241    msg_cfg = ast_config_load(filename, config_flags);
13242    DISPOSE(vms->curdir, vms->curmsg);
13243    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13244       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13245       return 0;
13246    }
13247 
13248    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13249       ast_config_destroy(msg_cfg);
13250       return 0;
13251    }
13252 
13253    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13254 
13255    context = ast_variable_retrieve(msg_cfg, "message", "context");
13256    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13257       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13258    switch (option) {
13259    case 3: /* Play message envelope */
13260       if (!res)
13261          res = play_message_datetime(chan, vmu, origtime, filename);
13262       if (!res)
13263          res = play_message_callerid(chan, vms, cid, context, 0);
13264 
13265       res = 't';
13266       break;
13267 
13268    case 2:  /* Call back */
13269 
13270       if (ast_strlen_zero(cid))
13271          break;
13272 
13273       ast_callerid_parse(cid, &name, &num);
13274       while ((res > -1) && (res != 't')) {
13275          switch (res) {
13276          case '1':
13277             if (num) {
13278                /* Dial the CID number */
13279                res = dialout(chan, vmu, num, vmu->callback);
13280                if (res) {
13281                   ast_config_destroy(msg_cfg);
13282                   return 9;
13283                }
13284             } else {
13285                res = '2';
13286             }
13287             break;
13288 
13289          case '2':
13290             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13291             if (!ast_strlen_zero(vmu->dialout)) {
13292                res = dialout(chan, vmu, NULL, vmu->dialout);
13293                if (res) {
13294                   ast_config_destroy(msg_cfg);
13295                   return 9;
13296                }
13297             } else {
13298                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13299                res = ast_play_and_wait(chan, "vm-sorry");
13300             }
13301             ast_config_destroy(msg_cfg);
13302             return res;
13303          case '*':
13304             res = 't';
13305             break;
13306          case '3':
13307          case '4':
13308          case '5':
13309          case '6':
13310          case '7':
13311          case '8':
13312          case '9':
13313          case '0':
13314 
13315             res = ast_play_and_wait(chan, "vm-sorry");
13316             retries++;
13317             break;
13318          default:
13319             if (num) {
13320                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13321                res = ast_play_and_wait(chan, "vm-num-i-have");
13322                if (!res)
13323                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13324                if (!res)
13325                   res = ast_play_and_wait(chan, "vm-tocallnum");
13326                /* Only prompt for a caller-specified number if there is a dialout context specified */
13327                if (!ast_strlen_zero(vmu->dialout)) {
13328                   if (!res)
13329                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13330                }
13331             } else {
13332                res = ast_play_and_wait(chan, "vm-nonumber");
13333                if (!ast_strlen_zero(vmu->dialout)) {
13334                   if (!res)
13335                      res = ast_play_and_wait(chan, "vm-toenternumber");
13336                }
13337             }
13338             if (!res) {
13339                res = ast_play_and_wait(chan, "vm-star-cancel");
13340             }
13341             if (!res) {
13342                res = ast_waitfordigit(chan, 6000);
13343             }
13344             if (!res) {
13345                retries++;
13346                if (retries > 3) {
13347                   res = 't';
13348                }
13349             }
13350             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13351             break; 
13352             
13353          }
13354          if (res == 't')
13355             res = 0;
13356          else if (res == '*')
13357             res = -1;
13358       }
13359       break;
13360       
13361    case 1:  /* Reply */
13362       /* Send reply directly to sender */
13363       if (ast_strlen_zero(cid))
13364          break;
13365 
13366       ast_callerid_parse(cid, &name, &num);
13367       if (!num) {
13368          ast_verb(3, "No CID number available, no reply sent\n");
13369          if (!res)
13370             res = ast_play_and_wait(chan, "vm-nonumber");
13371          ast_config_destroy(msg_cfg);
13372          return res;
13373       } else {
13374          struct ast_vm_user vmu2;
13375          if (find_user(&vmu2, vmu->context, num)) {
13376             struct leave_vm_options leave_options;
13377             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13378             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13379 
13380             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13381             
13382             memset(&leave_options, 0, sizeof(leave_options));
13383             leave_options.record_gain = record_gain;
13384             res = leave_voicemail(chan, mailbox, &leave_options);
13385             if (!res)
13386                res = 't';
13387             ast_config_destroy(msg_cfg);
13388             return res;
13389          } else {
13390             /* Sender has no mailbox, can't reply */
13391             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13392             ast_play_and_wait(chan, "vm-nobox");
13393             res = 't';
13394             ast_config_destroy(msg_cfg);
13395             return res;
13396          }
13397       } 
13398       res = 0;
13399 
13400       break;
13401    }
13402 
13403 #ifndef IMAP_STORAGE
13404    ast_config_destroy(msg_cfg);
13405 
13406    if (!res) {
13407       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13408       vms->heard[msg] = 1;
13409       res = wait_file(chan, vms, vms->fn);
13410    }
13411 #endif
13412    return res;
13413 }
13414 
13415 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13416          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13417          signed char record_gain, struct vm_state *vms, char *flag)
13418 {
13419    /* Record message & let caller review or re-record it, or set options if applicable */
13420    int res = 0;
13421    int cmd = 0;
13422    int max_attempts = 3;
13423    int attempts = 0;
13424    int recorded = 0;
13425    int msg_exists = 0;
13426    signed char zero_gain = 0;
13427    char tempfile[PATH_MAX];
13428    char *acceptdtmf = "#";
13429    char *canceldtmf = "";
13430    int canceleddtmf = 0;
13431 
13432    /* Note that urgent and private are for flagging messages as such in the future */
13433 
13434    /* barf if no pointer passed to store duration in */
13435    if (duration == NULL) {
13436       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13437       return -1;
13438    }
13439 
13440    if (!outsidecaller)
13441       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13442    else
13443       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13444 
13445    cmd = '3';  /* Want to start by recording */
13446 
13447    while ((cmd >= 0) && (cmd != 't')) {
13448       switch (cmd) {
13449       case '1':
13450          if (!msg_exists) {
13451             /* In this case, 1 is to record a message */
13452             cmd = '3';
13453             break;
13454          } else {
13455             /* Otherwise 1 is to save the existing message */
13456             ast_verb(3, "Saving message as is\n");
13457             if (!outsidecaller) 
13458                ast_filerename(tempfile, recordfile, NULL);
13459             ast_stream_and_wait(chan, "vm-msgsaved", "");
13460             if (!outsidecaller) {
13461                /* Saves to IMAP server only if imapgreeting=yes */
13462                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13463                DISPOSE(recordfile, -1);
13464             }
13465             cmd = 't';
13466             return res;
13467          }
13468       case '2':
13469          /* Review */
13470          ast_verb(3, "Reviewing the message\n");
13471          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13472          break;
13473       case '3':
13474          msg_exists = 0;
13475          /* Record */
13476          if (recorded == 1) 
13477             ast_verb(3, "Re-recording the message\n");
13478          else  
13479             ast_verb(3, "Recording the message\n");
13480          
13481          if (recorded && outsidecaller) {
13482             cmd = ast_play_and_wait(chan, INTRO);
13483             cmd = ast_play_and_wait(chan, "beep");
13484          }
13485          recorded = 1;
13486          /* 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 */
13487          if (record_gain)
13488             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13489          if (ast_test_flag(vmu, VM_OPERATOR))
13490             canceldtmf = "0";
13491          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13492          if (strchr(canceldtmf, cmd)) {
13493          /* need this flag here to distinguish between pressing '0' during message recording or after */
13494             canceleddtmf = 1;
13495          }
13496          if (record_gain)
13497             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13498          if (cmd == -1) {
13499             /* User has hung up, no options to give */
13500             if (!outsidecaller) {
13501                /* user was recording a greeting and they hung up, so let's delete the recording. */
13502                ast_filedelete(tempfile, NULL);
13503             }     
13504             return cmd;
13505          }
13506          if (cmd == '0') {
13507             break;
13508          } else if (cmd == '*') {
13509             break;
13510 #if 0
13511          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13512             /* Message is too short */
13513             ast_verb(3, "Message too short\n");
13514             cmd = ast_play_and_wait(chan, "vm-tooshort");
13515             cmd = ast_filedelete(tempfile, NULL);
13516             break;
13517          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13518             /* Message is all silence */
13519             ast_verb(3, "Nothing recorded\n");
13520             cmd = ast_filedelete(tempfile, NULL);
13521             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13522             if (!cmd)
13523                cmd = ast_play_and_wait(chan, "vm-speakup");
13524             break;
13525 #endif
13526          } else {
13527             /* If all is well, a message exists */
13528             msg_exists = 1;
13529             cmd = 0;
13530          }
13531          break;
13532       case '4':
13533          if (outsidecaller) {  /* only mark vm messages */
13534             /* Mark Urgent */
13535             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13536                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13537                res = ast_play_and_wait(chan, "vm-marked-urgent");
13538                strcpy(flag, "Urgent");
13539             } else if (flag) {
13540                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13541                res = ast_play_and_wait(chan, "vm-urgent-removed");
13542                strcpy(flag, "");
13543             } else {
13544                ast_play_and_wait(chan, "vm-sorry");
13545             }
13546             cmd = 0;
13547          } else {
13548             cmd = ast_play_and_wait(chan, "vm-sorry");
13549          }
13550          break;
13551       case '5':
13552       case '6':
13553       case '7':
13554       case '8':
13555       case '9':
13556       case '*':
13557       case '#':
13558          cmd = ast_play_and_wait(chan, "vm-sorry");
13559          break;
13560 #if 0 
13561 /*  XXX Commented out for the moment because of the dangers of deleting
13562     a message while recording (can put the message numbers out of sync) */
13563       case '*':
13564          /* Cancel recording, delete message, offer to take another message*/
13565          cmd = ast_play_and_wait(chan, "vm-deleted");
13566          cmd = ast_filedelete(tempfile, NULL);
13567          if (outsidecaller) {
13568             res = vm_exec(chan, NULL);
13569             return res;
13570          }
13571          else
13572             return 1;
13573 #endif
13574       case '0':
13575          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13576             cmd = ast_play_and_wait(chan, "vm-sorry");
13577             break;
13578          }
13579          if (msg_exists || recorded) {
13580             cmd = ast_play_and_wait(chan, "vm-saveoper");
13581             if (!cmd)
13582                cmd = ast_waitfordigit(chan, 3000);
13583             if (cmd == '1') {
13584                ast_filerename(tempfile, recordfile, NULL);
13585                ast_play_and_wait(chan, "vm-msgsaved");
13586                cmd = '0';
13587             } else if (cmd == '4') {
13588                if (flag) {
13589                   ast_play_and_wait(chan, "vm-marked-urgent");
13590                   strcpy(flag, "Urgent");
13591                }
13592                ast_play_and_wait(chan, "vm-msgsaved");
13593                cmd = '0';
13594             } else {
13595                ast_play_and_wait(chan, "vm-deleted");
13596                DELETE(tempfile, -1, tempfile, vmu);
13597                cmd = '0';
13598             }
13599          }
13600          return cmd;
13601       default:
13602          /* If the caller is an ouside caller, and the review option is enabled,
13603             allow them to review the message, but let the owner of the box review
13604             their OGM's */
13605          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13606             return cmd;
13607          if (msg_exists) {
13608             cmd = ast_play_and_wait(chan, "vm-review");
13609             if (!cmd && outsidecaller) {
13610                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13611                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13612                } else if (flag) {
13613                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13614                }
13615             }
13616          } else {
13617             cmd = ast_play_and_wait(chan, "vm-torerecord");
13618             if (!cmd)
13619                cmd = ast_waitfordigit(chan, 600);
13620          }
13621          
13622          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13623             cmd = ast_play_and_wait(chan, "vm-reachoper");
13624             if (!cmd)
13625                cmd = ast_waitfordigit(chan, 600);
13626          }
13627 #if 0
13628          if (!cmd)
13629             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13630 #endif
13631          if (!cmd)
13632             cmd = ast_waitfordigit(chan, 6000);
13633          if (!cmd) {
13634             attempts++;
13635          }
13636          if (attempts > max_attempts) {
13637             cmd = 't';
13638          }
13639       }
13640    }
13641    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13642       /* Hang up or timeout, so delete the recording. */
13643       ast_filedelete(tempfile, NULL);
13644    }
13645 
13646    if (cmd != 't' && outsidecaller)
13647       ast_play_and_wait(chan, "vm-goodbye");
13648 
13649    return cmd;
13650 }
13651 
13652 /* This is a workaround so that menuselect displays a proper description
13653  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13654  */
13655 
13656 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13657       .load = load_module,
13658       .unload = unload_module,
13659       .reload = reload,
13660       .nonoptreq = "res_adsi,res_smdi",
13661       );

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