Wed Jan 27 20:02:03 2016

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: 426691 $")
00068 
00069 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00070 #include <sys/time.h>
00071 #include <sys/stat.h>
00072 #include <sys/mman.h>
00073 #include <time.h>
00074 #include <dirent.h>
00075 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00076 #include <sys/wait.h>
00077 #endif
00078 
00079 #include "asterisk/logger.h"
00080 #include "asterisk/lock.h"
00081 #include "asterisk/file.h"
00082 #include "asterisk/channel.h"
00083 #include "asterisk/pbx.h"
00084 #include "asterisk/config.h"
00085 #include "asterisk/say.h"
00086 #include "asterisk/module.h"
00087 #include "asterisk/adsi.h"
00088 #include "asterisk/app.h"
00089 #include "asterisk/manager.h"
00090 #include "asterisk/dsp.h"
00091 #include "asterisk/localtime.h"
00092 #include "asterisk/cli.h"
00093 #include "asterisk/utils.h"
00094 #include "asterisk/stringfields.h"
00095 #include "asterisk/smdi.h"
00096 #include "asterisk/astobj2.h"
00097 #include "asterisk/event.h"
00098 #include "asterisk/taskprocessor.h"
00099 #include "asterisk/test.h"
00100 
00101 #ifdef ODBC_STORAGE
00102 #include "asterisk/res_odbc.h"
00103 #endif
00104 
00105 #ifdef IMAP_STORAGE
00106 #include "asterisk/threadstorage.h"
00107 #endif
00108 
00109 /*** DOCUMENTATION
00110    <application name="VoiceMail" language="en_US">
00111       <synopsis>
00112          Leave a Voicemail message.
00113       </synopsis>
00114       <syntax>
00115          <parameter name="mailboxs" argsep="&amp;" required="true">
00116             <argument name="mailbox1" argsep="@" required="true">
00117                <argument name="mailbox" required="true" />
00118                <argument name="context" />
00119             </argument>
00120             <argument name="mailbox2" argsep="@" multiple="true">
00121                <argument name="mailbox" required="true" />
00122                <argument name="context" />
00123             </argument>
00124          </parameter>
00125          <parameter name="options">
00126             <optionlist>
00127                <option name="b">
00128                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00129                </option>
00130                <option name="d">
00131                   <argument name="c" />
00132                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00133                   if played during the greeting. Context defaults to the current context.</para>
00134                </option>
00135                <option name="g">
00136                   <argument name="#" required="true" />
00137                   <para>Use the specified amount of gain when recording the voicemail
00138                   message. The units are whole-number decibels (dB). Only works on supported
00139                   technologies, which is DAHDI only.</para>
00140                </option>
00141                <option name="s">
00142                   <para>Skip the playback of instructions for leaving a message to the
00143                   calling party.</para>
00144                </option>
00145                <option name="u">
00146                   <para>Play the <literal>unavailable</literal> greeting.</para>
00147                </option>
00148                <option name="U">
00149                   <para>Mark message as <literal>URGENT</literal>.</para>
00150                </option>
00151                <option name="P">
00152                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00153                </option>
00154             </optionlist>
00155          </parameter>
00156       </syntax>
00157       <description>
00158          <para>This application allows the calling party to leave a message for the specified
00159          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00160          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00161          exist.</para>
00162          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00163          <enumlist>
00164             <enum name="0">
00165                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00166             </enum>
00167             <enum name="*">
00168                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00169             </enum>
00170          </enumlist>
00171          <para>This application will set the following channel variable upon completion:</para>
00172          <variablelist>
00173             <variable name="VMSTATUS">
00174                <para>This indicates the status of the execution of the VoiceMail application.</para>
00175                <value name="SUCCESS" />
00176                <value name="USEREXIT" />
00177                <value name="FAILED" />
00178             </variable>
00179          </variablelist>
00180       </description>
00181       <see-also>
00182          <ref type="application">VoiceMailMain</ref>
00183       </see-also>
00184    </application>
00185    <application name="VoiceMailMain" language="en_US">
00186       <synopsis>
00187          Check Voicemail messages.
00188       </synopsis>
00189       <syntax>
00190          <parameter name="mailbox" required="true" argsep="@">
00191             <argument name="mailbox" />
00192             <argument name="context" />
00193          </parameter>
00194          <parameter name="options">
00195             <optionlist>
00196                <option name="p">
00197                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00198                   the mailbox that is entered by the caller.</para>
00199                </option>
00200                <option name="g">
00201                   <argument name="#" required="true" />
00202                   <para>Use the specified amount of gain when recording a voicemail message.
00203                   The units are whole-number decibels (dB).</para>
00204                </option>
00205                <option name="s">
00206                   <para>Skip checking the passcode for the mailbox.</para>
00207                </option>
00208                <option name="a">
00209                   <argument name="folder" required="true" />
00210                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00211                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00212                   <enumlist>
00213                      <enum name="0"><para>INBOX</para></enum>
00214                      <enum name="1"><para>Old</para></enum>
00215                      <enum name="2"><para>Work</para></enum>
00216                      <enum name="3"><para>Family</para></enum>
00217                      <enum name="4"><para>Friends</para></enum>
00218                      <enum name="5"><para>Cust1</para></enum>
00219                      <enum name="6"><para>Cust2</para></enum>
00220                      <enum name="7"><para>Cust3</para></enum>
00221                      <enum name="8"><para>Cust4</para></enum>
00222                      <enum name="9"><para>Cust5</para></enum>
00223                   </enumlist>
00224                </option>
00225             </optionlist>
00226          </parameter>
00227       </syntax>
00228       <description>
00229          <para>This application allows the calling party to check voicemail messages. A specific
00230          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00231          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00232          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00233          <literal>default</literal> context will be used.</para>
00234          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00235          or Password, and the extension exists:</para>
00236          <enumlist>
00237             <enum name="*">
00238                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00239             </enum>
00240          </enumlist>
00241       </description>
00242       <see-also>
00243          <ref type="application">VoiceMail</ref>
00244       </see-also>
00245    </application>
00246    <application name="MailboxExists" language="en_US">
00247       <synopsis>
00248          Check to see if Voicemail mailbox exists.
00249       </synopsis>
00250       <syntax>
00251          <parameter name="mailbox" required="true" argsep="@">
00252             <argument name="mailbox" required="true" />
00253             <argument name="context" />
00254          </parameter>
00255          <parameter name="options">
00256             <para>None options.</para>
00257          </parameter>
00258       </syntax>
00259       <description>
00260          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00261          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00262          will be used.</para>
00263          <para>This application will set the following channel variable upon completion:</para>
00264          <variablelist>
00265             <variable name="VMBOXEXISTSSTATUS">
00266                <para>This will contain the status of the execution of the MailboxExists application.
00267                Possible values include:</para>
00268                <value name="SUCCESS" />
00269                <value name="FAILED" />
00270             </variable>
00271          </variablelist>
00272       </description>
00273    </application>
00274    <application name="VMAuthenticate" language="en_US">
00275       <synopsis>
00276          Authenticate with Voicemail passwords.
00277       </synopsis>
00278       <syntax>
00279          <parameter name="mailbox" required="true" argsep="@">
00280             <argument name="mailbox" />
00281             <argument name="context" />
00282          </parameter>
00283          <parameter name="options">
00284             <optionlist>
00285                <option name="s">
00286                   <para>Skip playing the initial prompts.</para>
00287                </option>
00288             </optionlist>
00289          </parameter>
00290       </syntax>
00291       <description>
00292          <para>This application behaves the same way as the Authenticate application, but the passwords
00293          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00294          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00295          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00296          mailbox.</para>
00297          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00298          or Password, and the extension exists:</para>
00299          <enumlist>
00300             <enum name="*">
00301                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00302             </enum>
00303          </enumlist>
00304       </description>
00305    </application>
00306    <application name="VMSayName" language="en_US">
00307       <synopsis>
00308          Play the name of a voicemail user
00309       </synopsis>
00310       <syntax>
00311          <parameter name="mailbox" required="true" argsep="@">
00312             <argument name="mailbox" />
00313             <argument name="context" />
00314          </parameter>
00315       </syntax>
00316       <description>
00317          <para>This application will say the recorded name of the voicemail user specified as the
00318          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00319       </description>
00320    </application>
00321    <function name="MAILBOX_EXISTS" language="en_US">
00322       <synopsis>
00323          Tell if a mailbox is configured.
00324       </synopsis>
00325       <syntax argsep="@">
00326          <parameter name="mailbox" required="true" />
00327          <parameter name="context" />
00328       </syntax>
00329       <description>
00330          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00331          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00332          context.</para>
00333       </description>
00334    </function>
00335    <manager name="VoicemailUsersList" language="en_US">
00336       <synopsis>
00337          List All Voicemail User Information.
00338       </synopsis>
00339       <syntax>
00340          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00341       </syntax>
00342       <description>
00343       </description>
00344    </manager>
00345  ***/
00346 
00347 #ifdef IMAP_STORAGE
00348 static char imapserver[48];
00349 static char imapport[8];
00350 static char imapflags[128];
00351 static char imapfolder[64];
00352 static char imapparentfolder[64] = "\0";
00353 static char greetingfolder[64];
00354 static char authuser[32];
00355 static char authpassword[42];
00356 static int imapversion = 1;
00357 
00358 static int expungeonhangup = 1;
00359 static int imapgreetings = 0;
00360 static char delimiter = '\0';
00361 
00362 struct vm_state;
00363 struct ast_vm_user;
00364 
00365 AST_THREADSTORAGE(ts_vmstate);
00366 
00367 /* Forward declarations for IMAP */
00368 static int init_mailstream(struct vm_state *vms, int box);
00369 static void write_file(char *filename, char *buffer, unsigned long len);
00370 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00371 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00372 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00373 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00374 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00375 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00376 static void vmstate_insert(struct vm_state *vms);
00377 static void vmstate_delete(struct vm_state *vms);
00378 static void set_update(MAILSTREAM * stream);
00379 static void init_vm_state(struct vm_state *vms);
00380 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00381 static void get_mailbox_delimiter(MAILSTREAM *stream);
00382 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00383 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00384 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00385 static void update_messages_by_imapuser(const char *user, unsigned long number);
00386 static int vm_delete(char *file);
00387 
00388 static int imap_remove_file (char *dir, int msgnum);
00389 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00390 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00391 static void check_quota(struct vm_state *vms, char *mailbox);
00392 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00393 struct vmstate {
00394    struct vm_state *vms;
00395    AST_LIST_ENTRY(vmstate) list;
00396 };
00397 
00398 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00399 
00400 #endif
00401 
00402 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00403 
00404 #define COMMAND_TIMEOUT 5000
00405 /* Don't modify these here; set your umask at runtime instead */
00406 #define  VOICEMAIL_DIR_MODE   0777
00407 #define  VOICEMAIL_FILE_MODE  0666
00408 #define  CHUNKSIZE   65536
00409 
00410 #define VOICEMAIL_CONFIG "voicemail.conf"
00411 #define ASTERISK_USERNAME "asterisk"
00412 
00413 /* Define fast-forward, pause, restart, and reverse keys
00414  * while listening to a voicemail message - these are
00415  * strings, not characters */
00416 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00417 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00418 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00419 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00420 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00421 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00422 
00423 /* Default mail command to mail voicemail. Change it with the
00424  * mailcmd= command in voicemail.conf */
00425 #define SENDMAIL "/usr/sbin/sendmail -t"
00426 
00427 #define INTRO "vm-intro"
00428 
00429 #define MAXMSG 100
00430 #define MAXMSGLIMIT 9999
00431 
00432 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00433 
00434 #define BASELINELEN 72
00435 #define BASEMAXINLINE 256
00436 #ifdef IMAP_STORAGE
00437 #define ENDL "\r\n"
00438 #else
00439 #define ENDL "\n"
00440 #endif
00441 
00442 #define MAX_DATETIME_FORMAT   512
00443 #define MAX_NUM_CID_CONTEXTS 10
00444 
00445 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00446 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00447 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00448 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00449 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00450 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00451 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00452 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00453 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00454 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00455 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00456 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00457 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00458 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00459 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00460 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00461 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00462 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00463 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00464 #define ERROR_LOCK_PATH  -100
00465 #define OPERATOR_EXIT     300
00466 
00467 
00468 enum vm_box {
00469    NEW_FOLDER,
00470    OLD_FOLDER,
00471    WORK_FOLDER,
00472    FAMILY_FOLDER,
00473    FRIENDS_FOLDER,
00474    GREETINGS_FOLDER
00475 };
00476 
00477 enum vm_option_flags {
00478    OPT_SILENT =           (1 << 0),
00479    OPT_BUSY_GREETING =    (1 << 1),
00480    OPT_UNAVAIL_GREETING = (1 << 2),
00481    OPT_RECORDGAIN =       (1 << 3),
00482    OPT_PREPEND_MAILBOX =  (1 << 4),
00483    OPT_AUTOPLAY =         (1 << 6),
00484    OPT_DTMFEXIT =         (1 << 7),
00485    OPT_MESSAGE_Urgent =   (1 << 8),
00486    OPT_MESSAGE_PRIORITY = (1 << 9)
00487 };
00488 
00489 enum vm_option_args {
00490    OPT_ARG_RECORDGAIN = 0,
00491    OPT_ARG_PLAYFOLDER = 1,
00492    OPT_ARG_DTMFEXIT   = 2,
00493    /* This *must* be the last value in this enum! */
00494    OPT_ARG_ARRAY_SIZE = 3,
00495 };
00496 
00497 enum vm_passwordlocation {
00498    OPT_PWLOC_VOICEMAILCONF = 0,
00499    OPT_PWLOC_SPOOLDIR      = 1,
00500    OPT_PWLOC_USERSCONF     = 2,
00501 };
00502 
00503 AST_APP_OPTIONS(vm_app_options, {
00504    AST_APP_OPTION('s', OPT_SILENT),
00505    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00506    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00507    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00508    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00509    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00510    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00511    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00512    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00513 });
00514 
00515 static int load_config(int reload);
00516 #ifdef TEST_FRAMEWORK
00517 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00518 #endif
00519 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00520 
00521 /*! \page vmlang Voicemail Language Syntaxes Supported
00522 
00523    \par Syntaxes supported, not really language codes.
00524    \arg \b en    - English
00525    \arg \b de    - German
00526    \arg \b es    - Spanish
00527    \arg \b fr    - French
00528    \arg \b it    - Italian
00529    \arg \b nl    - Dutch
00530    \arg \b pt    - Portuguese
00531    \arg \b pt_BR - Portuguese (Brazil)
00532    \arg \b gr    - Greek
00533    \arg \b no    - Norwegian
00534    \arg \b se    - Swedish
00535    \arg \b tw    - Chinese (Taiwan)
00536    \arg \b ua - Ukrainian
00537 
00538 German requires the following additional soundfile:
00539 \arg \b 1F  einE (feminine)
00540 
00541 Spanish requires the following additional soundfile:
00542 \arg \b 1M      un (masculine)
00543 
00544 Dutch, Portuguese & Spanish require the following additional soundfiles:
00545 \arg \b vm-INBOXs singular of 'new'
00546 \arg \b vm-Olds      singular of 'old/heard/read'
00547 
00548 NB these are plural:
00549 \arg \b vm-INBOX  nieuwe (nl)
00550 \arg \b vm-Old    oude (nl)
00551 
00552 Polish uses:
00553 \arg \b vm-new-a  'new', feminine singular accusative
00554 \arg \b vm-new-e  'new', feminine plural accusative
00555 \arg \b vm-new-ych   'new', feminine plural genitive
00556 \arg \b vm-old-a  'old', feminine singular accusative
00557 \arg \b vm-old-e  'old', feminine plural accusative
00558 \arg \b vm-old-ych   'old', feminine plural genitive
00559 \arg \b digits/1-a   'one', not always same as 'digits/1'
00560 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00561 
00562 Swedish uses:
00563 \arg \b vm-nytt      singular of 'new'
00564 \arg \b vm-nya    plural of 'new'
00565 \arg \b vm-gammalt   singular of 'old'
00566 \arg \b vm-gamla  plural of 'old'
00567 \arg \b digits/ett   'one', not always same as 'digits/1'
00568 
00569 Norwegian uses:
00570 \arg \b vm-ny     singular of 'new'
00571 \arg \b vm-nye    plural of 'new'
00572 \arg \b vm-gammel singular of 'old'
00573 \arg \b vm-gamle  plural of 'old'
00574 
00575 Dutch also uses:
00576 \arg \b nl-om     'at'?
00577 
00578 Spanish also uses:
00579 \arg \b vm-youhaveno
00580 
00581 Italian requires the following additional soundfile:
00582 
00583 For vm_intro_it:
00584 \arg \b vm-nuovo  new
00585 \arg \b vm-nuovi  new plural
00586 \arg \b vm-vecchio   old
00587 \arg \b vm-vecchi old plural
00588 
00589 Chinese (Taiwan) requires the following additional soundfile:
00590 \arg \b vm-tong      A class-word for call (tong1)
00591 \arg \b vm-ri     A class-word for day (ri4)
00592 \arg \b vm-you    You (ni3)
00593 \arg \b vm-haveno   Have no (mei2 you3)
00594 \arg \b vm-have     Have (you3)
00595 \arg \b vm-listen   To listen (yao4 ting1)
00596 
00597 
00598 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00599 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00600 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00601 
00602 */
00603 
00604 struct baseio {
00605    int iocp;
00606    int iolen;
00607    int linelength;
00608    int ateof;
00609    unsigned char iobuf[BASEMAXINLINE];
00610 };
00611 
00612 /*! Structure for linked list of users 
00613  * Use ast_vm_user_destroy() to free one of these structures. */
00614 struct ast_vm_user {
00615    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00616    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00617    char password[80];               /*!< Secret pin code, numbers only */
00618    char fullname[80];               /*!< Full name, for directory app */
00619    char email[80];                  /*!< E-mail address */
00620    char *emailsubject;              /*!< E-mail subject */
00621    char *emailbody;                 /*!< E-mail body */
00622    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00623    char serveremail[80];            /*!< From: Mail address */
00624    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00625    char zonetag[80];                /*!< Time zone */
00626    char locale[20];                 /*!< The locale (for presentation of date/time) */
00627    char callback[80];
00628    char dialout[80];
00629    char uniqueid[80];               /*!< Unique integer identifier */
00630    char exit[80];
00631    char attachfmt[20];              /*!< Attachment format */
00632    unsigned int flags;              /*!< VM_ flags */ 
00633    int saydurationm;
00634    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00635    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00636    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00637    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00638    int passwordlocation;            /*!< Storage location of the password */
00639 #ifdef IMAP_STORAGE
00640    char imapuser[80];               /*!< IMAP server login */
00641    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00642    char imapfolder[64];             /*!< IMAP voicemail folder */
00643    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00644    int imapversion;                 /*!< If configuration changes, use the new values */
00645 #endif
00646    double volgain;                  /*!< Volume gain for voicemails sent via email */
00647    AST_LIST_ENTRY(ast_vm_user) list;
00648 };
00649 
00650 /*! Voicemail time zones */
00651 struct vm_zone {
00652    AST_LIST_ENTRY(vm_zone) list;
00653    char name[80];
00654    char timezone[80];
00655    char msg_format[512];
00656 };
00657 
00658 #define VMSTATE_MAX_MSG_ARRAY 256
00659 
00660 /*! Voicemail mailbox state */
00661 struct vm_state {
00662    char curbox[80];
00663    char username[80];
00664    char context[80];
00665    char curdir[PATH_MAX];
00666    char vmbox[PATH_MAX];
00667    char fn[PATH_MAX];
00668    char intro[PATH_MAX];
00669    int *deleted;
00670    int *heard;
00671    int dh_arraysize; /* used for deleted / heard allocation */
00672    int curmsg;
00673    int lastmsg;
00674    int newmessages;
00675    int oldmessages;
00676    int urgentmessages;
00677    int starting;
00678    int repeats;
00679 #ifdef IMAP_STORAGE
00680    ast_mutex_t lock;
00681    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00682    long *msgArray;
00683    unsigned msg_array_max;
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 = ast_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          memset(retval, 0, sizeof(*retval));
01385       }
01386       populate_defaults(retval);
01387       if (!ivm) {
01388          ast_set_flag(retval, VM_ALLOCED);
01389       }
01390       if (mailbox) {
01391          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01392       }
01393       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01394          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01395       } else {
01396          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01397       }
01398       if (var) {
01399          apply_options_full(retval, var);
01400          ast_variables_destroy(var);
01401       } else { 
01402          if (!ivm) 
01403             free_user(retval);
01404          retval = NULL;
01405       }  
01406    } 
01407    return retval;
01408 }
01409 
01410 /*!
01411  * \brief Finds a voicemail user from the users file or the realtime engine.
01412  * \param ivm
01413  * \param context
01414  * \param mailbox
01415  * 
01416  * \return The ast_vm_user structure for the user that was found.
01417  */
01418 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01419 {
01420    /* This function could be made to generate one from a database, too */
01421    struct ast_vm_user *vmu = NULL, *cur;
01422    AST_LIST_LOCK(&users);
01423 
01424    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01425       context = "default";
01426 
01427    AST_LIST_TRAVERSE(&users, cur, list) {
01428 #ifdef IMAP_STORAGE
01429       if (cur->imapversion != imapversion) {
01430          continue;
01431       }
01432 #endif
01433       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01434          break;
01435       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01436          break;
01437    }
01438    if (cur) {
01439       /* Make a copy, so that on a reload, we have no race */
01440       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01441          *vmu = *cur;
01442          if (!ivm) {
01443             vmu->emailbody = ast_strdup(cur->emailbody);
01444             vmu->emailsubject = ast_strdup(cur->emailsubject);
01445          }
01446          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01447          AST_LIST_NEXT(vmu, list) = NULL;
01448       }
01449    } else
01450       vmu = find_user_realtime(ivm, context, mailbox);
01451    AST_LIST_UNLOCK(&users);
01452    return vmu;
01453 }
01454 
01455 /*!
01456  * \brief Resets a user password to a specified password.
01457  * \param context
01458  * \param mailbox
01459  * \param newpass
01460  *
01461  * This does the actual change password work, called by the vm_change_password() function.
01462  *
01463  * \return zero on success, -1 on error.
01464  */
01465 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01466 {
01467    /* This function could be made to generate one from a database, too */
01468    struct ast_vm_user *cur;
01469    int res = -1;
01470    AST_LIST_LOCK(&users);
01471    AST_LIST_TRAVERSE(&users, cur, list) {
01472       if ((!context || !strcasecmp(context, cur->context)) &&
01473          (!strcasecmp(mailbox, cur->mailbox)))
01474             break;
01475    }
01476    if (cur) {
01477       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01478       res = 0;
01479    }
01480    AST_LIST_UNLOCK(&users);
01481    return res;
01482 }
01483 
01484 /*!
01485  * \brief Check if configuration file is valid
01486  */
01487 static inline int valid_config(const struct ast_config *cfg)
01488 {
01489    return cfg && cfg != CONFIG_STATUS_FILEINVALID;
01490 }
01491 
01492 /*! 
01493  * \brief The handler for the change password option.
01494  * \param vmu The voicemail user to work with.
01495  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01496  * 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.
01497  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01498  */
01499 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01500 {
01501    struct ast_config   *cfg = NULL;
01502    struct ast_variable *var = NULL;
01503    struct ast_category *cat = NULL;
01504    char *category = NULL, *value = NULL, *new = NULL;
01505    const char *tmp = NULL;
01506    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01507    char secretfn[PATH_MAX] = "";
01508    int found = 0;
01509 
01510    if (!change_password_realtime(vmu, newpassword))
01511       return;
01512 
01513    /* check if we should store the secret in the spool directory next to the messages */
01514    switch (vmu->passwordlocation) {
01515    case OPT_PWLOC_SPOOLDIR:
01516       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01517       if (write_password_to_file(secretfn, newpassword) == 0) {
01518          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01519          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01520          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01521          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01522          break;
01523       } else {
01524          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01525       }
01526       /* Fall-through */
01527    case OPT_PWLOC_VOICEMAILCONF:
01528       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
01529          while ((category = ast_category_browse(cfg, category))) {
01530             if (!strcasecmp(category, vmu->context)) {
01531                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01532                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01533                   break;
01534                }
01535                value = strstr(tmp, ",");
01536                if (!value) {
01537                   new = ast_alloca(strlen(newpassword)+1);
01538                   sprintf(new, "%s", newpassword);
01539                } else {
01540                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01541                   sprintf(new, "%s%s", newpassword, value);
01542                }
01543                if (!(cat = ast_category_get(cfg, category))) {
01544                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01545                   break;
01546                }
01547                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01548                found = 1;
01549             }
01550          }
01551          /* save the results */
01552          if (found) {
01553             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01554             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01555             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01556             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01557             ast_config_destroy(cfg);
01558             break;
01559          }
01560 
01561          ast_config_destroy(cfg);
01562       }
01563       /* Fall-through */
01564    case OPT_PWLOC_USERSCONF:
01565       /* check users.conf and update the password stored for the mailbox */
01566       /* if no vmsecret entry exists create one. */
01567       if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
01568          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01569          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01570             ast_debug(4, "users.conf: %s\n", category);
01571             if (!strcasecmp(category, vmu->mailbox)) {
01572                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01573                   ast_debug(3, "looks like we need to make vmsecret!\n");
01574                   var = ast_variable_new("vmsecret", newpassword, "");
01575                } else {
01576                   var = NULL;
01577                }
01578                new = ast_alloca(strlen(newpassword) + 1);
01579                sprintf(new, "%s", newpassword);
01580                if (!(cat = ast_category_get(cfg, category))) {
01581                   ast_debug(4, "failed to get category!\n");
01582                   ast_free(var);
01583                   break;
01584                }
01585                if (!var) {
01586                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01587                } else {
01588                   ast_variable_append(cat, var);
01589                }
01590                found = 1;
01591                break;
01592             }
01593          }
01594          /* save the results and clean things up */
01595          if (found) {
01596             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01597             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01598             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01599             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01600          }
01601 
01602          ast_config_destroy(cfg);
01603       }
01604    }
01605 }
01606 
01607 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01608 {
01609    char buf[255];
01610    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01611    ast_debug(1, "External password: %s\n",buf);
01612    if (!ast_safe_system(buf)) {
01613       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01614       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01615       /* Reset the password in memory, too */
01616       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01617    }
01618 }
01619 
01620 /*! 
01621  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01622  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01623  * \param len The length of the path string that was written out.
01624  * \param context
01625  * \param ext 
01626  * \param folder 
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_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01634 {
01635    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01636 }
01637 
01638 /*! 
01639  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01640  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01641  * \param len The length of the path string that was written out.
01642  * \param dir 
01643  * \param num 
01644  * 
01645  * The path is constructed as 
01646  *    VM_SPOOL_DIRcontext/ext/folder
01647  *
01648  * \return zero on success, -1 on error.
01649  */
01650 static int make_file(char *dest, const int len, const char *dir, const int num)
01651 {
01652    return snprintf(dest, len, "%s/msg%04d", dir, num);
01653 }
01654 
01655 /* same as mkstemp, but return a FILE * */
01656 static FILE *vm_mkftemp(char *template)
01657 {
01658    FILE *p = NULL;
01659    int pfd = mkstemp(template);
01660    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01661    if (pfd > -1) {
01662       p = fdopen(pfd, "w+");
01663       if (!p) {
01664          close(pfd);
01665          pfd = -1;
01666       }
01667    }
01668    return p;
01669 }
01670 
01671 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01672  * \param dest    String. base directory.
01673  * \param len     Length of dest.
01674  * \param context String. Ignored if is null or empty string.
01675  * \param ext     String. Ignored if is null or empty string.
01676  * \param folder  String. Ignored if is null or empty string. 
01677  * \return -1 on failure, 0 on success.
01678  */
01679 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01680 {
01681    mode_t   mode = VOICEMAIL_DIR_MODE;
01682    int res;
01683 
01684    make_dir(dest, len, context, ext, folder);
01685    if ((res = ast_mkdir(dest, mode))) {
01686       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01687       return -1;
01688    }
01689    return 0;
01690 }
01691 
01692 static const char * const mailbox_folders[] = {
01693 #ifdef IMAP_STORAGE
01694    imapfolder,
01695 #else
01696    "INBOX",
01697 #endif
01698    "Old",
01699    "Work",
01700    "Family",
01701    "Friends",
01702    "Cust1",
01703    "Cust2",
01704    "Cust3",
01705    "Cust4",
01706    "Cust5",
01707    "Deleted",
01708    "Urgent",
01709 };
01710 
01711 static const char *mbox(struct ast_vm_user *vmu, int id)
01712 {
01713 #ifdef IMAP_STORAGE
01714    if (vmu && id == 0) {
01715       return vmu->imapfolder;
01716    }
01717 #endif
01718    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01719 }
01720 
01721 static int get_folder_by_name(const char *name)
01722 {
01723    size_t i;
01724 
01725    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01726       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01727          return i;
01728       }
01729    }
01730 
01731    return -1;
01732 }
01733 
01734 static void free_user(struct ast_vm_user *vmu)
01735 {
01736    if (ast_test_flag(vmu, VM_ALLOCED)) {
01737 
01738       ast_free(vmu->emailbody);
01739       vmu->emailbody = NULL;
01740 
01741       ast_free(vmu->emailsubject);
01742       vmu->emailsubject = NULL;
01743 
01744       ast_free(vmu);
01745    }
01746 }
01747 
01748 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01749 
01750    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01751 
01752    /* remove old allocation */
01753    if (vms->deleted) {
01754       ast_free(vms->deleted);
01755       vms->deleted = NULL;
01756    }
01757    if (vms->heard) {
01758       ast_free(vms->heard);
01759       vms->heard = NULL;
01760    }
01761    vms->dh_arraysize = 0;
01762 
01763    if (arraysize > 0) {
01764       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01765          return -1;
01766       }
01767       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01768          ast_free(vms->deleted);
01769          vms->deleted = NULL;
01770          return -1;
01771       }
01772       vms->dh_arraysize = arraysize;
01773    }
01774 
01775    return 0;
01776 }
01777 
01778 /* All IMAP-specific functions should go in this block. This
01779  * keeps them from being spread out all over the code */
01780 #ifdef IMAP_STORAGE
01781 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01782 {
01783    char arg[10];
01784    struct vm_state *vms;
01785    unsigned long messageNum;
01786 
01787    /* If greetings aren't stored in IMAP, just delete the file */
01788    if (msgnum < 0 && !imapgreetings) {
01789       ast_filedelete(file, NULL);
01790       return;
01791    }
01792 
01793    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01794       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);
01795       return;
01796    }
01797 
01798    if (msgnum < 0) {
01799       imap_delete_old_greeting(file, vms);
01800       return;
01801    }
01802 
01803    /* find real message number based on msgnum */
01804    /* this may be an index into vms->msgArray based on the msgnum. */
01805    messageNum = vms->msgArray[msgnum];
01806    if (messageNum == 0) {
01807       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01808       return;
01809    }
01810    if (option_debug > 2)
01811       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01812    /* delete message */
01813    snprintf (arg, sizeof(arg), "%lu", messageNum);
01814    ast_mutex_lock(&vms->lock);
01815    mail_setflag (vms->mailstream, arg, "\\DELETED");
01816    mail_expunge(vms->mailstream);
01817    ast_mutex_unlock(&vms->lock);
01818 }
01819 
01820 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01821 {
01822    struct vm_state *vms_p;
01823    char *file, *filename;
01824    char *attachment;
01825    int i;
01826    BODY *body;
01827 
01828    /* This function is only used for retrieval of IMAP greetings
01829     * regular messages are not retrieved this way, nor are greetings
01830     * if they are stored locally*/
01831    if (msgnum > -1 || !imapgreetings) {
01832       return 0;
01833    } else {
01834       file = strrchr(ast_strdupa(dir), '/');
01835       if (file)
01836          *file++ = '\0';
01837       else {
01838          ast_debug (1, "Failed to procure file name from directory passed.\n");
01839          return -1;
01840       }
01841    }
01842 
01843    /* check if someone is accessing this box right now... */
01844    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01845       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01846       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01847       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01848       * that's all we need to do.
01849       */
01850       if (!(vms_p = create_vm_state_from_user(vmu))) {
01851          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01852          return -1;
01853       }
01854    }
01855 
01856    /* Greetings will never have a prepended message */
01857    *vms_p->introfn = '\0';
01858 
01859    ast_mutex_lock(&vms_p->lock);
01860    init_mailstream(vms_p, GREETINGS_FOLDER);
01861    if (!vms_p->mailstream) {
01862       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01863       ast_mutex_unlock(&vms_p->lock);
01864       return -1;
01865    }
01866 
01867    /*XXX Yuck, this could probably be done a lot better */
01868    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01869       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01870       /* We have the body, now we extract the file name of the first attachment. */
01871       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01872          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01873       } else {
01874          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01875          ast_mutex_unlock(&vms_p->lock);
01876          return -1;
01877       }
01878       filename = strsep(&attachment, ".");
01879       if (!strcmp(filename, file)) {
01880          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01881          vms_p->msgArray[vms_p->curmsg] = i + 1;
01882          save_body(body, vms_p, "2", attachment, 0);
01883          ast_mutex_unlock(&vms_p->lock);
01884          return 0;
01885       }
01886    }
01887    ast_mutex_unlock(&vms_p->lock);
01888 
01889    return -1;
01890 }
01891 
01892 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01893 {
01894    BODY *body;
01895    char *header_content;
01896    char *attachedfilefmt;
01897    char buf[80];
01898    struct vm_state *vms;
01899    char text_file[PATH_MAX];
01900    FILE *text_file_ptr;
01901    int res = 0;
01902    struct ast_vm_user *vmu;
01903 
01904    if (!(vmu = find_user(NULL, context, mailbox))) {
01905       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01906       return -1;
01907    }
01908    
01909    if (msgnum < 0) {
01910       if (imapgreetings) {
01911          res = imap_retrieve_greeting(dir, msgnum, vmu);
01912          goto exit;
01913       } else {
01914          res = 0;
01915          goto exit;
01916       }
01917    }
01918 
01919    /* Before anything can happen, we need a vm_state so that we can
01920     * actually access the imap server through the vms->mailstream
01921     */
01922    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01923       /* This should not happen. If it does, then I guess we'd
01924        * need to create the vm_state, extract which mailbox to
01925        * open, and then set up the msgArray so that the correct
01926        * IMAP message could be accessed. If I have seen correctly
01927        * though, the vms should be obtainable from the vmstates list
01928        * and should have its msgArray properly set up.
01929        */
01930       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01931       res = -1;
01932       goto exit;
01933    }
01934    
01935    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01936    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01937 
01938    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01939    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01940       res = 0;
01941       goto exit;
01942    }
01943 
01944    if (option_debug > 2)
01945       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01946    if (vms->msgArray[msgnum] == 0) {
01947       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01948       res = -1;
01949       goto exit;
01950    }
01951 
01952    /* This will only work for new messages... */
01953    ast_mutex_lock(&vms->lock);
01954    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01955    ast_mutex_unlock(&vms->lock);
01956    /* empty string means no valid header */
01957    if (ast_strlen_zero(header_content)) {
01958       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01959       res = -1;
01960       goto exit;
01961    }
01962 
01963    ast_mutex_lock(&vms->lock);
01964    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01965    ast_mutex_unlock(&vms->lock);
01966 
01967    /* We have the body, now we extract the file name of the first attachment. */
01968    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01969       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01970    } else {
01971       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01972       res = -1;
01973       goto exit;
01974    }
01975    
01976    /* Find the format of the attached file */
01977 
01978    strsep(&attachedfilefmt, ".");
01979    if (!attachedfilefmt) {
01980       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01981       res = -1;
01982       goto exit;
01983    }
01984    
01985    save_body(body, vms, "2", attachedfilefmt, 0);
01986    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01987       *vms->introfn = '\0';
01988    }
01989 
01990    /* Get info from headers!! */
01991    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01992 
01993    if (!(text_file_ptr = fopen(text_file, "w"))) {
01994       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01995    }
01996 
01997    fprintf(text_file_ptr, "%s\n", "[message]");
01998 
01999    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02000    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02001    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02002    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02003    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02004    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02005    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02006    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02007    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02008    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02009    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02010    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02011    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02012    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02013    fclose(text_file_ptr);
02014 
02015 exit:
02016    free_user(vmu);
02017    return res;
02018 }
02019 
02020 static int folder_int(const char *folder)
02021 {
02022    /*assume a NULL folder means INBOX*/
02023    if (!folder) {
02024       return 0;
02025    }
02026    if (!strcasecmp(folder, imapfolder)) {
02027       return 0;
02028    } else if (!strcasecmp(folder, "Old")) {
02029       return 1;
02030    } else if (!strcasecmp(folder, "Work")) {
02031       return 2;
02032    } else if (!strcasecmp(folder, "Family")) {
02033       return 3;
02034    } else if (!strcasecmp(folder, "Friends")) {
02035       return 4;
02036    } else if (!strcasecmp(folder, "Cust1")) {
02037       return 5;
02038    } else if (!strcasecmp(folder, "Cust2")) {
02039       return 6;
02040    } else if (!strcasecmp(folder, "Cust3")) {
02041       return 7;
02042    } else if (!strcasecmp(folder, "Cust4")) {
02043       return 8;
02044    } else if (!strcasecmp(folder, "Cust5")) {
02045       return 9;
02046    } else if (!strcasecmp(folder, "Urgent")) {
02047       return 11;
02048    } else { /*assume they meant INBOX if folder is not found otherwise*/
02049       return 0;
02050    }
02051 }
02052 
02053 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02054 {
02055    SEARCHPGM *pgm;
02056    SEARCHHEADER *hdr;
02057 
02058    struct ast_vm_user *vmu, vmus;
02059    struct vm_state *vms_p;
02060    int ret = 0;
02061    int fold = folder_int(folder);
02062    int urgent = 0;
02063    
02064    /* If URGENT, then look at INBOX */
02065    if (fold == 11) {
02066       fold = NEW_FOLDER;
02067       urgent = 1;
02068    }
02069 
02070    if (ast_strlen_zero(mailbox))
02071       return 0;
02072 
02073    /* We have to get the user before we can open the stream! */
02074    vmu = find_user(&vmus, context, mailbox);
02075    if (!vmu) {
02076       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02077       return -1;
02078    } else {
02079       /* No IMAP account available */
02080       if (vmu->imapuser[0] == '\0') {
02081          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02082          return -1;
02083       }
02084    }
02085    
02086    /* No IMAP account available */
02087    if (vmu->imapuser[0] == '\0') {
02088       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02089       free_user(vmu);
02090       return -1;
02091    }
02092    ast_assert(msgnum < vms->msg_array_max);
02093 
02094    /* check if someone is accessing this box right now... */
02095    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02096    if (!vms_p) {
02097       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02098    }
02099    if (vms_p) {
02100       ast_debug(3, "Returning before search - user is logged in\n");
02101       if (fold == 0) { /* INBOX */
02102          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02103       }
02104       if (fold == 1) { /* Old messages */
02105          return vms_p->oldmessages;
02106       }
02107    }
02108 
02109    /* add one if not there... */
02110    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02111    if (!vms_p) {
02112       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02113    }
02114 
02115    if (!vms_p) {
02116       vms_p = create_vm_state_from_user(vmu);
02117    }
02118    ret = init_mailstream(vms_p, fold);
02119    if (!vms_p->mailstream) {
02120       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02121       return -1;
02122    }
02123    if (ret == 0) {
02124       ast_mutex_lock(&vms_p->lock);
02125       pgm = mail_newsearchpgm ();
02126       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02127       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02128       pgm->header = hdr;
02129       if (fold != OLD_FOLDER) {
02130          pgm->unseen = 1;
02131          pgm->seen = 0;
02132       }
02133       /* In the special case where fold is 1 (old messages) we have to do things a bit
02134        * differently. Old messages are stored in the INBOX but are marked as "seen"
02135        */
02136       else {
02137          pgm->unseen = 0;
02138          pgm->seen = 1;
02139       }
02140       /* look for urgent messages */
02141       if (fold == NEW_FOLDER) {
02142          if (urgent) {
02143             pgm->flagged = 1;
02144             pgm->unflagged = 0;
02145          } else {
02146             pgm->flagged = 0;
02147             pgm->unflagged = 1;
02148          }
02149       }
02150       pgm->undeleted = 1;
02151       pgm->deleted = 0;
02152 
02153       vms_p->vmArrayIndex = 0;
02154       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02155       if (fold == 0 && urgent == 0)
02156          vms_p->newmessages = vms_p->vmArrayIndex;
02157       if (fold == 1)
02158          vms_p->oldmessages = vms_p->vmArrayIndex;
02159       if (fold == 0 && urgent == 1)
02160          vms_p->urgentmessages = vms_p->vmArrayIndex;
02161       /*Freeing the searchpgm also frees the searchhdr*/
02162       mail_free_searchpgm(&pgm);
02163       ast_mutex_unlock(&vms_p->lock);
02164       vms_p->updated = 0;
02165       return vms_p->vmArrayIndex;
02166    } else {
02167       ast_mutex_lock(&vms_p->lock);
02168       mail_ping(vms_p->mailstream);
02169       ast_mutex_unlock(&vms_p->lock);
02170    }
02171    return 0;
02172 }
02173 
02174 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02175 {
02176    /* Check if mailbox is full */
02177    check_quota(vms, vmu->imapfolder);
02178    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02179       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02180       ast_play_and_wait(chan, "vm-mailboxfull");
02181       return -1;
02182    }
02183    
02184    /* Check if we have exceeded maxmsg */
02185    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));
02186    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02187       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02188       ast_play_and_wait(chan, "vm-mailboxfull");
02189       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02190       return -1;
02191    }
02192 
02193    return 0;
02194 }
02195 
02196 /*!
02197  * \brief Gets the number of messages that exist in a mailbox folder.
02198  * \param context
02199  * \param mailbox
02200  * \param folder
02201  * 
02202  * This method is used when IMAP backend is used.
02203  * \return The number of messages in this mailbox folder (zero or more).
02204  */
02205 static int messagecount(const char *context, const char *mailbox, const char *folder)
02206 {
02207    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02208       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02209    } else {
02210       return __messagecount(context, mailbox, folder);
02211    }
02212 }
02213 
02214 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)
02215 {
02216    char *myserveremail = serveremail;
02217    char fn[PATH_MAX];
02218    char introfn[PATH_MAX];
02219    char mailbox[256];
02220    char *stringp;
02221    FILE *p = NULL;
02222    char tmp[80] = "/tmp/astmail-XXXXXX";
02223    long len;
02224    void *buf;
02225    int tempcopy = 0;
02226    STRING str;
02227    int ret; /* for better error checking */
02228    char *imap_flags = NIL;
02229    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02230    int box = NEW_FOLDER;
02231 
02232    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02233    if (msgnum < 0) {
02234       if(!imapgreetings) {
02235          return 0;
02236       } else {
02237          box = GREETINGS_FOLDER;
02238       }
02239    }
02240    
02241    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02242       return -1;
02243    }
02244 
02245    /* Set urgent flag for IMAP message */
02246    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02247       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02248       imap_flags = "\\FLAGGED";
02249    }
02250    
02251    /* Attach only the first format */
02252    fmt = ast_strdupa(fmt);
02253    stringp = fmt;
02254    strsep(&stringp, "|");
02255 
02256    if (!ast_strlen_zero(vmu->serveremail))
02257       myserveremail = vmu->serveremail;
02258 
02259    if (msgnum > -1)
02260       make_file(fn, sizeof(fn), dir, msgnum);
02261    else
02262       ast_copy_string (fn, dir, sizeof(fn));
02263 
02264    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02265    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02266       *introfn = '\0';
02267    }
02268    
02269    if (ast_strlen_zero(vmu->email)) {
02270       /* We need the vmu->email to be set when we call make_email_file, but
02271        * if we keep it set, a duplicate e-mail will be created. So at the end
02272        * of this function, we will revert back to an empty string if tempcopy
02273        * is 1.
02274        */
02275       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02276       tempcopy = 1;
02277    }
02278 
02279    if (!strcmp(fmt, "wav49"))
02280       fmt = "WAV";
02281    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02282 
02283    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02284       command hangs. */
02285    if (!(p = vm_mkftemp(tmp))) {
02286       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02287       if (tempcopy)
02288          *(vmu->email) = '\0';
02289       return -1;
02290    }
02291 
02292    if (msgnum < 0 && imapgreetings) {
02293       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02294          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02295          return -1;
02296       }
02297       imap_delete_old_greeting(fn, vms);
02298    }
02299 
02300    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02301       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02302       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02303       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02304    /* read mail file to memory */
02305    len = ftell(p);
02306    rewind(p);
02307    if (!(buf = ast_malloc(len + 1))) {
02308       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02309       fclose(p);
02310       if (tempcopy)
02311          *(vmu->email) = '\0';
02312       return -1;
02313    }
02314    if (fread(buf, len, 1, p) < len) {
02315       if (ferror(p)) {
02316          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02317          return -1;
02318       }
02319    }
02320    ((char *) buf)[len] = '\0';
02321    INIT(&str, mail_string, buf, len);
02322    ret = init_mailstream(vms, box);
02323    if (ret == 0) {
02324       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02325       ast_mutex_lock(&vms->lock);
02326       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02327          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02328       ast_mutex_unlock(&vms->lock);
02329       fclose(p);
02330       unlink(tmp);
02331       ast_free(buf);
02332    } else {
02333       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02334       fclose(p);
02335       unlink(tmp);
02336       ast_free(buf);
02337       return -1;
02338    }
02339    ast_debug(3, "%s stored\n", fn);
02340    
02341    if (tempcopy)
02342       *(vmu->email) = '\0';
02343    inprocess_count(vmu->mailbox, vmu->context, -1);
02344    return 0;
02345 
02346 }
02347 
02348 /*!
02349  * \brief Gets the number of messages that exist in the inbox folder.
02350  * \param mailbox_context
02351  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02352  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02353  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02354  * 
02355  * This method is used when IMAP backend is used.
02356  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02357  *
02358  * \return zero on success, -1 on error.
02359  */
02360 
02361 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02362 {
02363    char tmp[PATH_MAX] = "";
02364    char *mailboxnc;
02365    char *context;
02366    char *mb;
02367    char *cur;
02368    if (newmsgs)
02369       *newmsgs = 0;
02370    if (oldmsgs)
02371       *oldmsgs = 0;
02372    if (urgentmsgs)
02373       *urgentmsgs = 0;
02374 
02375    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02376    /* If no mailbox, return immediately */
02377    if (ast_strlen_zero(mailbox_context))
02378       return 0;
02379    
02380    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02381    context = strchr(tmp, '@');
02382    if (strchr(mailbox_context, ',')) {
02383       int tmpnew, tmpold, tmpurgent;
02384       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02385       mb = tmp;
02386       while ((cur = strsep(&mb, ", "))) {
02387          if (!ast_strlen_zero(cur)) {
02388             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02389                return -1;
02390             else {
02391                if (newmsgs)
02392                   *newmsgs += tmpnew; 
02393                if (oldmsgs)
02394                   *oldmsgs += tmpold;
02395                if (urgentmsgs)
02396                   *urgentmsgs += tmpurgent;
02397             }
02398          }
02399       }
02400       return 0;
02401    }
02402    if (context) {
02403       *context = '\0';
02404       mailboxnc = tmp;
02405       context++;
02406    } else {
02407       context = "default";
02408       mailboxnc = (char *) mailbox_context;
02409    }
02410 
02411    if (newmsgs) {
02412       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02413       if (!vmu) {
02414          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02415          return -1;
02416       }
02417       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02418          free_user(vmu);
02419          return -1;
02420       }
02421       free_user(vmu);
02422    }
02423    if (oldmsgs) {
02424       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02425          return -1;
02426       }
02427    }
02428    if (urgentmsgs) {
02429       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02430          return -1;
02431       }
02432    }
02433    return 0;
02434 }
02435 
02436 /** 
02437  * \brief Determines if the given folder has messages.
02438  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02439  * \param folder the folder to look in
02440  *
02441  * This function is used when the mailbox is stored in an IMAP back end.
02442  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02443  * \return 1 if the folder has one or more messages. zero otherwise.
02444  */
02445 
02446 static int has_voicemail(const char *mailbox, const char *folder)
02447 {
02448    char tmp[256], *tmp2, *box, *context;
02449    ast_copy_string(tmp, mailbox, sizeof(tmp));
02450    tmp2 = tmp;
02451    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02452       while ((box = strsep(&tmp2, ",&"))) {
02453          if (!ast_strlen_zero(box)) {
02454             if (has_voicemail(box, folder)) {
02455                return 1;
02456             }
02457          }
02458       }
02459    }
02460    if ((context = strchr(tmp, '@'))) {
02461       *context++ = '\0';
02462    } else {
02463       context = "default";
02464    }
02465    return __messagecount(context, tmp, folder) ? 1 : 0;
02466 }
02467 
02468 /*!
02469  * \brief Copies a message from one mailbox to another.
02470  * \param chan
02471  * \param vmu
02472  * \param imbox
02473  * \param msgnum
02474  * \param duration
02475  * \param recip
02476  * \param fmt
02477  * \param dir
02478  *
02479  * This works with IMAP storage based mailboxes.
02480  *
02481  * \return zero on success, -1 on error.
02482  */
02483 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)
02484 {
02485    struct vm_state *sendvms = NULL, *destvms = NULL;
02486    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02487    if (msgnum >= recip->maxmsg) {
02488       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02489       return -1;
02490    }
02491    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02492       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02493       return -1;
02494    }
02495    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02496       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02497       return -1;
02498    }
02499    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02500    ast_mutex_lock(&sendvms->lock);
02501    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02502       ast_mutex_unlock(&sendvms->lock);
02503       return 0;
02504    }
02505    ast_mutex_unlock(&sendvms->lock);
02506    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02507    return -1;
02508 }
02509 
02510 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02511 {
02512    char tmp[256], *t = tmp;
02513    size_t left = sizeof(tmp);
02514    
02515    if (box == OLD_FOLDER) {
02516       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02517    } else {
02518       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02519    }
02520 
02521    if (box == NEW_FOLDER) {
02522       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02523    } else {
02524       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02525    }
02526 
02527    /* Build up server information */
02528    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02529 
02530    /* Add authentication user if present */
02531    if (!ast_strlen_zero(authuser))
02532       ast_build_string(&t, &left, "/authuser=%s", authuser);
02533 
02534    /* Add flags if present */
02535    if (!ast_strlen_zero(imapflags))
02536       ast_build_string(&t, &left, "/%s", imapflags);
02537 
02538    /* End with username */
02539 #if 1
02540    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02541 #else
02542    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02543 #endif
02544    if (box == NEW_FOLDER || box == OLD_FOLDER)
02545       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02546    else if (box == GREETINGS_FOLDER)
02547       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02548    else {   /* Other folders such as Friends, Family, etc... */
02549       if (!ast_strlen_zero(imapparentfolder)) {
02550          /* imapparentfolder would typically be set to INBOX */
02551          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02552       } else {
02553          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02554       }
02555    }
02556 }
02557 
02558 static int init_mailstream(struct vm_state *vms, int box)
02559 {
02560    MAILSTREAM *stream = NIL;
02561    long debug;
02562    char tmp[256];
02563    
02564    if (!vms) {
02565       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02566       return -1;
02567    }
02568    if (option_debug > 2)
02569       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02570    if (vms->mailstream == NIL || !vms->mailstream) {
02571       if (option_debug)
02572          ast_log(LOG_DEBUG, "mailstream not set.\n");
02573    } else {
02574       stream = vms->mailstream;
02575    }
02576    /* debug = T;  user wants protocol telemetry? */
02577    debug = NIL;  /* NO protocol telemetry? */
02578 
02579    if (delimiter == '\0') {      /* did not probe the server yet */
02580       char *cp;
02581 #ifdef USE_SYSTEM_IMAP
02582 #include <imap/linkage.c>
02583 #elif defined(USE_SYSTEM_CCLIENT)
02584 #include <c-client/linkage.c>
02585 #else
02586 #include "linkage.c"
02587 #endif
02588       /* Connect to INBOX first to get folders delimiter */
02589       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02590       ast_mutex_lock(&vms->lock);
02591       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02592       ast_mutex_unlock(&vms->lock);
02593       if (stream == NIL) {
02594          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02595          return -1;
02596       }
02597       get_mailbox_delimiter(stream);
02598       /* update delimiter in imapfolder */
02599       for (cp = vms->imapfolder; *cp; cp++)
02600          if (*cp == '/')
02601             *cp = delimiter;
02602    }
02603    /* Now connect to the target folder */
02604    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02605    if (option_debug > 2)
02606       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02607    ast_mutex_lock(&vms->lock);
02608    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02609    ast_mutex_unlock(&vms->lock);
02610    if (vms->mailstream == NIL) {
02611       return -1;
02612    } else {
02613       return 0;
02614    }
02615 }
02616 
02617 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02618 {
02619    SEARCHPGM *pgm;
02620    SEARCHHEADER *hdr;
02621    int ret, urgent = 0;
02622 
02623    /* If Urgent, then look at INBOX */
02624    if (box == 11) {
02625       box = NEW_FOLDER;
02626       urgent = 1;
02627    }
02628 
02629    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02630    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02631    vms->imapversion = vmu->imapversion;
02632    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02633 
02634    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02635       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02636       return -1;
02637    }
02638    
02639    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02640    
02641    /* Check Quota */
02642    if  (box == 0)  {
02643       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02644       check_quota(vms, (char *) mbox(vmu, box));
02645    }
02646 
02647    ast_mutex_lock(&vms->lock);
02648    pgm = mail_newsearchpgm();
02649 
02650    /* Check IMAP folder for Asterisk messages only... */
02651    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02652    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02653    pgm->header = hdr;
02654    pgm->deleted = 0;
02655    pgm->undeleted = 1;
02656 
02657    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02658    if (box == NEW_FOLDER && urgent == 1) {
02659       pgm->unseen = 1;
02660       pgm->seen = 0;
02661       pgm->flagged = 1;
02662       pgm->unflagged = 0;
02663    } else if (box == NEW_FOLDER && urgent == 0) {
02664       pgm->unseen = 1;
02665       pgm->seen = 0;
02666       pgm->flagged = 0;
02667       pgm->unflagged = 1;
02668    } else if (box == OLD_FOLDER) {
02669       pgm->seen = 1;
02670       pgm->unseen = 0;
02671    }
02672 
02673    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02674 
02675    vms->vmArrayIndex = 0;
02676    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02677    vms->lastmsg = vms->vmArrayIndex - 1;
02678    mail_free_searchpgm(&pgm);
02679    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02680     * ensure to allocate enough space to account for all of them. Warn if old messages
02681     * have not been checked first as that is required.
02682     */
02683    if (box == 0 && !vms->dh_arraysize) {
02684       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02685    }
02686    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02687       ast_mutex_unlock(&vms->lock);
02688       return -1;
02689    }
02690 
02691    ast_mutex_unlock(&vms->lock);
02692    return 0;
02693 }
02694 
02695 static void write_file(char *filename, char *buffer, unsigned long len)
02696 {
02697    FILE *output;
02698 
02699    output = fopen (filename, "w");
02700    if (fwrite(buffer, len, 1, output) != 1) {
02701       if (ferror(output)) {
02702          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02703       }
02704    }
02705    fclose (output);
02706 }
02707 
02708 static void update_messages_by_imapuser(const char *user, unsigned long number)
02709 {
02710    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02711 
02712    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02713       return;
02714    }
02715 
02716    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02717 
02718    /* Ensure we have room for the next message. */
02719    if (vms->vmArrayIndex >= vms->msg_array_max) {
02720       long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
02721       if (!new_mem) {
02722          return;
02723       }
02724       vms->msgArray = new_mem;
02725       vms->msg_array_max *= 2;
02726    }
02727 
02728    vms->msgArray[vms->vmArrayIndex++] = number;
02729 }
02730 
02731 void mm_searched(MAILSTREAM *stream, unsigned long number)
02732 {
02733    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02734 
02735    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02736       return;
02737 
02738    update_messages_by_imapuser(user, number);
02739 }
02740 
02741 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02742 {
02743    struct ast_variable *var;
02744    struct ast_vm_user *vmu;
02745 
02746    vmu = ast_calloc(1, sizeof *vmu);
02747    if (!vmu)
02748       return NULL;
02749 
02750    populate_defaults(vmu);
02751    ast_set_flag(vmu, VM_ALLOCED);
02752 
02753    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02754    if (var) {
02755       apply_options_full(vmu, var);
02756       ast_variables_destroy(var);
02757       return vmu;
02758    } else {
02759       ast_free(vmu);
02760       return NULL;
02761    }
02762 }
02763 
02764 /* Interfaces to C-client */
02765 
02766 void mm_exists(MAILSTREAM * stream, unsigned long number)
02767 {
02768    /* mail_ping will callback here if new mail! */
02769    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02770    if (number == 0) return;
02771    set_update(stream);
02772 }
02773 
02774 
02775 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02776 {
02777    /* mail_ping will callback here if expunged mail! */
02778    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02779    if (number == 0) return;
02780    set_update(stream);
02781 }
02782 
02783 
02784 void mm_flags(MAILSTREAM * stream, unsigned long number)
02785 {
02786    /* mail_ping will callback here if read mail! */
02787    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02788    if (number == 0) return;
02789    set_update(stream);
02790 }
02791 
02792 
02793 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02794 {
02795    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02796    mm_log (string, errflg);
02797 }
02798 
02799 
02800 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02801 {
02802    if (delimiter == '\0') {
02803       delimiter = delim;
02804    }
02805 
02806    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02807    if (attributes & LATT_NOINFERIORS)
02808       ast_debug(5, "no inferiors\n");
02809    if (attributes & LATT_NOSELECT)
02810       ast_debug(5, "no select\n");
02811    if (attributes & LATT_MARKED)
02812       ast_debug(5, "marked\n");
02813    if (attributes & LATT_UNMARKED)
02814       ast_debug(5, "unmarked\n");
02815 }
02816 
02817 
02818 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02819 {
02820    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02821    if (attributes & LATT_NOINFERIORS)
02822       ast_debug(5, "no inferiors\n");
02823    if (attributes & LATT_NOSELECT)
02824       ast_debug(5, "no select\n");
02825    if (attributes & LATT_MARKED)
02826       ast_debug(5, "marked\n");
02827    if (attributes & LATT_UNMARKED)
02828       ast_debug(5, "unmarked\n");
02829 }
02830 
02831 
02832 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02833 {
02834    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02835    if (status->flags & SA_MESSAGES)
02836       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02837    if (status->flags & SA_RECENT)
02838       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02839    if (status->flags & SA_UNSEEN)
02840       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02841    if (status->flags & SA_UIDVALIDITY)
02842       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02843    if (status->flags & SA_UIDNEXT)
02844       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02845    ast_log(AST_LOG_NOTICE, "\n");
02846 }
02847 
02848 
02849 void mm_log(char *string, long errflg)
02850 {
02851    switch ((short) errflg) {
02852       case NIL:
02853          ast_debug(1, "IMAP Info: %s\n", string);
02854          break;
02855       case PARSE:
02856       case WARN:
02857          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02858          break;
02859       case ERROR:
02860          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02861          break;
02862    }
02863 }
02864 
02865 
02866 void mm_dlog(char *string)
02867 {
02868    ast_log(AST_LOG_NOTICE, "%s\n", string);
02869 }
02870 
02871 
02872 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02873 {
02874    struct ast_vm_user *vmu;
02875 
02876    ast_debug(4, "Entering callback mm_login\n");
02877 
02878    ast_copy_string(user, mb->user, MAILTMPLEN);
02879 
02880    /* We should only do this when necessary */
02881    if (!ast_strlen_zero(authpassword)) {
02882       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02883    } else {
02884       AST_LIST_TRAVERSE(&users, vmu, list) {
02885          if (!strcasecmp(mb->user, vmu->imapuser)) {
02886             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02887             break;
02888          }
02889       }
02890       if (!vmu) {
02891          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02892             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02893             free_user(vmu);
02894          }
02895       }
02896    }
02897 }
02898 
02899 
02900 void mm_critical(MAILSTREAM * stream)
02901 {
02902 }
02903 
02904 
02905 void mm_nocritical(MAILSTREAM * stream)
02906 {
02907 }
02908 
02909 
02910 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02911 {
02912    kill (getpid (), SIGSTOP);
02913    return NIL;
02914 }
02915 
02916 
02917 void mm_fatal(char *string)
02918 {
02919    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02920 }
02921 
02922 /* C-client callback to handle quota */
02923 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02924 {
02925    struct vm_state *vms;
02926    char *mailbox = stream->mailbox, *user;
02927    char buf[1024] = "";
02928    unsigned long usage = 0, limit = 0;
02929    
02930    while (pquota) {
02931       usage = pquota->usage;
02932       limit = pquota->limit;
02933       pquota = pquota->next;
02934    }
02935    
02936    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)))) {
02937       ast_log(AST_LOG_ERROR, "No state found.\n");
02938       return;
02939    }
02940 
02941    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02942 
02943    vms->quota_usage = usage;
02944    vms->quota_limit = limit;
02945 }
02946 
02947 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02948 {
02949    char *start, *eol_pnt;
02950    int taglen;
02951 
02952    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02953       return NULL;
02954 
02955    taglen = strlen(tag) + 1;
02956    if (taglen < 1)
02957       return NULL;
02958 
02959    if (!(start = strstr(header, tag)))
02960       return NULL;
02961 
02962    /* Since we can be called multiple times we should clear our buffer */
02963    memset(buf, 0, len);
02964 
02965    ast_copy_string(buf, start+taglen, len);
02966    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02967       *eol_pnt = '\0';
02968    return buf;
02969 }
02970 
02971 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02972 {
02973    char *start, *quote, *eol_pnt;
02974 
02975    if (ast_strlen_zero(mailbox))
02976       return NULL;
02977 
02978    if (!(start = strstr(mailbox, "/user=")))
02979       return NULL;
02980 
02981    ast_copy_string(buf, start+6, len);
02982 
02983    if (!(quote = strchr(buf, '\"'))) {
02984       if (!(eol_pnt = strchr(buf, '/')))
02985          eol_pnt = strchr(buf,'}');
02986       *eol_pnt = '\0';
02987       return buf;
02988    } else {
02989       eol_pnt = strchr(buf+1,'\"');
02990       *eol_pnt = '\0';
02991       return buf+1;
02992    }
02993 }
02994 
02995 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02996 {
02997    struct vm_state *vms_p;
02998 
02999    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03000    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03001       return vms_p;
03002    }
03003    if (option_debug > 4)
03004       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
03005    /* XXX: Is this correctly freed always? */
03006    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03007       return NULL;
03008    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03009    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03010    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03011    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03012    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03013    vms_p->imapversion = vmu->imapversion;
03014    if (option_debug > 4)
03015       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03016    vms_p->updated = 1;
03017    /* set mailbox to INBOX! */
03018    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03019    init_vm_state(vms_p);
03020    vmstate_insert(vms_p);
03021    return vms_p;
03022 }
03023 
03024 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03025 {
03026    struct vmstate *vlist = NULL;
03027 
03028    if (interactive) {
03029       struct vm_state *vms;
03030       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03031       vms = pthread_getspecific(ts_vmstate.key);
03032       return vms;
03033    }
03034 
03035    AST_LIST_LOCK(&vmstates);
03036    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03037       if (!vlist->vms) {
03038          ast_debug(3, "error: vms is NULL for %s\n", user);
03039          continue;
03040       }
03041       if (vlist->vms->imapversion != imapversion) {
03042          continue;
03043       }
03044       if (!vlist->vms->imapuser) {
03045          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03046          continue;
03047       }
03048 
03049       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03050          AST_LIST_UNLOCK(&vmstates);
03051          return vlist->vms;
03052       }
03053    }
03054    AST_LIST_UNLOCK(&vmstates);
03055 
03056    ast_debug(3, "%s not found in vmstates\n", user);
03057 
03058    return NULL;
03059 }
03060 
03061 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03062 {
03063 
03064    struct vmstate *vlist = NULL;
03065    const char *local_context = S_OR(context, "default");
03066 
03067    if (interactive) {
03068       struct vm_state *vms;
03069       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03070       vms = pthread_getspecific(ts_vmstate.key);
03071       return vms;
03072    }
03073 
03074    AST_LIST_LOCK(&vmstates);
03075    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03076       if (!vlist->vms) {
03077          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03078          continue;
03079       }
03080       if (vlist->vms->imapversion != imapversion) {
03081          continue;
03082       }
03083       if (!vlist->vms->username || !vlist->vms->context) {
03084          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03085          continue;
03086       }
03087 
03088       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);
03089       
03090       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03091          ast_debug(3, "Found it!\n");
03092          AST_LIST_UNLOCK(&vmstates);
03093          return vlist->vms;
03094       }
03095    }
03096    AST_LIST_UNLOCK(&vmstates);
03097 
03098    ast_debug(3, "%s not found in vmstates\n", mailbox);
03099 
03100    return NULL;
03101 }
03102 
03103 static void vmstate_insert(struct vm_state *vms) 
03104 {
03105    struct vmstate *v;
03106    struct vm_state *altvms;
03107 
03108    /* If interactive, it probably already exists, and we should
03109       use the one we already have since it is more up to date.
03110       We can compare the username to find the duplicate */
03111    if (vms->interactive == 1) {
03112       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03113       if (altvms) {  
03114          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03115          vms->newmessages = altvms->newmessages;
03116          vms->oldmessages = altvms->oldmessages;
03117          vms->vmArrayIndex = altvms->vmArrayIndex;
03118          /* XXX: no msgArray copying? */
03119          vms->lastmsg = altvms->lastmsg;
03120          vms->curmsg = altvms->curmsg;
03121          /* get a pointer to the persistent store */
03122          vms->persist_vms = altvms;
03123          /* Reuse the mailstream? */
03124 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03125          vms->mailstream = altvms->mailstream;
03126 #else
03127          vms->mailstream = NIL;
03128 #endif
03129       }
03130       return;
03131    }
03132 
03133    if (!(v = ast_calloc(1, sizeof(*v))))
03134       return;
03135    
03136    v->vms = vms;
03137 
03138    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03139 
03140    AST_LIST_LOCK(&vmstates);
03141    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03142    AST_LIST_UNLOCK(&vmstates);
03143 }
03144 
03145 static void vmstate_delete(struct vm_state *vms) 
03146 {
03147    struct vmstate *vc = NULL;
03148    struct vm_state *altvms = NULL;
03149 
03150    /* If interactive, we should copy pertinent info
03151       back to the persistent state (to make update immediate) */
03152    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03153       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03154       altvms->newmessages = vms->newmessages;
03155       altvms->oldmessages = vms->oldmessages;
03156       altvms->updated = 1;
03157       vms->mailstream = mail_close(vms->mailstream);
03158 
03159       /* Interactive states are not stored within the persistent list */
03160       return;
03161    }
03162    
03163    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03164    
03165    AST_LIST_LOCK(&vmstates);
03166    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03167       if (vc->vms == vms) {
03168          AST_LIST_REMOVE_CURRENT(list);
03169          break;
03170       }
03171    }
03172    AST_LIST_TRAVERSE_SAFE_END
03173    AST_LIST_UNLOCK(&vmstates);
03174    
03175    if (vc) {
03176       ast_mutex_destroy(&vc->vms->lock);
03177       ast_free(vc->vms->msgArray);
03178       vc->vms->msgArray = NULL;
03179       vc->vms->msg_array_max = 0;
03180       /* XXX: is no one supposed to free vms itself? */
03181       ast_free(vc);
03182    } else {
03183       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03184    }
03185 }
03186 
03187 static void set_update(MAILSTREAM * stream) 
03188 {
03189    struct vm_state *vms;
03190    char *mailbox = stream->mailbox, *user;
03191    char buf[1024] = "";
03192 
03193    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03194       if (user && option_debug > 2)
03195          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03196       return;
03197    }
03198 
03199    ast_debug(3, "User %s mailbox set for update.\n", user);
03200 
03201    vms->updated = 1; /* Set updated flag since mailbox changed */
03202 }
03203 
03204 static void init_vm_state(struct vm_state *vms) 
03205 {
03206    vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
03207    vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
03208    if (!vms->msgArray) {
03209       /* Out of mem? This can't be good. */
03210       vms->msg_array_max = 0;
03211    }
03212    vms->vmArrayIndex = 0;
03213    ast_mutex_init(&vms->lock);
03214 }
03215 
03216 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03217 {
03218    char *body_content;
03219    char *body_decoded;
03220    char *fn = is_intro ? vms->introfn : vms->fn;
03221    unsigned long len;
03222    unsigned long newlen;
03223    char filename[256];
03224    
03225    if (!body || body == NIL)
03226       return -1;
03227 
03228    ast_mutex_lock(&vms->lock);
03229    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03230    ast_mutex_unlock(&vms->lock);
03231    if (body_content != NIL) {
03232       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03233       /* ast_debug(1,body_content); */
03234       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03235       /* If the body of the file is empty, return an error */
03236       if (!newlen) {
03237          return -1;
03238       }
03239       write_file(filename, (char *) body_decoded, newlen);
03240    } else {
03241       ast_debug(5, "Body of message is NULL.\n");
03242       return -1;
03243    }
03244    return 0;
03245 }
03246 
03247 /*! 
03248  * \brief Get delimiter via mm_list callback 
03249  * \param stream
03250  *
03251  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03252  */
03253 /* MUTEX should already be held */
03254 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03255    char tmp[50];
03256    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03257    mail_list(stream, tmp, "*");
03258 }
03259 
03260 /*! 
03261  * \brief Check Quota for user 
03262  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03263  * \param mailbox the mailbox to check the quota for.
03264  *
03265  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03266  */
03267 static void check_quota(struct vm_state *vms, char *mailbox) {
03268    ast_mutex_lock(&vms->lock);
03269    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03270    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03271    if (vms && vms->mailstream != NULL) {
03272       imap_getquotaroot(vms->mailstream, mailbox);
03273    } else {
03274       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03275    }
03276    ast_mutex_unlock(&vms->lock);
03277 }
03278 
03279 #endif /* IMAP_STORAGE */
03280 
03281 /*! \brief Lock file path
03282  * only return failure if ast_lock_path returns 'timeout',
03283  * not if the path does not exist or any other reason
03284  */
03285 static int vm_lock_path(const char *path)
03286 {
03287    switch (ast_lock_path(path)) {
03288    case AST_LOCK_TIMEOUT:
03289       return -1;
03290    default:
03291       return 0;
03292    }
03293 }
03294 
03295 
03296 #ifdef ODBC_STORAGE
03297 struct generic_prepare_struct {
03298    char *sql;
03299    int argc;
03300    char **argv;
03301 };
03302 
03303 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03304 {
03305    struct generic_prepare_struct *gps = data;
03306    int res, i;
03307    SQLHSTMT stmt;
03308 
03309    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03310    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03311       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03312       return NULL;
03313    }
03314    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03315    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03316       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03317       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03318       return NULL;
03319    }
03320    for (i = 0; i < gps->argc; i++)
03321       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03322 
03323    return stmt;
03324 }
03325 
03326 /*!
03327  * \brief Retrieves a file from an ODBC data store.
03328  * \param dir the path to the file to be retreived.
03329  * \param msgnum the message number, such as within a mailbox folder.
03330  * 
03331  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03332  * 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.
03333  *
03334  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03335  * The output is the message information file with the name msgnum and the extension .txt
03336  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03337  * 
03338  * \return 0 on success, -1 on error.
03339  */
03340 static int retrieve_file(char *dir, int msgnum)
03341 {
03342    int x = 0;
03343    int res;
03344    int fd = -1;
03345    size_t fdlen = 0;
03346    void *fdm = MAP_FAILED;
03347    SQLSMALLINT colcount = 0;
03348    SQLHSTMT stmt;
03349    char sql[PATH_MAX];
03350    char fmt[80]="";
03351    char *c;
03352    char coltitle[256];
03353    SQLSMALLINT collen;
03354    SQLSMALLINT datatype;
03355    SQLSMALLINT decimaldigits;
03356    SQLSMALLINT nullable;
03357    SQLULEN colsize;
03358    SQLLEN colsize2;
03359    FILE *f = NULL;
03360    char rowdata[80];
03361    char fn[PATH_MAX];
03362    char full_fn[PATH_MAX];
03363    char msgnums[80];
03364    char *argv[] = { dir, msgnums };
03365    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03366 
03367    struct odbc_obj *obj;
03368    obj = ast_odbc_request_obj(odbc_database, 0);
03369    if (obj) {
03370       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03371       c = strchr(fmt, '|');
03372       if (c)
03373          *c = '\0';
03374       if (!strcasecmp(fmt, "wav49"))
03375          strcpy(fmt, "WAV");
03376       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03377       if (msgnum > -1)
03378          make_file(fn, sizeof(fn), dir, msgnum);
03379       else
03380          ast_copy_string(fn, dir, sizeof(fn));
03381 
03382       /* Create the information file */
03383       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03384       
03385       if (!(f = fopen(full_fn, "w+"))) {
03386          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03387          goto yuck;
03388       }
03389       
03390       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03391       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03392       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03393       if (!stmt) {
03394          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03395          ast_odbc_release_obj(obj);
03396          goto yuck;
03397       }
03398       res = SQLFetch(stmt);
03399       if (res == SQL_NO_DATA) {
03400          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03401          ast_odbc_release_obj(obj);
03402          goto yuck;
03403       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03404          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03405          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03406          ast_odbc_release_obj(obj);
03407          goto yuck;
03408       }
03409       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03410       if (fd < 0) {
03411          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03412          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03413          ast_odbc_release_obj(obj);
03414          goto yuck;
03415       }
03416       res = SQLNumResultCols(stmt, &colcount);
03417       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03418          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03419          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03420          ast_odbc_release_obj(obj);
03421          goto yuck;
03422       }
03423       if (f) 
03424          fprintf(f, "[message]\n");
03425       for (x = 0; x < colcount; x++) {
03426          rowdata[0] = '\0';
03427          colsize = 0;
03428          collen = sizeof(coltitle);
03429          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03430                   &datatype, &colsize, &decimaldigits, &nullable);
03431          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03432             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03433             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03434             ast_odbc_release_obj(obj);
03435             goto yuck;
03436          }
03437          if (!strcasecmp(coltitle, "recording")) {
03438             off_t offset;
03439             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03440             fdlen = colsize2;
03441             if (fd > -1) {
03442                char tmp[1]="";
03443                lseek(fd, fdlen - 1, SEEK_SET);
03444                if (write(fd, tmp, 1) != 1) {
03445                   close(fd);
03446                   fd = -1;
03447                   continue;
03448                }
03449                /* Read out in small chunks */
03450                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03451                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03452                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03453                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03454                      ast_odbc_release_obj(obj);
03455                      goto yuck;
03456                   } else {
03457                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03458                      munmap(fdm, CHUNKSIZE);
03459                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03460                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03461                         unlink(full_fn);
03462                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03463                         ast_odbc_release_obj(obj);
03464                         goto yuck;
03465                      }
03466                   }
03467                }
03468                if (truncate(full_fn, fdlen) < 0) {
03469                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03470                }
03471             }
03472          } else {
03473             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03474             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03475                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03476                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03477                ast_odbc_release_obj(obj);
03478                goto yuck;
03479             }
03480             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03481                fprintf(f, "%s=%s\n", coltitle, rowdata);
03482          }
03483       }
03484       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03485       ast_odbc_release_obj(obj);
03486    } else
03487       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03488 yuck:
03489    if (f)
03490       fclose(f);
03491    if (fd > -1)
03492       close(fd);
03493    return x - 1;
03494 }
03495 
03496 /*!
03497  * \brief Determines the highest message number in use for a given user and mailbox folder.
03498  * \param vmu 
03499  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03500  *
03501  * This method is used when mailboxes are stored in an ODBC back end.
03502  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03503  *
03504  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03505 
03506  */
03507 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03508 {
03509    int x = 0;
03510    int res;
03511    SQLHSTMT stmt;
03512    char sql[PATH_MAX];
03513    char rowdata[20];
03514    char *argv[] = { dir };
03515    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03516 
03517    struct odbc_obj *obj;
03518    obj = ast_odbc_request_obj(odbc_database, 0);
03519    if (obj) {
03520       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03521 
03522       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03523       if (!stmt) {
03524          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03525          ast_odbc_release_obj(obj);
03526          goto yuck;
03527       }
03528       res = SQLFetch(stmt);
03529       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03530          if (res == SQL_NO_DATA) {
03531             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03532          } else {
03533             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03534          }
03535 
03536          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03537          ast_odbc_release_obj(obj);
03538          goto yuck;
03539       }
03540       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03541       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03542          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03543          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03544          ast_odbc_release_obj(obj);
03545          goto yuck;
03546       }
03547       if (sscanf(rowdata, "%30d", &x) != 1)
03548          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03549       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03550       ast_odbc_release_obj(obj);
03551       return x;
03552    } else
03553       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03554 yuck:
03555    return x - 1;
03556 }
03557 
03558 /*!
03559  * \brief Determines if the specified message exists.
03560  * \param dir the folder the mailbox folder to look for messages. 
03561  * \param msgnum the message index to query for.
03562  *
03563  * This method is used when mailboxes are stored in an ODBC back end.
03564  *
03565  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03566  */
03567 static int message_exists(char *dir, int msgnum)
03568 {
03569    int x = 0;
03570    int res;
03571    SQLHSTMT stmt;
03572    char sql[PATH_MAX];
03573    char rowdata[20];
03574    char msgnums[20];
03575    char *argv[] = { dir, msgnums };
03576    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03577 
03578    struct odbc_obj *obj;
03579    obj = ast_odbc_request_obj(odbc_database, 0);
03580    if (obj) {
03581       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03582       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03583       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03584       if (!stmt) {
03585          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03586          ast_odbc_release_obj(obj);
03587          goto yuck;
03588       }
03589       res = SQLFetch(stmt);
03590       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03591          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03592          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03593          ast_odbc_release_obj(obj);
03594          goto yuck;
03595       }
03596       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03597       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03598          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03599          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03600          ast_odbc_release_obj(obj);
03601          goto yuck;
03602       }
03603       if (sscanf(rowdata, "%30d", &x) != 1)
03604          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03605       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03606       ast_odbc_release_obj(obj);
03607    } else
03608       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03609 yuck:
03610    return x;
03611 }
03612 
03613 /*!
03614  * \brief returns the number of messages found.
03615  * \param vmu
03616  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03617  *
03618  * This method is used when mailboxes are stored in an ODBC back end.
03619  *
03620  * \return The count of messages being zero or more, less than zero on error.
03621  */
03622 static int count_messages(struct ast_vm_user *vmu, char *dir)
03623 {
03624    int x = 0;
03625    int res;
03626    SQLHSTMT stmt;
03627    char sql[PATH_MAX];
03628    char rowdata[20];
03629    char *argv[] = { dir };
03630    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03631 
03632    struct odbc_obj *obj;
03633    obj = ast_odbc_request_obj(odbc_database, 0);
03634    if (obj) {
03635       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03636       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03637       if (!stmt) {
03638          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03639          ast_odbc_release_obj(obj);
03640          goto yuck;
03641       }
03642       res = SQLFetch(stmt);
03643       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03644          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03645          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03646          ast_odbc_release_obj(obj);
03647          goto yuck;
03648       }
03649       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03650       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03651          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03652          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03653          ast_odbc_release_obj(obj);
03654          goto yuck;
03655       }
03656       if (sscanf(rowdata, "%30d", &x) != 1)
03657          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03658       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03659       ast_odbc_release_obj(obj);
03660       return x;
03661    } else
03662       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03663 yuck:
03664    return x - 1;
03665 
03666 }
03667 
03668 /*!
03669  * \brief Deletes a message from the mailbox folder.
03670  * \param sdir The mailbox folder to work in.
03671  * \param smsg The message index to be deleted.
03672  *
03673  * This method is used when mailboxes are stored in an ODBC back end.
03674  * The specified message is directly deleted from the database 'voicemessages' table.
03675  * 
03676  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03677  */
03678 static void delete_file(const char *sdir, int smsg)
03679 {
03680    SQLHSTMT stmt;
03681    char sql[PATH_MAX];
03682    char msgnums[20];
03683    char *argv[] = { NULL, msgnums };
03684    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03685    struct odbc_obj *obj;
03686 
03687    argv[0] = ast_strdupa(sdir);
03688 
03689    obj = ast_odbc_request_obj(odbc_database, 0);
03690    if (obj) {
03691       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03692       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03693       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03694       if (!stmt)
03695          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03696       else
03697          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03698       ast_odbc_release_obj(obj);
03699    } else
03700       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03701    return;  
03702 }
03703 
03704 /*!
03705  * \brief Copies a voicemail from one mailbox to another.
03706  * \param sdir the folder for which to look for the message to be copied.
03707  * \param smsg the index of the message to be copied.
03708  * \param ddir the destination folder to copy the message into.
03709  * \param dmsg the index to be used for the copied message.
03710  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03711  * \param dmailboxcontext The context for the destination user.
03712  *
03713  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03714  */
03715 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03716 {
03717    SQLHSTMT stmt;
03718    char sql[512];
03719    char msgnums[20];
03720    char msgnumd[20];
03721    struct odbc_obj *obj;
03722    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03723    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03724 
03725    delete_file(ddir, dmsg);
03726    obj = ast_odbc_request_obj(odbc_database, 0);
03727    if (obj) {
03728       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03729       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03730       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);
03731       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03732       if (!stmt)
03733          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03734       else
03735          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03736       ast_odbc_release_obj(obj);
03737    } else
03738       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03739    return;  
03740 }
03741 
03742 struct insert_data {
03743    char *sql;
03744    const char *dir;
03745    const char *msgnums;
03746    void *data;
03747    SQLLEN datalen;
03748    SQLLEN indlen;
03749    const char *context;
03750    const char *macrocontext;
03751    const char *callerid;
03752    const char *origtime;
03753    const char *duration;
03754    const char *mailboxuser;
03755    const char *mailboxcontext;
03756    const char *category;
03757    const char *flag;
03758 };
03759 
03760 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03761 {
03762    struct insert_data *data = vdata;
03763    int res;
03764    SQLHSTMT stmt;
03765 
03766    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03767    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03768       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03769       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03770       return NULL;
03771    }
03772 
03773    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03774    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03775    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03776    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03777    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03778    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03779    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03780    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03781    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03782    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03783    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03784    if (!ast_strlen_zero(data->category)) {
03785       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03786    }
03787    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03788    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03789       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03790       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03791       return NULL;
03792    }
03793 
03794    return stmt;
03795 }
03796 
03797 /*!
03798  * \brief Stores a voicemail into the database.
03799  * \param dir the folder the mailbox folder to store the message.
03800  * \param mailboxuser the user owning the mailbox folder.
03801  * \param mailboxcontext
03802  * \param msgnum the message index for the message to be stored.
03803  *
03804  * This method is used when mailboxes are stored in an ODBC back end.
03805  * The message sound file and information file is looked up on the file system. 
03806  * A SQL query is invoked to store the message into the (MySQL) database.
03807  *
03808  * \return the zero on success -1 on error.
03809  */
03810 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03811 {
03812    int res = 0;
03813    int fd = -1;
03814    void *fdm = MAP_FAILED;
03815    off_t fdlen = -1;
03816    SQLHSTMT stmt;
03817    char sql[PATH_MAX];
03818    char msgnums[20];
03819    char fn[PATH_MAX];
03820    char full_fn[PATH_MAX];
03821    char fmt[80]="";
03822    char *c;
03823    struct ast_config *cfg = NULL;
03824    struct odbc_obj *obj;
03825    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03826       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03827    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03828 
03829    delete_file(dir, msgnum);
03830    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03831       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03832       return -1;
03833    }
03834 
03835    do {
03836       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03837       c = strchr(fmt, '|');
03838       if (c)
03839          *c = '\0';
03840       if (!strcasecmp(fmt, "wav49"))
03841          strcpy(fmt, "WAV");
03842       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03843       if (msgnum > -1)
03844          make_file(fn, sizeof(fn), dir, msgnum);
03845       else
03846          ast_copy_string(fn, dir, sizeof(fn));
03847       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03848       cfg = ast_config_load(full_fn, config_flags);
03849       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03850       fd = open(full_fn, O_RDWR);
03851       if (fd < 0) {
03852          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03853          res = -1;
03854          break;
03855       }
03856       if (valid_config(cfg)) {
03857          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03858             idata.context = "";
03859          }
03860          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03861             idata.macrocontext = "";
03862          }
03863          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03864             idata.callerid = "";
03865          }
03866          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03867             idata.origtime = "";
03868          }
03869          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03870             idata.duration = "";
03871          }
03872          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03873             idata.category = "";
03874          }
03875          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03876             idata.flag = "";
03877          }
03878       }
03879       fdlen = lseek(fd, 0, SEEK_END);
03880       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03881          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03882          res = -1;
03883          break;
03884       }
03885       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03886       if (fdm == MAP_FAILED) {
03887          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03888          res = -1;
03889          break;
03890       } 
03891       idata.data = fdm;
03892       idata.datalen = idata.indlen = fdlen;
03893 
03894       if (!ast_strlen_zero(idata.category)) 
03895          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03896       else
03897          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03898 
03899       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03900          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03901       } else {
03902          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03903          res = -1;
03904       }
03905    } while (0);
03906    if (obj) {
03907       ast_odbc_release_obj(obj);
03908    }
03909    if (valid_config(cfg))
03910       ast_config_destroy(cfg);
03911    if (fdm != MAP_FAILED)
03912       munmap(fdm, fdlen);
03913    if (fd > -1)
03914       close(fd);
03915    return res;
03916 }
03917 
03918 /*!
03919  * \brief Renames a message in a mailbox folder.
03920  * \param sdir The folder of the message to be renamed.
03921  * \param smsg The index of the message to be renamed.
03922  * \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.
03923  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03924  * \param ddir The destination folder for the message to be renamed into
03925  * \param dmsg The destination message for the message to be renamed.
03926  *
03927  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03928  * 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.
03929  * 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.
03930  */
03931 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03932 {
03933    SQLHSTMT stmt;
03934    char sql[PATH_MAX];
03935    char msgnums[20];
03936    char msgnumd[20];
03937    struct odbc_obj *obj;
03938    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03939    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03940 
03941    delete_file(ddir, dmsg);
03942    obj = ast_odbc_request_obj(odbc_database, 0);
03943    if (obj) {
03944       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03945       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03946       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03947       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03948       if (!stmt)
03949          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03950       else
03951          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03952       ast_odbc_release_obj(obj);
03953    } else
03954       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03955    return;  
03956 }
03957 
03958 /*!
03959  * \brief Removes a voicemail message file.
03960  * \param dir the path to the message file.
03961  * \param msgnum the unique number for the message within the mailbox.
03962  *
03963  * Removes the message content file and the information file.
03964  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03965  * Typical use is to clean up after a RETRIEVE operation. 
03966  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03967  * \return zero on success, -1 on error.
03968  */
03969 static int remove_file(char *dir, int msgnum)
03970 {
03971    char fn[PATH_MAX];
03972    char full_fn[PATH_MAX];
03973    char msgnums[80];
03974    
03975    if (msgnum > -1) {
03976       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03977       make_file(fn, sizeof(fn), dir, msgnum);
03978    } else
03979       ast_copy_string(fn, dir, sizeof(fn));
03980    ast_filedelete(fn, NULL);  
03981    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03982    unlink(full_fn);
03983    return 0;
03984 }
03985 #else
03986 #ifndef IMAP_STORAGE
03987 /*!
03988  * \brief Find all .txt files - even if they are not in sequence from 0000.
03989  * \param vmu
03990  * \param dir
03991  *
03992  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03993  *
03994  * \return the count of messages, zero or more.
03995  */
03996 static int count_messages(struct ast_vm_user *vmu, char *dir)
03997 {
03998 
03999    int vmcount = 0;
04000    DIR *vmdir = NULL;
04001    struct dirent *vment = NULL;
04002 
04003    if (vm_lock_path(dir))
04004       return ERROR_LOCK_PATH;
04005 
04006    if ((vmdir = opendir(dir))) {
04007       while ((vment = readdir(vmdir))) {
04008          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04009             vmcount++;
04010          }
04011       }
04012       closedir(vmdir);
04013    }
04014    ast_unlock_path(dir);
04015    
04016    return vmcount;
04017 }
04018 
04019 /*!
04020  * \brief Renames a message in a mailbox folder.
04021  * \param sfn The path to the mailbox information and data file to be renamed.
04022  * \param dfn The path for where the message data and information files will be renamed to.
04023  *
04024  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04025  */
04026 static void rename_file(char *sfn, char *dfn)
04027 {
04028    char stxt[PATH_MAX];
04029    char dtxt[PATH_MAX];
04030    ast_filerename(sfn, dfn, NULL);
04031    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04032    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04033    if (ast_check_realtime("voicemail_data")) {
04034       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04035    }
04036    rename(stxt, dtxt);
04037 }
04038 
04039 /*! 
04040  * \brief Determines the highest message number in use for a given user and mailbox folder.
04041  * \param vmu 
04042  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04043  *
04044  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04045  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04046  *
04047  * \note Should always be called with a lock already set on dir.
04048  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04049  */
04050 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04051 {
04052    int x;
04053    unsigned char map[MAXMSGLIMIT] = "";
04054    DIR *msgdir;
04055    struct dirent *msgdirent;
04056    int msgdirint;
04057    char extension[4];
04058    int stopcount = 0;
04059 
04060    /* Reading the entire directory into a file map scales better than
04061     * doing a stat repeatedly on a predicted sequence.  I suspect this
04062     * is partially due to stat(2) internally doing a readdir(2) itself to
04063     * find each file. */
04064    if (!(msgdir = opendir(dir))) {
04065       return -1;
04066    }
04067 
04068    while ((msgdirent = readdir(msgdir))) {
04069       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04070          map[msgdirint] = 1;
04071          stopcount++;
04072          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04073       }
04074    }
04075    closedir(msgdir);
04076 
04077    for (x = 0; x < vmu->maxmsg; x++) {
04078       if (map[x] == 1) {
04079          stopcount--;
04080       } else if (map[x] == 0 && !stopcount) {
04081          break;
04082       }
04083    }
04084 
04085    return x - 1;
04086 }
04087 
04088 #endif /* #ifndef IMAP_STORAGE */
04089 #endif /* #else of #ifdef ODBC_STORAGE */
04090 #ifndef IMAP_STORAGE
04091 /*!
04092  * \brief Utility function to copy a file.
04093  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04094  * \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.
04095  *
04096  * 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.
04097  * The copy operation copies up to 4096 bytes at once.
04098  *
04099  * \return zero on success, -1 on error.
04100  */
04101 static int copy(char *infile, char *outfile)
04102 {
04103    int ifd;
04104    int ofd;
04105    int res;
04106    int len;
04107    char buf[4096];
04108 
04109 #ifdef HARDLINK_WHEN_POSSIBLE
04110    /* Hard link if possible; saves disk space & is faster */
04111    if (link(infile, outfile)) {
04112 #endif
04113       if ((ifd = open(infile, O_RDONLY)) < 0) {
04114          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04115          return -1;
04116       }
04117       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04118          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04119          close(ifd);
04120          return -1;
04121       }
04122       do {
04123          len = read(ifd, buf, sizeof(buf));
04124          if (len < 0) {
04125             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04126             close(ifd);
04127             close(ofd);
04128             unlink(outfile);
04129          } else if (len) {
04130             res = write(ofd, buf, len);
04131             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04132                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04133                close(ifd);
04134                close(ofd);
04135                unlink(outfile);
04136             }
04137          }
04138       } while (len);
04139       close(ifd);
04140       close(ofd);
04141       return 0;
04142 #ifdef HARDLINK_WHEN_POSSIBLE
04143    } else {
04144       /* Hard link succeeded */
04145       return 0;
04146    }
04147 #endif
04148 }
04149 
04150 /*!
04151  * \brief Copies a voicemail information (envelope) file.
04152  * \param frompath
04153  * \param topath 
04154  *
04155  * Every voicemail has the data (.wav) file, and the information file.
04156  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04157  * This is used by the COPY macro when not using IMAP storage.
04158  */
04159 static void copy_plain_file(char *frompath, char *topath)
04160 {
04161    char frompath2[PATH_MAX], topath2[PATH_MAX];
04162    struct ast_variable *tmp,*var = NULL;
04163    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04164    ast_filecopy(frompath, topath, NULL);
04165    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04166    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04167    if (ast_check_realtime("voicemail_data")) {
04168       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04169       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04170       for (tmp = var; tmp; tmp = tmp->next) {
04171          if (!strcasecmp(tmp->name, "origmailbox")) {
04172             origmailbox = tmp->value;
04173          } else if (!strcasecmp(tmp->name, "context")) {
04174             context = tmp->value;
04175          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04176             macrocontext = tmp->value;
04177          } else if (!strcasecmp(tmp->name, "exten")) {
04178             exten = tmp->value;
04179          } else if (!strcasecmp(tmp->name, "priority")) {
04180             priority = tmp->value;
04181          } else if (!strcasecmp(tmp->name, "callerchan")) {
04182             callerchan = tmp->value;
04183          } else if (!strcasecmp(tmp->name, "callerid")) {
04184             callerid = tmp->value;
04185          } else if (!strcasecmp(tmp->name, "origdate")) {
04186             origdate = tmp->value;
04187          } else if (!strcasecmp(tmp->name, "origtime")) {
04188             origtime = tmp->value;
04189          } else if (!strcasecmp(tmp->name, "category")) {
04190             category = tmp->value;
04191          } else if (!strcasecmp(tmp->name, "duration")) {
04192             duration = tmp->value;
04193          }
04194       }
04195       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);
04196    }
04197    copy(frompath2, topath2);
04198    ast_variables_destroy(var);
04199 }
04200 #endif
04201 
04202 /*! 
04203  * \brief Removes the voicemail sound and information file.
04204  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04205  *
04206  * This is used by the DELETE macro when voicemails are stored on the file system.
04207  *
04208  * \return zero on success, -1 on error.
04209  */
04210 static int vm_delete(char *file)
04211 {
04212    char *txt;
04213    int txtsize = 0;
04214 
04215    txtsize = (strlen(file) + 5)*sizeof(char);
04216    txt = ast_alloca(txtsize);
04217    /* Sprintf here would safe because we alloca'd exactly the right length,
04218     * but trying to eliminate all sprintf's anyhow
04219     */
04220    if (ast_check_realtime("voicemail_data")) {
04221       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04222    }
04223    snprintf(txt, txtsize, "%s.txt", file);
04224    unlink(txt);
04225    return ast_filedelete(file, NULL);
04226 }
04227 
04228 /*!
04229  * \brief utility used by inchar(), for base_encode()
04230  */
04231 static int inbuf(struct baseio *bio, FILE *fi)
04232 {
04233    int l;
04234 
04235    if (bio->ateof)
04236       return 0;
04237 
04238    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04239       if (ferror(fi))
04240          return -1;
04241 
04242       bio->ateof = 1;
04243       return 0;
04244    }
04245 
04246    bio->iolen = l;
04247    bio->iocp = 0;
04248 
04249    return 1;
04250 }
04251 
04252 /*!
04253  * \brief utility used by base_encode()
04254  */
04255 static int inchar(struct baseio *bio, FILE *fi)
04256 {
04257    if (bio->iocp>=bio->iolen) {
04258       if (!inbuf(bio, fi))
04259          return EOF;
04260    }
04261 
04262    return bio->iobuf[bio->iocp++];
04263 }
04264 
04265 /*!
04266  * \brief utility used by base_encode()
04267  */
04268 static int ochar(struct baseio *bio, int c, FILE *so)
04269 {
04270    if (bio->linelength >= BASELINELEN) {
04271       if (fputs(ENDL, so) == EOF) {
04272          return -1;
04273       }
04274 
04275       bio->linelength = 0;
04276    }
04277 
04278    if (putc(((unsigned char) c), so) == EOF) {
04279       return -1;
04280    }
04281 
04282    bio->linelength++;
04283 
04284    return 1;
04285 }
04286 
04287 /*!
04288  * \brief Performs a base 64 encode algorithm on the contents of a File
04289  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04290  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04291  *
04292  * 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 ?
04293  *
04294  * \return zero on success, -1 on error.
04295  */
04296 static int base_encode(char *filename, FILE *so)
04297 {
04298    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04299       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04300       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04301       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04302    int i, hiteof = 0;
04303    FILE *fi;
04304    struct baseio bio;
04305 
04306    memset(&bio, 0, sizeof(bio));
04307    bio.iocp = BASEMAXINLINE;
04308 
04309    if (!(fi = fopen(filename, "rb"))) {
04310       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04311       return -1;
04312    }
04313 
04314    while (!hiteof){
04315       unsigned char igroup[3], ogroup[4];
04316       int c, n;
04317 
04318       memset(igroup, 0, sizeof(igroup));
04319 
04320       for (n = 0; n < 3; n++) {
04321          if ((c = inchar(&bio, fi)) == EOF) {
04322             hiteof = 1;
04323             break;
04324          }
04325 
04326          igroup[n] = (unsigned char) c;
04327       }
04328 
04329       if (n > 0) {
04330          ogroup[0]= dtable[igroup[0] >> 2];
04331          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04332          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04333          ogroup[3]= dtable[igroup[2] & 0x3F];
04334 
04335          if (n < 3) {
04336             ogroup[3] = '=';
04337 
04338             if (n < 2)
04339                ogroup[2] = '=';
04340          }
04341 
04342          for (i = 0; i < 4; i++)
04343             ochar(&bio, ogroup[i], so);
04344       }
04345    }
04346 
04347    fclose(fi);
04348    
04349    if (fputs(ENDL, so) == EOF) {
04350       return 0;
04351    }
04352 
04353    return 1;
04354 }
04355 
04356 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)
04357 {
04358    char callerid[256];
04359    char num[12];
04360    char fromdir[256], fromfile[256];
04361    struct ast_config *msg_cfg;
04362    const char *origcallerid, *origtime;
04363    char origcidname[80], origcidnum[80], origdate[80];
04364    int inttime;
04365    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04366 
04367    /* Prepare variables for substitution in email body and subject */
04368    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04369    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04370    snprintf(num, sizeof(num), "%d", msgnum);
04371    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04372    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04373    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04374    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04375       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04376    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04377    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04378    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04379    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04380    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04381 
04382    /* Retrieve info from VM attribute file */
04383    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04384    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04385    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04386       strcat(fromfile, ".txt");
04387    }
04388    if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
04389       if (option_debug > 0) {
04390          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04391       }
04392       return;
04393    }
04394 
04395    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04396       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04397       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04398       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04399       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04400    }
04401 
04402    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04403       struct timeval tv = { inttime, };
04404       struct ast_tm tm;
04405       ast_localtime(&tv, &tm, NULL);
04406       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04407       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04408    }
04409    ast_config_destroy(msg_cfg);
04410 }
04411 
04412 /*!
04413  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04414  * \param from The string to work with.
04415  * \param buf The buffer into which to write the modified quoted string.
04416  * \param maxlen Always zero, but see \see ast_str
04417  * 
04418  * \return The destination string with quotes wrapped on it (the to field).
04419  */
04420 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04421 {
04422    const char *ptr;
04423 
04424    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04425    ast_str_set(buf, maxlen, "\"");
04426    for (ptr = from; *ptr; ptr++) {
04427       if (*ptr == '"' || *ptr == '\\') {
04428          ast_str_append(buf, maxlen, "\\%c", *ptr);
04429       } else {
04430          ast_str_append(buf, maxlen, "%c", *ptr);
04431       }
04432    }
04433    ast_str_append(buf, maxlen, "\"");
04434 
04435    return ast_str_buffer(*buf);
04436 }
04437 
04438 /*! \brief
04439  * fill in *tm for current time according to the proper timezone, if any.
04440  * \return tm so it can be used as a function argument.
04441  */
04442 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04443 {
04444    const struct vm_zone *z = NULL;
04445    struct timeval t = ast_tvnow();
04446 
04447    /* Does this user have a timezone specified? */
04448    if (!ast_strlen_zero(vmu->zonetag)) {
04449       /* Find the zone in the list */
04450       AST_LIST_LOCK(&zones);
04451       AST_LIST_TRAVERSE(&zones, z, list) {
04452          if (!strcmp(z->name, vmu->zonetag))
04453             break;
04454       }
04455       AST_LIST_UNLOCK(&zones);
04456    }
04457    ast_localtime(&t, tm, z ? z->timezone : NULL);
04458    return tm;
04459 }
04460 
04461 /*!\brief Check if the string would need encoding within the MIME standard, to
04462  * avoid confusing certain mail software that expects messages to be 7-bit
04463  * clean.
04464  */
04465 static int check_mime(const char *str)
04466 {
04467    for (; *str; str++) {
04468       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04469          return 1;
04470       }
04471    }
04472    return 0;
04473 }
04474 
04475 /*!\brief Encode a string according to the MIME rules for encoding strings
04476  * that are not 7-bit clean or contain control characters.
04477  *
04478  * Additionally, if the encoded string would exceed the MIME limit of 76
04479  * characters per line, then the encoding will be broken up into multiple
04480  * sections, separated by a space character, in order to facilitate
04481  * breaking up the associated header across multiple lines.
04482  *
04483  * \param end An expandable buffer for holding the result
04484  * \param maxlen Always zero, but see \see ast_str
04485  * \param start A string to be encoded
04486  * \param preamble The length of the first line already used for this string,
04487  * to ensure that each line maintains a maximum length of 76 chars.
04488  * \param postamble the length of any additional characters appended to the
04489  * line, used to ensure proper field wrapping.
04490  * \retval The encoded string.
04491  */
04492 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04493 {
04494    struct ast_str *tmp = ast_str_alloca(80);
04495    int first_section = 1;
04496 
04497    ast_str_reset(*end);
04498    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04499    for (; *start; start++) {
04500       int need_encoding = 0;
04501       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04502          need_encoding = 1;
04503       }
04504       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04505          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04506          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04507          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04508          /* Start new line */
04509          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04510          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04511          first_section = 0;
04512       }
04513       if (need_encoding && *start == ' ') {
04514          ast_str_append(&tmp, -1, "_");
04515       } else if (need_encoding) {
04516          ast_str_append(&tmp, -1, "=%hhX", *start);
04517       } else {
04518          ast_str_append(&tmp, -1, "%c", *start);
04519       }
04520    }
04521    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04522    return ast_str_buffer(*end);
04523 }
04524 
04525 /*!
04526  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04527  * \param p The output file to generate the email contents into.
04528  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04529  * \param vmu The voicemail user who is sending the voicemail.
04530  * \param msgnum The message index in the mailbox folder.
04531  * \param context 
04532  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04533  * \param fromfolder
04534  * \param cidnum The caller ID number.
04535  * \param cidname The caller ID name.
04536  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04537  * \param attach2 
04538  * \param format The message sound file format. i.e. .wav
04539  * \param duration The time of the message content, in seconds.
04540  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04541  * \param chan
04542  * \param category
04543  * \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.
04544  * \param flag
04545  *
04546  * 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.
04547  */
04548 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)
04549 {
04550    char date[256];
04551    char host[MAXHOSTNAMELEN] = "";
04552    char who[256];
04553    char bound[256];
04554    char dur[256];
04555    struct ast_tm tm;
04556    char enc_cidnum[256] = "", enc_cidname[256] = "";
04557    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04558    char *greeting_attachment; 
04559    char filename[256];
04560 
04561    if (!str1 || !str2) {
04562       ast_free(str1);
04563       ast_free(str2);
04564       return;
04565    }
04566 
04567    if (cidnum) {
04568       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04569    }
04570    if (cidname) {
04571       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04572    }
04573    gethostname(host, sizeof(host) - 1);
04574 
04575    if (strchr(srcemail, '@')) {
04576       ast_copy_string(who, srcemail, sizeof(who));
04577    } else {
04578       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04579    }
04580 
04581    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04582    if (greeting_attachment) {
04583       *greeting_attachment++ = '\0';
04584    }
04585 
04586    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04587    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04588    fprintf(p, "Date: %s" ENDL, date);
04589 
04590    /* Set date format for voicemail mail */
04591    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04592 
04593    if (!ast_strlen_zero(fromstring)) {
04594       struct ast_channel *ast;
04595       if ((ast = ast_dummy_channel_alloc())) {
04596          char *ptr;
04597          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04598          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04599 
04600          if (check_mime(ast_str_buffer(str1))) {
04601             int first_line = 1;
04602             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04603             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04604                *ptr = '\0';
04605                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", 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 <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04611          } else {
04612             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
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 {
04619       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04620    }
04621 
04622    if (check_mime(vmu->fullname)) {
04623       int first_line = 1;
04624       char *ptr;
04625       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04626       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04627          *ptr = '\0';
04628          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04629          first_line = 0;
04630          /* Substring is smaller, so this will never grow */
04631          ast_str_set(&str2, 0, "%s", ptr + 1);
04632       }
04633       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04634    } else {
04635       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04636    }
04637 
04638    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04639       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04640       struct ast_channel *ast;
04641       if ((ast = ast_dummy_channel_alloc())) {
04642          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04643          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04644          if (check_mime(ast_str_buffer(str1))) {
04645             int first_line = 1;
04646             char *ptr;
04647             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04648             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04649                *ptr = '\0';
04650                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04651                first_line = 0;
04652                /* Substring is smaller, so this will never grow */
04653                ast_str_set(&str2, 0, "%s", ptr + 1);
04654             }
04655             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04656          } else {
04657             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04658          }
04659          ast = ast_channel_unref(ast);
04660       } else {
04661          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04662       }
04663    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04664       if (ast_strlen_zero(flag)) {
04665          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04666       } else {
04667          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04668       }
04669    } else {
04670       if (ast_strlen_zero(flag)) {
04671          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04672       } else {
04673          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04674       }
04675    }
04676 
04677    fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
04678       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04679    if (imap) {
04680       /* additional information needed for IMAP searching */
04681       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04682       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04683       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04684       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04685 #ifdef IMAP_STORAGE
04686       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04687 #else
04688       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04689 #endif
04690       /* flag added for Urgent */
04691       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04692       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04693       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04694       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04695       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04696       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04697       if (!ast_strlen_zero(category)) {
04698          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04699       } else {
04700          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04701       }
04702       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04703       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04704       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04705    }
04706    if (!ast_strlen_zero(cidnum)) {
04707       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04708    }
04709    if (!ast_strlen_zero(cidname)) {
04710       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04711    }
04712    fprintf(p, "MIME-Version: 1.0" ENDL);
04713    if (attach_user_voicemail) {
04714       /* Something unique. */
04715       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
04716          (int) getpid(), (unsigned int) ast_random());
04717 
04718       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04719       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04720       fprintf(p, "--%s" ENDL, bound);
04721    }
04722    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04723    if (emailbody || vmu->emailbody) {
04724       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04725       struct ast_channel *ast;
04726       if ((ast = ast_dummy_channel_alloc())) {
04727          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04728          ast_str_substitute_variables(&str1, 0, ast, e_body);
04729 #ifdef IMAP_STORAGE
04730             {
04731                /* Convert body to native line terminators for IMAP backend */
04732                char *line = ast_str_buffer(str1), *next;
04733                do {
04734                   /* Terminate line before outputting it to the file */
04735                   if ((next = strchr(line, '\n'))) {
04736                      *next++ = '\0';
04737                   }
04738                   fprintf(p, "%s" ENDL, line);
04739                   line = next;
04740                } while (!ast_strlen_zero(line));
04741             }
04742 #else
04743          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04744 #endif
04745          ast = ast_channel_unref(ast);
04746       } else {
04747          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04748       }
04749    } else if (msgnum > -1) {
04750       if (strcmp(vmu->mailbox, mailbox)) {
04751          /* Forwarded type */
04752          struct ast_config *msg_cfg;
04753          const char *v;
04754          int inttime;
04755          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04756          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04757          /* Retrieve info from VM attribute file */
04758          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04759          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04760          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04761             strcat(fromfile, ".txt");
04762          }
04763          if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
04764             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04765                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04766             }
04767 
04768             /* You might be tempted to do origdate, except that a) it's in the wrong
04769              * format, and b) it's missing for IMAP recordings. */
04770             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04771                struct timeval tv = { inttime, };
04772                struct ast_tm tm;
04773                ast_localtime(&tv, &tm, NULL);
04774                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04775             }
04776             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04777                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04778                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04779                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04780                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04781                date, origcallerid, origdate);
04782             ast_config_destroy(msg_cfg);
04783          } else {
04784             goto plain_message;
04785          }
04786       } else {
04787 plain_message:
04788          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04789             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04790             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04791             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04792             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04793       }
04794    } else {
04795       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04796             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04797    }
04798 
04799    if (imap || attach_user_voicemail) {
04800       if (!ast_strlen_zero(attach2)) {
04801          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04802          ast_debug(5, "creating second attachment filename %s\n", filename);
04803          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04804          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04805          ast_debug(5, "creating attachment filename %s\n", filename);
04806          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04807       } else {
04808          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04809          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04810          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04811       }
04812    }
04813    ast_free(str1);
04814    ast_free(str2);
04815 }
04816 
04817 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)
04818 {
04819    char tmpdir[256], newtmp[256];
04820    char fname[256];
04821    char tmpcmd[256];
04822    int tmpfd = -1;
04823    int soxstatus = 0;
04824 
04825    /* Eww. We want formats to tell us their own MIME type */
04826    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04827 
04828    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04829       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04830       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04831       tmpfd = mkstemp(newtmp);
04832       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04833       ast_debug(3, "newtmp: %s\n", newtmp);
04834       if (tmpfd > -1) {
04835          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04836          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04837             attach = newtmp;
04838             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04839          } else {
04840             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04841                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04842             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04843          }
04844       }
04845    }
04846    fprintf(p, "--%s" ENDL, bound);
04847    if (msgnum > -1)
04848       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04849    else
04850       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04851    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04852    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04853    if (msgnum > -1)
04854       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04855    else
04856       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04857    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04858    base_encode(fname, p);
04859    if (last)
04860       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04861    if (tmpfd > -1) {
04862       if (soxstatus == 0) {
04863          unlink(fname);
04864       }
04865       close(tmpfd);
04866       unlink(newtmp);
04867    }
04868    return 0;
04869 }
04870 
04871 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)
04872 {
04873    FILE *p = NULL;
04874    char tmp[80] = "/tmp/astmail-XXXXXX";
04875    char tmp2[256];
04876    char *stringp;
04877 
04878    if (vmu && ast_strlen_zero(vmu->email)) {
04879       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04880       return(0);
04881    }
04882 
04883    /* Mail only the first format */
04884    format = ast_strdupa(format);
04885    stringp = format;
04886    strsep(&stringp, "|");
04887 
04888    if (!strcmp(format, "wav49"))
04889       format = "WAV";
04890    ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
04891    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04892       command hangs */
04893    if ((p = vm_mkftemp(tmp)) == NULL) {
04894       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04895       return -1;
04896    } else {
04897       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04898       fclose(p);
04899       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04900       ast_safe_system(tmp2);
04901       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04902    }
04903    return 0;
04904 }
04905 
04906 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)
04907 {
04908    char enc_cidnum[256], enc_cidname[256];
04909    char date[256];
04910    char host[MAXHOSTNAMELEN] = "";
04911    char who[256];
04912    char dur[PATH_MAX];
04913    char tmp[80] = "/tmp/astmail-XXXXXX";
04914    char tmp2[PATH_MAX];
04915    struct ast_tm tm;
04916    FILE *p;
04917    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04918 
04919    if (!str1 || !str2) {
04920       ast_free(str1);
04921       ast_free(str2);
04922       return -1;
04923    }
04924 
04925    if (cidnum) {
04926       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04927    }
04928    if (cidname) {
04929       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04930    }
04931 
04932    if ((p = vm_mkftemp(tmp)) == NULL) {
04933       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04934       ast_free(str1);
04935       ast_free(str2);
04936       return -1;
04937    }
04938    gethostname(host, sizeof(host)-1);
04939    if (strchr(srcemail, '@')) {
04940       ast_copy_string(who, srcemail, sizeof(who));
04941    } else {
04942       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04943    }
04944    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04945    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04946    fprintf(p, "Date: %s\n", date);
04947 
04948    /* Reformat for custom pager format */
04949    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04950 
04951    if (!ast_strlen_zero(pagerfromstring)) {
04952       struct ast_channel *ast;
04953       if ((ast = ast_dummy_channel_alloc())) {
04954          char *ptr;
04955          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04956          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04957 
04958          if (check_mime(ast_str_buffer(str1))) {
04959             int first_line = 1;
04960             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04961             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04962                *ptr = '\0';
04963                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04964                first_line = 0;
04965                /* Substring is smaller, so this will never grow */
04966                ast_str_set(&str2, 0, "%s", ptr + 1);
04967             }
04968             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04969          } else {
04970             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04971          }
04972          ast = ast_channel_unref(ast);
04973       } else {
04974          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04975       }
04976    } else {
04977       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04978    }
04979 
04980    if (check_mime(vmu->fullname)) {
04981       int first_line = 1;
04982       char *ptr;
04983       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04984       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04985          *ptr = '\0';
04986          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04987          first_line = 0;
04988          /* Substring is smaller, so this will never grow */
04989          ast_str_set(&str2, 0, "%s", ptr + 1);
04990       }
04991       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04992    } else {
04993       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04994    }
04995 
04996    if (!ast_strlen_zero(pagersubject)) {
04997       struct ast_channel *ast;
04998       if ((ast = ast_dummy_channel_alloc())) {
04999          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05000          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05001          if (check_mime(ast_str_buffer(str1))) {
05002             int first_line = 1;
05003             char *ptr;
05004             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05005             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05006                *ptr = '\0';
05007                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05008                first_line = 0;
05009                /* Substring is smaller, so this will never grow */
05010                ast_str_set(&str2, 0, "%s", ptr + 1);
05011             }
05012             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05013          } else {
05014             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05015          }
05016          ast = ast_channel_unref(ast);
05017       } else {
05018          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05019       }
05020    } else {
05021       if (ast_strlen_zero(flag)) {
05022          fprintf(p, "Subject: New VM\n\n");
05023       } else {
05024          fprintf(p, "Subject: New %s VM\n\n", flag);
05025       }
05026    }
05027 
05028    if (pagerbody) {
05029       struct ast_channel *ast;
05030       if ((ast = ast_dummy_channel_alloc())) {
05031          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05032          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05033          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05034          ast = ast_channel_unref(ast);
05035       } else {
05036          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05037       }
05038    } else {
05039       fprintf(p, "New %s long %s msg in box %s\n"
05040             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05041    }
05042 
05043    fclose(p);
05044    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05045    ast_safe_system(tmp2);
05046    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05047    ast_free(str1);
05048    ast_free(str2);
05049    return 0;
05050 }
05051 
05052 /*!
05053  * \brief Gets the current date and time, as formatted string.
05054  * \param s The buffer to hold the output formatted date.
05055  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05056  * 
05057  * The date format string used is "%a %b %e %r UTC %Y".
05058  * 
05059  * \return zero on success, -1 on error.
05060  */
05061 static int get_date(char *s, int len)
05062 {
05063    struct ast_tm tm;
05064    struct timeval t = ast_tvnow();
05065    
05066    ast_localtime(&t, &tm, "UTC");
05067 
05068    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05069 }
05070 
05071 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05072 {
05073    int res;
05074    char fn[PATH_MAX];
05075    char dest[PATH_MAX];
05076 
05077    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05078 
05079    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05080       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05081       return -1;
05082    }
05083 
05084    RETRIEVE(fn, -1, ext, context);
05085    if (ast_fileexists(fn, NULL, NULL) > 0) {
05086       res = ast_stream_and_wait(chan, fn, ecodes);
05087       if (res) {
05088          DISPOSE(fn, -1);
05089          return res;
05090       }
05091    } else {
05092       /* Dispose just in case */
05093       DISPOSE(fn, -1);
05094       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05095       if (res)
05096          return res;
05097       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05098       if (res)
05099          return res;
05100    }
05101    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05102    return res;
05103 }
05104 
05105 static void free_zone(struct vm_zone *z)
05106 {
05107    ast_free(z);
05108 }
05109 
05110 #ifdef ODBC_STORAGE
05111 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05112 {
05113    int x = -1;
05114    int res;
05115    SQLHSTMT stmt = NULL;
05116    char sql[PATH_MAX];
05117    char rowdata[20];
05118    char tmp[PATH_MAX] = "";
05119    struct odbc_obj *obj = NULL;
05120    char *context;
05121    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05122 
05123    if (newmsgs)
05124       *newmsgs = 0;
05125    if (oldmsgs)
05126       *oldmsgs = 0;
05127    if (urgentmsgs)
05128       *urgentmsgs = 0;
05129 
05130    /* If no mailbox, return immediately */
05131    if (ast_strlen_zero(mailbox))
05132       return 0;
05133 
05134    ast_copy_string(tmp, mailbox, sizeof(tmp));
05135 
05136    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05137       int u, n, o;
05138       char *next, *remaining = tmp;
05139       while ((next = strsep(&remaining, " ,"))) {
05140          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05141             return -1;
05142          }
05143          if (urgentmsgs) {
05144             *urgentmsgs += u;
05145          }
05146          if (newmsgs) {
05147             *newmsgs += n;
05148          }
05149          if (oldmsgs) {
05150             *oldmsgs += o;
05151          }
05152       }
05153       return 0;
05154    }
05155 
05156    context = strchr(tmp, '@');
05157    if (context) {
05158       *context = '\0';
05159       context++;
05160    } else
05161       context = "default";
05162 
05163    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05164       do {
05165          if (newmsgs) {
05166             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05167             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05168                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05169                break;
05170             }
05171             res = SQLFetch(stmt);
05172             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05173                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05174                break;
05175             }
05176             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05177             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05178                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05179                break;
05180             }
05181             *newmsgs = atoi(rowdata);
05182             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05183          }
05184 
05185          if (oldmsgs) {
05186             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05187             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05188                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05189                break;
05190             }
05191             res = SQLFetch(stmt);
05192             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05193                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05194                break;
05195             }
05196             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05197             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05198                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05199                break;
05200             }
05201             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05202             *oldmsgs = atoi(rowdata);
05203          }
05204 
05205          if (urgentmsgs) {
05206             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05207             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05208                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05209                break;
05210             }
05211             res = SQLFetch(stmt);
05212             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05213                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05214                break;
05215             }
05216             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05217             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05218                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05219                break;
05220             }
05221             *urgentmsgs = atoi(rowdata);
05222          }
05223 
05224          x = 0;
05225       } while (0);
05226    } else {
05227       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05228    }
05229 
05230    if (stmt) {
05231       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05232    }
05233    if (obj) {
05234       ast_odbc_release_obj(obj);
05235    }
05236    return x;
05237 }
05238 
05239 /*!
05240  * \brief Gets the number of messages that exist in a mailbox folder.
05241  * \param context
05242  * \param mailbox
05243  * \param folder
05244  * 
05245  * This method is used when ODBC backend is used.
05246  * \return The number of messages in this mailbox folder (zero or more).
05247  */
05248 static int messagecount(const char *context, const char *mailbox, const char *folder)
05249 {
05250    struct odbc_obj *obj = NULL;
05251    int nummsgs = 0;
05252    int res;
05253    SQLHSTMT stmt = NULL;
05254    char sql[PATH_MAX];
05255    char rowdata[20];
05256    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05257    if (!folder)
05258       folder = "INBOX";
05259    /* If no mailbox, return immediately */
05260    if (ast_strlen_zero(mailbox))
05261       return 0;
05262 
05263    obj = ast_odbc_request_obj(odbc_database, 0);
05264    if (obj) {
05265       if (!strcmp(folder, "INBOX")) {
05266          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);
05267       } else {
05268          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05269       }
05270       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05271       if (!stmt) {
05272          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05273          goto yuck;
05274       }
05275       res = SQLFetch(stmt);
05276       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05277          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05278          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05279          goto yuck;
05280       }
05281       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05282       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05283          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05284          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05285          goto yuck;
05286       }
05287       nummsgs = atoi(rowdata);
05288       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05289    } else
05290       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05291 
05292 yuck:
05293    if (obj)
05294       ast_odbc_release_obj(obj);
05295    return nummsgs;
05296 }
05297 
05298 /** 
05299  * \brief Determines if the given folder has messages.
05300  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05301  * 
05302  * This function is used when the mailbox is stored in an ODBC back end.
05303  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05304  * \return 1 if the folder has one or more messages. zero otherwise.
05305  */
05306 static int has_voicemail(const char *mailbox, const char *folder)
05307 {
05308    char tmp[256], *tmp2 = tmp, *box, *context;
05309    ast_copy_string(tmp, mailbox, sizeof(tmp));
05310    while ((context = box = strsep(&tmp2, ",&"))) {
05311       strsep(&context, "@");
05312       if (ast_strlen_zero(context))
05313          context = "default";
05314       if (messagecount(context, box, folder))
05315          return 1;
05316    }
05317    return 0;
05318 }
05319 #endif
05320 #ifndef IMAP_STORAGE
05321 /*! 
05322  * \brief Copies a message from one mailbox to another.
05323  * \param chan
05324  * \param vmu
05325  * \param imbox
05326  * \param msgnum
05327  * \param duration
05328  * \param recip
05329  * \param fmt
05330  * \param dir
05331  * \param flag
05332  *
05333  * This is only used by file storage based mailboxes.
05334  *
05335  * \return zero on success, -1 on error.
05336  */
05337 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)
05338 {
05339    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05340    const char *frombox = mbox(vmu, imbox);
05341    const char *userfolder;
05342    int recipmsgnum;
05343    int res = 0;
05344 
05345    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05346 
05347    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05348       userfolder = "Urgent";
05349    } else {
05350       userfolder = "INBOX";
05351    }
05352 
05353    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05354 
05355    if (!dir)
05356       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05357    else
05358       ast_copy_string(fromdir, dir, sizeof(fromdir));
05359 
05360    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05361    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05362 
05363    if (vm_lock_path(todir))
05364       return ERROR_LOCK_PATH;
05365 
05366    recipmsgnum = last_message_index(recip, todir) + 1;
05367    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05368       make_file(topath, sizeof(topath), todir, recipmsgnum);
05369 #ifndef ODBC_STORAGE
05370       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05371          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05372       } else {
05373 #endif
05374          /* If we are prepending a message for ODBC, then the message already
05375           * exists in the database, but we want to force copying from the
05376           * filesystem (since only the FS contains the prepend). */
05377          copy_plain_file(frompath, topath);
05378          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05379          vm_delete(topath);
05380 #ifndef ODBC_STORAGE
05381       }
05382 #endif
05383    } else {
05384       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05385       res = -1;
05386    }
05387    ast_unlock_path(todir);
05388    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05389       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05390       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05391       flag);
05392    
05393    return res;
05394 }
05395 #endif
05396 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05397 
05398 static int messagecount(const char *context, const char *mailbox, const char *folder)
05399 {
05400    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05401 }
05402 
05403 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05404 {
05405    DIR *dir;
05406    struct dirent *de;
05407    char fn[256];
05408    int ret = 0;
05409 
05410    /* If no mailbox, return immediately */
05411    if (ast_strlen_zero(mailbox))
05412       return 0;
05413 
05414    if (ast_strlen_zero(folder))
05415       folder = "INBOX";
05416    if (ast_strlen_zero(context))
05417       context = "default";
05418 
05419    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05420 
05421    if (!(dir = opendir(fn)))
05422       return 0;
05423 
05424    while ((de = readdir(dir))) {
05425       if (!strncasecmp(de->d_name, "msg", 3)) {
05426          if (shortcircuit) {
05427             ret = 1;
05428             break;
05429          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05430             ret++;
05431          }
05432       }
05433    }
05434 
05435    closedir(dir);
05436 
05437    return ret;
05438 }
05439 
05440 /** 
05441  * \brief Determines if the given folder has messages.
05442  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05443  * \param folder the folder to look in
05444  *
05445  * This function is used when the mailbox is stored in a filesystem back end.
05446  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05447  * \return 1 if the folder has one or more messages. zero otherwise.
05448  */
05449 static int has_voicemail(const char *mailbox, const char *folder)
05450 {
05451    char tmp[256], *tmp2 = tmp, *box, *context;
05452    ast_copy_string(tmp, mailbox, sizeof(tmp));
05453    if (ast_strlen_zero(folder)) {
05454       folder = "INBOX";
05455    }
05456    while ((box = strsep(&tmp2, ",&"))) {
05457       if ((context = strchr(box, '@')))
05458          *context++ = '\0';
05459       else
05460          context = "default";
05461       if (__has_voicemail(context, box, folder, 1))
05462          return 1;
05463       /* If we are checking INBOX, we should check Urgent as well */
05464       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05465          return 1;
05466       }
05467    }
05468    return 0;
05469 }
05470 
05471 
05472 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05473 {
05474    char tmp[256];
05475    char *context;
05476 
05477    /* If no mailbox, return immediately */
05478    if (ast_strlen_zero(mailbox))
05479       return 0;
05480 
05481    if (newmsgs)
05482       *newmsgs = 0;
05483    if (oldmsgs)
05484       *oldmsgs = 0;
05485    if (urgentmsgs)
05486       *urgentmsgs = 0;
05487 
05488    if (strchr(mailbox, ',')) {
05489       int tmpnew, tmpold, tmpurgent;
05490       char *mb, *cur;
05491 
05492       ast_copy_string(tmp, mailbox, sizeof(tmp));
05493       mb = tmp;
05494       while ((cur = strsep(&mb, ", "))) {
05495          if (!ast_strlen_zero(cur)) {
05496             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05497                return -1;
05498             else {
05499                if (newmsgs)
05500                   *newmsgs += tmpnew; 
05501                if (oldmsgs)
05502                   *oldmsgs += tmpold;
05503                if (urgentmsgs)
05504                   *urgentmsgs += tmpurgent;
05505             }
05506          }
05507       }
05508       return 0;
05509    }
05510 
05511    ast_copy_string(tmp, mailbox, sizeof(tmp));
05512    
05513    if ((context = strchr(tmp, '@')))
05514       *context++ = '\0';
05515    else
05516       context = "default";
05517 
05518    if (newmsgs)
05519       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05520    if (oldmsgs)
05521       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05522    if (urgentmsgs)
05523       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05524 
05525    return 0;
05526 }
05527 
05528 #endif
05529 
05530 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05531 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05532 {
05533    int urgentmsgs = 0;
05534    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05535    if (newmsgs) {
05536       *newmsgs += urgentmsgs;
05537    }
05538    return res;
05539 }
05540 
05541 static void run_externnotify(char *context, char *extension, const char *flag)
05542 {
05543    char arguments[255];
05544    char ext_context[256] = "";
05545    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05546    struct ast_smdi_mwi_message *mwi_msg;
05547 
05548    if (!ast_strlen_zero(context))
05549       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05550    else
05551       ast_copy_string(ext_context, extension, sizeof(ext_context));
05552 
05553    if (smdi_iface) {
05554       if (ast_app_has_voicemail(ext_context, NULL)) 
05555          ast_smdi_mwi_set(smdi_iface, extension);
05556       else
05557          ast_smdi_mwi_unset(smdi_iface, extension);
05558 
05559       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05560          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05561          if (!strncmp(mwi_msg->cause, "INV", 3))
05562             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05563          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05564             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05565          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05566          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05567       } else {
05568          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05569       }
05570    }
05571 
05572    if (!ast_strlen_zero(externnotify)) {
05573       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05574          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05575       } else {
05576          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
05577             externnotify, S_OR(context, "\"\""),
05578             extension, newvoicemails,
05579             oldvoicemails, urgentvoicemails);
05580          ast_debug(1, "Executing %s\n", arguments);
05581          ast_safe_system(arguments);
05582       }
05583    }
05584 }
05585 
05586 /*!
05587  * \brief Variables used for saving a voicemail.
05588  *
05589  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05590  */
05591 struct leave_vm_options {
05592    unsigned int flags;
05593    signed char record_gain;
05594    char *exitcontext;
05595 };
05596 
05597 /*!
05598  * \brief Prompts the user and records a voicemail to a mailbox.
05599  * \param chan
05600  * \param ext
05601  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05602  * 
05603  * 
05604  * 
05605  * \return zero on success, -1 on error.
05606  */
05607 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05608 {
05609 #ifdef IMAP_STORAGE
05610    int newmsgs, oldmsgs;
05611 #else
05612    char urgdir[PATH_MAX];
05613 #endif
05614    char txtfile[PATH_MAX];
05615    char tmptxtfile[PATH_MAX];
05616    struct vm_state *vms = NULL;
05617    char callerid[256];
05618    FILE *txt;
05619    char date[256];
05620    int txtdes;
05621    int res = 0;
05622    int msgnum;
05623    int duration = 0;
05624    int sound_duration = 0;
05625    int ausemacro = 0;
05626    int ousemacro = 0;
05627    int ouseexten = 0;
05628    char tmpdur[16];
05629    char priority[16];
05630    char origtime[16];
05631    char dir[PATH_MAX];
05632    char tmpdir[PATH_MAX];
05633    char fn[PATH_MAX];
05634    char prefile[PATH_MAX] = "";
05635    char tempfile[PATH_MAX] = "";
05636    char ext_context[256] = "";
05637    char fmt[80];
05638    char *context;
05639    char ecodes[17] = "#";
05640    struct ast_str *tmp = ast_str_create(16);
05641    char *tmpptr;
05642    struct ast_vm_user *vmu;
05643    struct ast_vm_user svm;
05644    const char *category = NULL;
05645    const char *code;
05646    const char *alldtmf = "0123456789ABCD*#";
05647    char flag[80];
05648 
05649    if (!tmp) {
05650       return -1;
05651    }
05652 
05653    ast_str_set(&tmp, 0, "%s", ext);
05654    ext = ast_str_buffer(tmp);
05655    if ((context = strchr(ext, '@'))) {
05656       *context++ = '\0';
05657       tmpptr = strchr(context, '&');
05658    } else {
05659       tmpptr = strchr(ext, '&');
05660    }
05661 
05662    if (tmpptr)
05663       *tmpptr++ = '\0';
05664 
05665    ast_channel_lock(chan);
05666    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05667       category = ast_strdupa(category);
05668    }
05669    ast_channel_unlock(chan);
05670 
05671    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05672       ast_copy_string(flag, "Urgent", sizeof(flag));
05673    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05674       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05675    } else {
05676       flag[0] = '\0';
05677    }
05678 
05679    ast_debug(3, "Before find_user\n");
05680    if (!(vmu = find_user(&svm, context, ext))) {
05681       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05682       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05683       ast_free(tmp);
05684       return res;
05685    }
05686    /* Setup pre-file if appropriate */
05687    if (strcmp(vmu->context, "default"))
05688       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05689    else
05690       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05691 
05692    /* Set the path to the prefile. Will be one of 
05693       VM_SPOOL_DIRcontext/ext/busy
05694       VM_SPOOL_DIRcontext/ext/unavail
05695       Depending on the flag set in options.
05696    */
05697    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05698       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05699    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05700       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05701    }
05702    /* Set the path to the tmpfile as
05703       VM_SPOOL_DIR/context/ext/temp
05704       and attempt to create the folder structure.
05705    */
05706    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05707    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05708       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05709       ast_free(tmp);
05710       return -1;
05711    }
05712    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05713    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05714       ast_copy_string(prefile, tempfile, sizeof(prefile));
05715 
05716    DISPOSE(tempfile, -1);
05717    /* It's easier just to try to make it than to check for its existence */
05718 #ifndef IMAP_STORAGE
05719    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05720 #else
05721    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05722    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05723       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05724    }
05725 #endif
05726 
05727    /* Check current or macro-calling context for special extensions */
05728    if (ast_test_flag(vmu, VM_OPERATOR)) {
05729       if (!ast_strlen_zero(vmu->exit)) {
05730          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05731             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05732             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05733             ouseexten = 1;
05734          }
05735       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05736          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05737          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05738          ouseexten = 1;
05739       } else if (!ast_strlen_zero(chan->macrocontext)
05740          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05741             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05742          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05743          ousemacro = 1;
05744       }
05745    }
05746 
05747    if (!ast_strlen_zero(vmu->exit)) {
05748       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05749          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05750          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05751       }
05752    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05753       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05754       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05755    } else if (!ast_strlen_zero(chan->macrocontext)
05756       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05757          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05758       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05759       ausemacro = 1;
05760    }
05761 
05762    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05763       for (code = alldtmf; *code; code++) {
05764          char e[2] = "";
05765          e[0] = *code;
05766          if (strchr(ecodes, e[0]) == NULL
05767             && ast_canmatch_extension(chan,
05768                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05769                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05770             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05771          }
05772       }
05773    }
05774 
05775    /* Play the beginning intro if desired */
05776    if (!ast_strlen_zero(prefile)) {
05777 #ifdef ODBC_STORAGE
05778       int success = 
05779 #endif
05780          RETRIEVE(prefile, -1, ext, context);
05781       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05782          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05783             res = ast_waitstream(chan, ecodes);
05784 #ifdef ODBC_STORAGE
05785          if (success == -1) {
05786             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05787             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05788             store_file(prefile, vmu->mailbox, vmu->context, -1);
05789          }
05790 #endif
05791       } else {
05792          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05793          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05794       }
05795       DISPOSE(prefile, -1);
05796       if (res < 0) {
05797          ast_debug(1, "Hang up during prefile playback\n");
05798          free_user(vmu);
05799          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05800          ast_free(tmp);
05801          return -1;
05802       }
05803    }
05804    if (res == '#') {
05805       /* On a '#' we skip the instructions */
05806       ast_set_flag(options, OPT_SILENT);
05807       res = 0;
05808    }
05809    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05810    if (vmu->maxmsg == 0) {
05811       if (option_debug > 2)
05812          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05813       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05814       goto leave_vm_out;
05815    }
05816    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05817       res = ast_stream_and_wait(chan, INTRO, ecodes);
05818       if (res == '#') {
05819          ast_set_flag(options, OPT_SILENT);
05820          res = 0;
05821       }
05822    }
05823    if (res > 0)
05824       ast_stopstream(chan);
05825    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05826     other than the operator -- an automated attendant or mailbox login for example */
05827    if (res == '*') {
05828       chan->exten[0] = 'a';
05829       chan->exten[1] = '\0';
05830       if (!ast_strlen_zero(vmu->exit)) {
05831          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05832       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05833          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05834       }
05835       chan->priority = 0;
05836       free_user(vmu);
05837       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05838       ast_free(tmp);
05839       return 0;
05840    }
05841 
05842    /* Check for a '0' here */
05843    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05844    transfer:
05845       if (ouseexten || ousemacro) {
05846          chan->exten[0] = 'o';
05847          chan->exten[1] = '\0';
05848          if (!ast_strlen_zero(vmu->exit)) {
05849             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05850          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05851             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05852          }
05853          ast_play_and_wait(chan, "transfer");
05854          chan->priority = 0;
05855          free_user(vmu);
05856          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05857       }
05858       ast_free(tmp);
05859       return OPERATOR_EXIT;
05860    }
05861 
05862    /* Allow all other digits to exit Voicemail and return to the dialplan */
05863    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05864       if (!ast_strlen_zero(options->exitcontext)) {
05865          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05866       }
05867       free_user(vmu);
05868       ast_free(tmp);
05869       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05870       return res;
05871    }
05872 
05873    if (res < 0) {
05874       free_user(vmu);
05875       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05876       ast_free(tmp);
05877       return -1;
05878    }
05879    /* The meat of recording the message...  All the announcements and beeps have been played*/
05880    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05881    if (!ast_strlen_zero(fmt)) {
05882       msgnum = 0;
05883 
05884 #ifdef IMAP_STORAGE
05885       /* Is ext a mailbox? */
05886       /* must open stream for this user to get info! */
05887       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05888       if (res < 0) {
05889          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05890          ast_free(tmp);
05891          return -1;
05892       }
05893       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05894       /* It is possible under certain circumstances that inboxcount did not
05895        * create a vm_state when it was needed. This is a catchall which will
05896        * rarely be used.
05897        */
05898          if (!(vms = create_vm_state_from_user(vmu))) {
05899             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05900             ast_free(tmp);
05901             return -1;
05902          }
05903       }
05904       vms->newmessages++;
05905       
05906       /* here is a big difference! We add one to it later */
05907       msgnum = newmsgs + oldmsgs;
05908       ast_debug(3, "Messagecount set to %d\n", msgnum);
05909       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05910       /* set variable for compatibility */
05911       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05912 
05913       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05914          goto leave_vm_out;
05915       }
05916 #else
05917       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05918          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05919          if (!res)
05920             res = ast_waitstream(chan, "");
05921          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05922          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05923          inprocess_count(vmu->mailbox, vmu->context, -1);
05924          goto leave_vm_out;
05925       }
05926 
05927 #endif
05928       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05929       txtdes = mkstemp(tmptxtfile);
05930       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05931       if (txtdes < 0) {
05932          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05933          if (!res)
05934             res = ast_waitstream(chan, "");
05935          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05936          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05937          inprocess_count(vmu->mailbox, vmu->context, -1);
05938          goto leave_vm_out;
05939       }
05940 
05941       /* Now play the beep once we have the message number for our next message. */
05942       if (res >= 0) {
05943          /* Unless we're *really* silent, try to send the beep */
05944          res = ast_stream_and_wait(chan, "beep", "");
05945       }
05946             
05947       /* Store information in real-time storage */
05948       if (ast_check_realtime("voicemail_data")) {
05949          snprintf(priority, sizeof(priority), "%d", chan->priority);
05950          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05951          get_date(date, sizeof(date));
05952          ast_callerid_merge(callerid, sizeof(callerid),
05953             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05954             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05955             "Unknown");
05956          ast_store_realtime("voicemail_data",
05957             "origmailbox", ext,
05958             "context", chan->context,
05959             "macrocontext", chan->macrocontext,
05960             "exten", chan->exten,
05961             "priority", priority,
05962             "callerchan", chan->name,
05963             "callerid", callerid,
05964             "origdate", date,
05965             "origtime", origtime,
05966             "category", S_OR(category, ""),
05967             "filename", tmptxtfile,
05968             SENTINEL);
05969       }
05970 
05971       /* Store information */
05972       txt = fdopen(txtdes, "w+");
05973       if (txt) {
05974          get_date(date, sizeof(date));
05975          ast_callerid_merge(callerid, sizeof(callerid),
05976             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05977             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05978             "Unknown");
05979          fprintf(txt, 
05980             ";\n"
05981             "; Message Information file\n"
05982             ";\n"
05983             "[message]\n"
05984             "origmailbox=%s\n"
05985             "context=%s\n"
05986             "macrocontext=%s\n"
05987             "exten=%s\n"
05988             "rdnis=%s\n"
05989             "priority=%d\n"
05990             "callerchan=%s\n"
05991             "callerid=%s\n"
05992             "origdate=%s\n"
05993             "origtime=%ld\n"
05994             "category=%s\n",
05995             ext,
05996             chan->context,
05997             chan->macrocontext, 
05998             chan->exten,
05999             S_COR(chan->redirecting.from.number.valid,
06000                chan->redirecting.from.number.str, "unknown"),
06001             chan->priority,
06002             chan->name,
06003             callerid,
06004             date, (long) time(NULL),
06005             category ? category : "");
06006       } else {
06007          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06008          inprocess_count(vmu->mailbox, vmu->context, -1);
06009          if (ast_check_realtime("voicemail_data")) {
06010             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06011          }
06012          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
06013          goto leave_vm_out;
06014       }
06015       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06016 
06017       if (txt) {
06018          fprintf(txt, "flag=%s\n", flag);
06019          if (sound_duration < vmu->minsecs) {
06020             fclose(txt);
06021             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06022             ast_filedelete(tmptxtfile, NULL);
06023             unlink(tmptxtfile);
06024             if (ast_check_realtime("voicemail_data")) {
06025                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06026             }
06027             inprocess_count(vmu->mailbox, vmu->context, -1);
06028          } else {
06029             fprintf(txt, "duration=%d\n", duration);
06030             fclose(txt);
06031             if (vm_lock_path(dir)) {
06032                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06033                /* Delete files */
06034                ast_filedelete(tmptxtfile, NULL);
06035                unlink(tmptxtfile);
06036                inprocess_count(vmu->mailbox, vmu->context, -1);
06037             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06038                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06039                unlink(tmptxtfile);
06040                ast_unlock_path(dir);
06041                inprocess_count(vmu->mailbox, vmu->context, -1);
06042                if (ast_check_realtime("voicemail_data")) {
06043                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06044                }
06045             } else {
06046 #ifndef IMAP_STORAGE
06047                msgnum = last_message_index(vmu, dir) + 1;
06048 #endif
06049                make_file(fn, sizeof(fn), dir, msgnum);
06050 
06051                /* assign a variable with the name of the voicemail file */ 
06052 #ifndef IMAP_STORAGE
06053                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06054 #else
06055                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06056 #endif
06057 
06058                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06059                ast_filerename(tmptxtfile, fn, NULL);
06060                rename(tmptxtfile, txtfile);
06061                inprocess_count(vmu->mailbox, vmu->context, -1);
06062 
06063                /* Properly set permissions on voicemail text descriptor file.
06064                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06065                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06066                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06067 
06068                ast_unlock_path(dir);
06069                if (ast_check_realtime("voicemail_data")) {
06070                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06071                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06072                }
06073                /* We must store the file first, before copying the message, because
06074                 * ODBC storage does the entire copy with SQL.
06075                 */
06076                if (ast_fileexists(fn, NULL, NULL) > 0) {
06077                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06078                }
06079 
06080                /* Are there to be more recipients of this message? */
06081                while (tmpptr) {
06082                   struct ast_vm_user recipu, *recip;
06083                   char *exten, *cntx;
06084 
06085                   exten = strsep(&tmpptr, "&");
06086                   cntx = strchr(exten, '@');
06087                   if (cntx) {
06088                      *cntx = '\0';
06089                      cntx++;
06090                   }
06091                   if ((recip = find_user(&recipu, cntx, exten))) {
06092                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06093                      free_user(recip);
06094                   }
06095                }
06096 #ifndef IMAP_STORAGE
06097                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06098                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06099                   char sfn[PATH_MAX];
06100                   char dfn[PATH_MAX];
06101                   int x;
06102                   /* It's easier just to try to make it than to check for its existence */
06103                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06104                   x = last_message_index(vmu, urgdir) + 1;
06105                   make_file(sfn, sizeof(sfn), dir, msgnum);
06106                   make_file(dfn, sizeof(dfn), urgdir, x);
06107                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06108                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06109                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06110                   ast_copy_string(fn, dfn, sizeof(fn));
06111                   msgnum = x;
06112                }
06113 #endif
06114                /* Notification needs to happen after the copy, though. */
06115                if (ast_fileexists(fn, NULL, NULL)) {
06116 #ifdef IMAP_STORAGE
06117                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06118                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06119                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06120                      flag);
06121 #else
06122                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06123                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06124                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06125                      flag);
06126 #endif
06127                }
06128 
06129                /* Disposal needs to happen after the optional move and copy */
06130                if (ast_fileexists(fn, NULL, NULL)) {
06131                   DISPOSE(dir, msgnum);
06132                }
06133             }
06134          }
06135       } else {
06136          inprocess_count(vmu->mailbox, vmu->context, -1);
06137       }
06138       if (res == '0') {
06139          goto transfer;
06140       } else if (res > 0 && res != 't')
06141          res = 0;
06142 
06143       if (sound_duration < vmu->minsecs)
06144          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06145          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06146       else
06147          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06148    } else
06149       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06150 leave_vm_out:
06151    free_user(vmu);
06152 
06153 #ifdef IMAP_STORAGE
06154    /* expunge message - use UID Expunge if supported on IMAP server*/
06155    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06156    if (expungeonhangup == 1) {
06157       ast_mutex_lock(&vms->lock);
06158 #ifdef HAVE_IMAP_TK2006
06159       if (LEVELUIDPLUS (vms->mailstream)) {
06160          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06161       } else 
06162 #endif
06163          mail_expunge(vms->mailstream);
06164       ast_mutex_unlock(&vms->lock);
06165    }
06166 #endif
06167 
06168    ast_free(tmp);
06169    return res;
06170 }
06171 
06172 #if !defined(IMAP_STORAGE)
06173 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06174 {
06175    /* we know the actual number of messages, so stop process when number is hit */
06176 
06177    int x, dest;
06178    char sfn[PATH_MAX];
06179    char dfn[PATH_MAX];
06180 
06181    if (vm_lock_path(dir)) {
06182       return ERROR_LOCK_PATH;
06183    }
06184 
06185    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06186       make_file(sfn, sizeof(sfn), dir, x);
06187       if (EXISTS(dir, x, sfn, NULL)) {
06188 
06189          if (x != dest) {
06190             make_file(dfn, sizeof(dfn), dir, dest);
06191             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06192          }
06193 
06194          dest++;
06195       }
06196    }
06197    ast_unlock_path(dir);
06198 
06199    return dest;
06200 }
06201 #endif
06202 
06203 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06204 {
06205    int d;
06206    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06207    return d;
06208 }
06209 
06210 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06211 {
06212 #ifdef IMAP_STORAGE
06213    /* we must use mbox(x) folder names, and copy the message there */
06214    /* simple. huh? */
06215    char sequence[10];
06216    char mailbox[256];
06217    int res;
06218 
06219    /* get the real IMAP message number for this message */
06220    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06221    
06222    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06223    ast_mutex_lock(&vms->lock);
06224    /* if save to Old folder, put in INBOX as read */
06225    if (box == OLD_FOLDER) {
06226       mail_setflag(vms->mailstream, sequence, "\\Seen");
06227       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06228    } else if (box == NEW_FOLDER) {
06229       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06230       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06231    }
06232    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06233       ast_mutex_unlock(&vms->lock);
06234       return 0;
06235    }
06236    /* Create the folder if it don't exist */
06237    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06238    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06239    if (mail_create(vms->mailstream, mailbox) == NIL) 
06240       ast_debug(5, "Folder exists.\n");
06241    else
06242       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06243    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06244    ast_mutex_unlock(&vms->lock);
06245    return res;
06246 #else
06247    char *dir = vms->curdir;
06248    char *username = vms->username;
06249    char *context = vmu->context;
06250    char sfn[PATH_MAX];
06251    char dfn[PATH_MAX];
06252    char ddir[PATH_MAX];
06253    const char *dbox = mbox(vmu, box);
06254    int x, i;
06255    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06256 
06257    if (vm_lock_path(ddir))
06258       return ERROR_LOCK_PATH;
06259 
06260    x = last_message_index(vmu, ddir) + 1;
06261 
06262    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06263       x--;
06264       for (i = 1; i <= x; i++) {
06265          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06266          make_file(sfn, sizeof(sfn), ddir, i);
06267          make_file(dfn, sizeof(dfn), ddir, i - 1);
06268          if (EXISTS(ddir, i, sfn, NULL)) {
06269             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06270          } else
06271             break;
06272       }
06273    } else {
06274       if (x >= vmu->maxmsg) {
06275          ast_unlock_path(ddir);
06276          return -1;
06277       }
06278    }
06279    make_file(sfn, sizeof(sfn), dir, msg);
06280    make_file(dfn, sizeof(dfn), ddir, x);
06281    if (strcmp(sfn, dfn)) {
06282       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06283    }
06284    ast_unlock_path(ddir);
06285 #endif
06286    return 0;
06287 }
06288 
06289 static int adsi_logo(unsigned char *buf)
06290 {
06291    int bytes = 0;
06292    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06293    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06294    return bytes;
06295 }
06296 
06297 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06298 {
06299    unsigned char buf[256];
06300    int bytes = 0;
06301    int x;
06302    char num[5];
06303 
06304    *useadsi = 0;
06305    bytes += ast_adsi_data_mode(buf + bytes);
06306    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06307 
06308    bytes = 0;
06309    bytes += adsi_logo(buf);
06310    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06311 #ifdef DISPLAY
06312    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06313 #endif
06314    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06315    bytes += ast_adsi_data_mode(buf + bytes);
06316    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06317 
06318    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06319       bytes = 0;
06320       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06321       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06322       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06323       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06324       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06325       return 0;
06326    }
06327 
06328 #ifdef DISPLAY
06329    /* Add a dot */
06330    bytes = 0;
06331    bytes += ast_adsi_logo(buf);
06332    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06333    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06334    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06335    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06336 #endif
06337    bytes = 0;
06338    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06339    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06340    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06341    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06342    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06343    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06344    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06345 
06346 #ifdef DISPLAY
06347    /* Add another dot */
06348    bytes = 0;
06349    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06350    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06351 
06352    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06353    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06354 #endif
06355 
06356    bytes = 0;
06357    /* These buttons we load but don't use yet */
06358    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06359    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06360    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06361    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06362    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06363    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06364    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06365 
06366 #ifdef DISPLAY
06367    /* Add another dot */
06368    bytes = 0;
06369    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06370    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06371    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06372 #endif
06373 
06374    bytes = 0;
06375    for (x = 0; x < 5; x++) {
06376       snprintf(num, sizeof(num), "%d", x);
06377       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06378    }
06379    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06380    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06381 
06382 #ifdef DISPLAY
06383    /* Add another dot */
06384    bytes = 0;
06385    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06386    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06387    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06388 #endif
06389 
06390    if (ast_adsi_end_download(chan)) {
06391       bytes = 0;
06392       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06393       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06394       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06395       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06396       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06397       return 0;
06398    }
06399    bytes = 0;
06400    bytes += ast_adsi_download_disconnect(buf + bytes);
06401    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06402    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06403 
06404    ast_debug(1, "Done downloading scripts...\n");
06405 
06406 #ifdef DISPLAY
06407    /* Add last dot */
06408    bytes = 0;
06409    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06410    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06411 #endif
06412    ast_debug(1, "Restarting session...\n");
06413 
06414    bytes = 0;
06415    /* Load the session now */
06416    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06417       *useadsi = 1;
06418       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06419    } else
06420       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06421 
06422    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06423    return 0;
06424 }
06425 
06426 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06427 {
06428    int x;
06429    if (!ast_adsi_available(chan))
06430       return;
06431    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06432    if (x < 0)
06433       return;
06434    if (!x) {
06435       if (adsi_load_vmail(chan, useadsi)) {
06436          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06437          return;
06438       }
06439    } else
06440       *useadsi = 1;
06441 }
06442 
06443 static void adsi_login(struct ast_channel *chan)
06444 {
06445    unsigned char buf[256];
06446    int bytes = 0;
06447    unsigned char keys[8];
06448    int x;
06449    if (!ast_adsi_available(chan))
06450       return;
06451 
06452    for (x = 0; x < 8; x++)
06453       keys[x] = 0;
06454    /* Set one key for next */
06455    keys[3] = ADSI_KEY_APPS + 3;
06456 
06457    bytes += adsi_logo(buf + bytes);
06458    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06459    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06460    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06461    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06462    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06463    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06464    bytes += ast_adsi_set_keys(buf + bytes, keys);
06465    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06466    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06467 }
06468 
06469 static void adsi_password(struct ast_channel *chan)
06470 {
06471    unsigned char buf[256];
06472    int bytes = 0;
06473    unsigned char keys[8];
06474    int x;
06475    if (!ast_adsi_available(chan))
06476       return;
06477 
06478    for (x = 0; x < 8; x++)
06479       keys[x] = 0;
06480    /* Set one key for next */
06481    keys[3] = ADSI_KEY_APPS + 3;
06482 
06483    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06484    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06485    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06486    bytes += ast_adsi_set_keys(buf + bytes, keys);
06487    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06488    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06489 }
06490 
06491 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06492 {
06493    unsigned char buf[256];
06494    int bytes = 0;
06495    unsigned char keys[8];
06496    int x, y;
06497 
06498    if (!ast_adsi_available(chan))
06499       return;
06500 
06501    for (x = 0; x < 5; x++) {
06502       y = ADSI_KEY_APPS + 12 + start + x;
06503       if (y > ADSI_KEY_APPS + 12 + 4)
06504          y = 0;
06505       keys[x] = ADSI_KEY_SKT | y;
06506    }
06507    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06508    keys[6] = 0;
06509    keys[7] = 0;
06510 
06511    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06512    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06513    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06514    bytes += ast_adsi_set_keys(buf + bytes, keys);
06515    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06516 
06517    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06518 }
06519 
06520 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06521 {
06522    int bytes = 0;
06523    unsigned char buf[256]; 
06524    char buf1[256], buf2[256];
06525    char fn2[PATH_MAX];
06526 
06527    char cid[256] = "";
06528    char *val;
06529    char *name, *num;
06530    char datetime[21] = "";
06531    FILE *f;
06532 
06533    unsigned char keys[8];
06534 
06535    int x;
06536 
06537    if (!ast_adsi_available(chan))
06538       return;
06539 
06540    /* Retrieve important info */
06541    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06542    f = fopen(fn2, "r");
06543    if (f) {
06544       while (!feof(f)) {   
06545          if (!fgets((char *) buf, sizeof(buf), f)) {
06546             continue;
06547          }
06548          if (!feof(f)) {
06549             char *stringp = NULL;
06550             stringp = (char *) buf;
06551             strsep(&stringp, "=");
06552             val = strsep(&stringp, "=");
06553             if (!ast_strlen_zero(val)) {
06554                if (!strcmp((char *) buf, "callerid"))
06555                   ast_copy_string(cid, val, sizeof(cid));
06556                if (!strcmp((char *) buf, "origdate"))
06557                   ast_copy_string(datetime, val, sizeof(datetime));
06558             }
06559          }
06560       }
06561       fclose(f);
06562    }
06563    /* New meaning for keys */
06564    for (x = 0; x < 5; x++)
06565       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06566    keys[6] = 0x0;
06567    keys[7] = 0x0;
06568 
06569    if (!vms->curmsg) {
06570       /* No prev key, provide "Folder" instead */
06571       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06572    }
06573    if (vms->curmsg >= vms->lastmsg) {
06574       /* If last message ... */
06575       if (vms->curmsg) {
06576          /* but not only message, provide "Folder" instead */
06577          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06578          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06579 
06580       } else {
06581          /* Otherwise if only message, leave blank */
06582          keys[3] = 1;
06583       }
06584    }
06585 
06586    if (!ast_strlen_zero(cid)) {
06587       ast_callerid_parse(cid, &name, &num);
06588       if (!name)
06589          name = num;
06590    } else
06591       name = "Unknown Caller";
06592 
06593    /* If deleted, show "undeleted" */
06594 #ifdef IMAP_STORAGE
06595    ast_mutex_lock(&vms->lock);
06596 #endif
06597    if (vms->deleted[vms->curmsg]) {
06598       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06599    }
06600 #ifdef IMAP_STORAGE
06601    ast_mutex_unlock(&vms->lock);
06602 #endif
06603 
06604    /* Except "Exit" */
06605    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06606    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06607       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06608    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06609 
06610    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06611    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06612    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06613    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06614    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06615    bytes += ast_adsi_set_keys(buf + bytes, keys);
06616    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06617 
06618    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06619 }
06620 
06621 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06622 {
06623    int bytes = 0;
06624    unsigned char buf[256];
06625    unsigned char keys[8];
06626 
06627    int x;
06628 
06629    if (!ast_adsi_available(chan))
06630       return;
06631 
06632    /* New meaning for keys */
06633    for (x = 0; x < 5; x++)
06634       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06635 
06636    keys[6] = 0x0;
06637    keys[7] = 0x0;
06638 
06639    if (!vms->curmsg) {
06640       /* No prev key, provide "Folder" instead */
06641       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06642    }
06643    if (vms->curmsg >= vms->lastmsg) {
06644       /* If last message ... */
06645       if (vms->curmsg) {
06646          /* but not only message, provide "Folder" instead */
06647          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06648       } else {
06649          /* Otherwise if only message, leave blank */
06650          keys[3] = 1;
06651       }
06652    }
06653 
06654    /* If deleted, show "undeleted" */
06655 #ifdef IMAP_STORAGE
06656    ast_mutex_lock(&vms->lock);
06657 #endif
06658    if (vms->deleted[vms->curmsg]) {
06659       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06660    }
06661 #ifdef IMAP_STORAGE
06662    ast_mutex_unlock(&vms->lock);
06663 #endif
06664 
06665    /* Except "Exit" */
06666    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06667    bytes += ast_adsi_set_keys(buf + bytes, keys);
06668    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06669 
06670    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06671 }
06672 
06673 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06674 {
06675    unsigned char buf[256] = "";
06676    char buf1[256] = "", buf2[256] = "";
06677    int bytes = 0;
06678    unsigned char keys[8];
06679    int x;
06680 
06681    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06682    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06683    if (!ast_adsi_available(chan))
06684       return;
06685    if (vms->newmessages) {
06686       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06687       if (vms->oldmessages) {
06688          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06689          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06690       } else {
06691          snprintf(buf2, sizeof(buf2), "%s.", newm);
06692       }
06693    } else if (vms->oldmessages) {
06694       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06695       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06696    } else {
06697       strcpy(buf1, "You have no messages.");
06698       buf2[0] = ' ';
06699       buf2[1] = '\0';
06700    }
06701    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06702    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06703    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06704 
06705    for (x = 0; x < 6; x++)
06706       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06707    keys[6] = 0;
06708    keys[7] = 0;
06709 
06710    /* Don't let them listen if there are none */
06711    if (vms->lastmsg < 0)
06712       keys[0] = 1;
06713    bytes += ast_adsi_set_keys(buf + bytes, keys);
06714 
06715    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06716 
06717    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06718 }
06719 
06720 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06721 {
06722    unsigned char buf[256] = "";
06723    char buf1[256] = "", buf2[256] = "";
06724    int bytes = 0;
06725    unsigned char keys[8];
06726    int x;
06727 
06728    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06729 
06730    if (!ast_adsi_available(chan))
06731       return;
06732 
06733    /* Original command keys */
06734    for (x = 0; x < 6; x++)
06735       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06736 
06737    keys[6] = 0;
06738    keys[7] = 0;
06739 
06740    if ((vms->lastmsg + 1) < 1)
06741       keys[0] = 0;
06742 
06743    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06744       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06745 
06746    if (vms->lastmsg + 1)
06747       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06748    else
06749       strcpy(buf2, "no messages.");
06750    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06751    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06752    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06753    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06754    bytes += ast_adsi_set_keys(buf + bytes, keys);
06755 
06756    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06757 
06758    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06759    
06760 }
06761 
06762 /*
06763 static void adsi_clear(struct ast_channel *chan)
06764 {
06765    char buf[256];
06766    int bytes=0;
06767    if (!ast_adsi_available(chan))
06768       return;
06769    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06770    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06771 
06772    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06773 }
06774 */
06775 
06776 static void adsi_goodbye(struct ast_channel *chan)
06777 {
06778    unsigned char buf[256];
06779    int bytes = 0;
06780 
06781    if (!ast_adsi_available(chan))
06782       return;
06783    bytes += adsi_logo(buf + bytes);
06784    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06785    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06786    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06787    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06788 
06789    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06790 }
06791 
06792 /*!\brief get_folder: Folder menu
06793  * Plays "press 1 for INBOX messages" etc.
06794  * Should possibly be internationalized
06795  */
06796 static int get_folder(struct ast_channel *chan, int start)
06797 {
06798    int x;
06799    int d;
06800    char fn[PATH_MAX];
06801    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06802    if (d)
06803       return d;
06804    for (x = start; x < 5; x++) { /* For all folders */
06805       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06806          return d;
06807       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06808       if (d)
06809          return d;
06810       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06811 
06812       /* The inbox folder can have its name changed under certain conditions
06813        * so this checks if the sound file exists for the inbox folder name and
06814        * if it doesn't, plays the default name instead. */
06815       if (x == 0) {
06816          if (ast_fileexists(fn, NULL, NULL)) {
06817             d = vm_play_folder_name(chan, fn);
06818          } else {
06819             ast_verb(1, "failed to find %s\n", fn);
06820             d = vm_play_folder_name(chan, "vm-INBOX");
06821          }
06822       } else {
06823          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06824          d = vm_play_folder_name(chan, fn);
06825       }
06826 
06827       if (d)
06828          return d;
06829       d = ast_waitfordigit(chan, 500);
06830       if (d)
06831          return d;
06832    }
06833 
06834    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06835    if (d)
06836       return d;
06837    d = ast_waitfordigit(chan, 4000);
06838    return d;
06839 }
06840 
06841 /*!
06842  * \brief plays a prompt and waits for a keypress.
06843  * \param chan
06844  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06845  * \param start Does not appear to be used at this time.
06846  *
06847  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06848  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06849  * prompting for the number inputs that correspond to the available folders.
06850  * 
06851  * \return zero on success, or -1 on error.
06852  */
06853 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06854 {
06855    int res = 0;
06856    int loops = 0;
06857 
06858    res = ast_play_and_wait(chan, fn);  /* Folder name */
06859    while (((res < '0') || (res > '9')) &&
06860          (res != '#') && (res >= 0) &&
06861          loops < 4) {
06862       res = get_folder(chan, 0);
06863       loops++;
06864    }
06865    if (loops == 4) { /* give up */
06866       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06867       return '#';
06868    }
06869    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06870    return res;
06871 }
06872 
06873 /*!
06874  * \brief presents the option to prepend to an existing message when forwarding it.
06875  * \param chan
06876  * \param vmu
06877  * \param curdir
06878  * \param curmsg
06879  * \param vm_fmts
06880  * \param context
06881  * \param record_gain
06882  * \param duration
06883  * \param vms
06884  * \param flag 
06885  *
06886  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06887  *
06888  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06889  * \return zero on success, -1 on error.
06890  */
06891 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06892          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06893 {
06894    int cmd = 0;
06895    int retries = 0, prepend_duration = 0, already_recorded = 0;
06896    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06897    char textfile[PATH_MAX];
06898    struct ast_config *msg_cfg;
06899    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06900 #ifndef IMAP_STORAGE
06901    signed char zero_gain = 0;
06902 #endif
06903    const char *duration_str;
06904 
06905    /* Must always populate duration correctly */
06906    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06907    strcpy(textfile, msgfile);
06908    strcpy(backup, msgfile);
06909    strcpy(backup_textfile, msgfile);
06910    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06911    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06912    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06913 
06914    if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06915       *duration = atoi(duration_str);
06916    } else {
06917       *duration = 0;
06918    }
06919 
06920    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06921       if (cmd)
06922          retries = 0;
06923       switch (cmd) {
06924       case '1': 
06925 
06926 #ifdef IMAP_STORAGE
06927          /* Record new intro file */
06928          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06929          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06930          ast_play_and_wait(chan, INTRO);
06931          ast_play_and_wait(chan, "beep");
06932          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06933          if (cmd == -1) {
06934             break;
06935          }
06936          cmd = 't';
06937 #else
06938 
06939          /* prepend a message to the current message, update the metadata and return */
06940 
06941          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06942          strcpy(textfile, msgfile);
06943          strncat(textfile, ".txt", sizeof(textfile) - 1);
06944          *duration = 0;
06945 
06946          /* if we can't read the message metadata, stop now */
06947          if (!valid_config(msg_cfg)) {
06948             cmd = 0;
06949             break;
06950          }
06951 
06952          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06953 #ifndef IMAP_STORAGE
06954          if (already_recorded) {
06955             ast_filecopy(backup, msgfile, NULL);
06956             copy(backup_textfile, textfile);
06957          }
06958          else {
06959             ast_filecopy(msgfile, backup, NULL);
06960             copy(textfile, backup_textfile);
06961          }
06962 #endif
06963          already_recorded = 1;
06964 
06965          if (record_gain)
06966             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06967 
06968          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06969 
06970          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06971             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06972             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06973             ast_filerename(backup, msgfile, NULL);
06974          }
06975 
06976          if (record_gain)
06977             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06978 
06979          
06980          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06981             *duration = atoi(duration_str);
06982 
06983          if (prepend_duration) {
06984             struct ast_category *msg_cat;
06985             /* need enough space for a maximum-length message duration */
06986             char duration_buf[12];
06987 
06988             *duration += prepend_duration;
06989             msg_cat = ast_category_get(msg_cfg, "message");
06990             snprintf(duration_buf, 11, "%ld", *duration);
06991             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06992                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06993             }
06994          }
06995 
06996 #endif
06997          break;
06998       case '2': 
06999          /* NULL out introfile so we know there is no intro! */
07000 #ifdef IMAP_STORAGE
07001          *vms->introfn = '\0';
07002 #endif
07003          cmd = 't';
07004          break;
07005       case '*':
07006          cmd = '*';
07007          break;
07008       default: 
07009          /* If time_out and return to menu, reset already_recorded */
07010          already_recorded = 0;
07011 
07012          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07013             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07014          if (!cmd) {
07015             cmd = ast_play_and_wait(chan, "vm-starmain");
07016             /* "press star to return to the main menu" */
07017          }
07018          if (!cmd) {
07019             cmd = ast_waitfordigit(chan, 6000);
07020          }
07021          if (!cmd) {
07022             retries++;
07023          }
07024          if (retries > 3) {
07025             cmd = '*'; /* Let's cancel this beast */
07026          }
07027          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07028       }
07029    }
07030 
07031    if (valid_config(msg_cfg))
07032       ast_config_destroy(msg_cfg);
07033    if (prepend_duration)
07034       *duration = prepend_duration;
07035 
07036    if (already_recorded && cmd == -1) {
07037       /* restore original message if prepention cancelled */
07038       ast_filerename(backup, msgfile, NULL);
07039       rename(backup_textfile, textfile);
07040    }
07041 
07042    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07043       cmd = 0;
07044    return cmd;
07045 }
07046 
07047 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07048 {
07049    struct ast_event *event;
07050    char *mailbox, *context;
07051 
07052    /* Strip off @default */
07053    context = mailbox = ast_strdupa(box);
07054    strsep(&context, "@");
07055    if (ast_strlen_zero(context))
07056       context = "default";
07057 
07058    if (!(event = ast_event_new(AST_EVENT_MWI,
07059          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07060          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07061          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07062          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07063          AST_EVENT_IE_END))) {
07064       return;
07065    }
07066 
07067    ast_event_queue_and_cache(event);
07068 }
07069 
07070 /*!
07071  * \brief Sends email notification that a user has a new voicemail waiting for them.
07072  * \param chan
07073  * \param vmu
07074  * \param vms
07075  * \param msgnum
07076  * \param duration
07077  * \param fmt
07078  * \param cidnum The Caller ID phone number value.
07079  * \param cidname The Caller ID name value.
07080  * \param flag
07081  *
07082  * \return zero on success, -1 on error.
07083  */
07084 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)
07085 {
07086    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07087    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07088    const char *category;
07089    char *myserveremail = serveremail;
07090 
07091    ast_channel_lock(chan);
07092    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07093       category = ast_strdupa(category);
07094    }
07095    ast_channel_unlock(chan);
07096 
07097 #ifndef IMAP_STORAGE
07098    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07099 #else
07100    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07101 #endif
07102    make_file(fn, sizeof(fn), todir, msgnum);
07103    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07104 
07105    if (!ast_strlen_zero(vmu->attachfmt)) {
07106       if (strstr(fmt, vmu->attachfmt))
07107          fmt = vmu->attachfmt;
07108       else
07109          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);
07110    }
07111 
07112    /* Attach only the first format */
07113    fmt = ast_strdupa(fmt);
07114    stringp = fmt;
07115    strsep(&stringp, "|");
07116 
07117    if (!ast_strlen_zero(vmu->serveremail))
07118       myserveremail = vmu->serveremail;
07119 
07120    if (!ast_strlen_zero(vmu->email)) {
07121       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07122 
07123       if (attach_user_voicemail)
07124          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07125 
07126       /* XXX possible imap issue, should category be NULL XXX */
07127       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07128 
07129       if (attach_user_voicemail)
07130          DISPOSE(todir, msgnum);
07131    }
07132 
07133    if (!ast_strlen_zero(vmu->pager)) {
07134       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07135    }
07136 
07137    if (ast_test_flag(vmu, VM_DELETE))
07138       DELETE(todir, msgnum, fn, vmu);
07139 
07140    /* Leave voicemail for someone */
07141    if (ast_app_has_voicemail(ext_context, NULL)) 
07142       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07143 
07144    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07145 
07146    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);
07147    run_externnotify(vmu->context, vmu->mailbox, flag);
07148 
07149 #ifdef IMAP_STORAGE
07150    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07151    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07152       vm_imap_delete(NULL, vms->curmsg, vmu);
07153       vms->newmessages--;  /* Fix new message count */
07154    }
07155 #endif
07156 
07157    return 0;
07158 }
07159 
07160 /*!
07161  * \brief Sends a voicemail message to a mailbox recipient.
07162  * \param chan
07163  * \param context
07164  * \param vms
07165  * \param sender
07166  * \param fmt
07167  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07168  *             Will be 0 when called to forward an existing message (option 8)
07169  *             Will be 1 when called to leave a message (option 3->5)
07170  * \param record_gain 
07171  * \param urgent
07172  *
07173  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07174  * 
07175  * When in the leave message mode (is_new_message == 1):
07176  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07177  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07178  *
07179  * When in the forward message mode (is_new_message == 0):
07180  *   - retreives the current message to be forwarded
07181  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07182  *   - determines the target mailbox and folders
07183  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07184  *
07185  * \return zero on success, -1 on error.
07186  */
07187 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)
07188 {
07189 #ifdef IMAP_STORAGE
07190    int todircount = 0;
07191    struct vm_state *dstvms;
07192 #endif
07193    char username[70]="";
07194    char fn[PATH_MAX]; /* for playback of name greeting */
07195    char ecodes[16] = "#";
07196    int res = 0, cmd = 0;
07197    struct ast_vm_user *receiver = NULL, *vmtmp;
07198    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07199    char *stringp;
07200    const char *s;
07201    int saved_messages = 0;
07202    int valid_extensions = 0;
07203    char *dir;
07204    int curmsg;
07205    char urgent_str[7] = "";
07206    int prompt_played = 0;
07207 #ifndef IMAP_STORAGE
07208    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07209 #endif
07210    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07211       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07212    }
07213 
07214    if (vms == NULL) return -1;
07215    dir = vms->curdir;
07216    curmsg = vms->curmsg;
07217 
07218    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07219    while (!res && !valid_extensions) {
07220       int use_directory = 0;
07221       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07222          int done = 0;
07223          int retries = 0;
07224          cmd = 0;
07225          while ((cmd >= 0) && !done ){
07226             if (cmd)
07227                retries = 0;
07228             switch (cmd) {
07229             case '1': 
07230                use_directory = 0;
07231                done = 1;
07232                break;
07233             case '2': 
07234                use_directory = 1;
07235                done = 1;
07236                break;
07237             case '*': 
07238                cmd = 't';
07239                done = 1;
07240                break;
07241             default: 
07242                /* Press 1 to enter an extension press 2 to use the directory */
07243                cmd = ast_play_and_wait(chan, "vm-forward");
07244                if (!cmd) {
07245                   cmd = ast_waitfordigit(chan, 3000);
07246                }
07247                if (!cmd) {
07248                   retries++;
07249                }
07250                if (retries > 3) {
07251                   cmd = 't';
07252                   done = 1;
07253                }
07254                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07255             }
07256          }
07257          if (cmd < 0 || cmd == 't')
07258             break;
07259       }
07260       
07261       if (use_directory) {
07262          /* use app_directory */
07263          
07264          char old_context[sizeof(chan->context)];
07265          char old_exten[sizeof(chan->exten)];
07266          int old_priority;
07267          struct ast_app* directory_app;
07268 
07269          directory_app = pbx_findapp("Directory");
07270          if (directory_app) {
07271             char vmcontext[256];
07272             /* make backup copies */
07273             memcpy(old_context, chan->context, sizeof(chan->context));
07274             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07275             old_priority = chan->priority;
07276             
07277             /* call the the Directory, changes the channel */
07278             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07279             res = pbx_exec(chan, directory_app, vmcontext);
07280             
07281             ast_copy_string(username, chan->exten, sizeof(username));
07282             
07283             /* restore the old context, exten, and priority */
07284             memcpy(chan->context, old_context, sizeof(chan->context));
07285             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07286             chan->priority = old_priority;
07287          } else {
07288             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07289             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07290          }
07291       } else {
07292          /* Ask for an extension */
07293          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07294          prompt_played++;
07295          if (res || prompt_played > 4)
07296             break;
07297          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07298             break;
07299       }
07300       
07301       /* start all over if no username */
07302       if (ast_strlen_zero(username))
07303          continue;
07304       stringp = username;
07305       s = strsep(&stringp, "*");
07306       /* start optimistic */
07307       valid_extensions = 1;
07308       while (s) {
07309          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07310             int oldmsgs;
07311             int newmsgs;
07312             int capacity;
07313             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07314                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07315                /* Shouldn't happen, but allow trying another extension if it does */
07316                res = ast_play_and_wait(chan, "pbx-invalid");
07317                valid_extensions = 0;
07318                break;
07319             }
07320             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07321             if ((newmsgs + oldmsgs) >= capacity) {
07322                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07323                res = ast_play_and_wait(chan, "vm-mailboxfull");
07324                valid_extensions = 0;
07325                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07326                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07327                   free_user(vmtmp);
07328                }
07329                inprocess_count(receiver->mailbox, receiver->context, -1);
07330                break;
07331             }
07332             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07333          } else {
07334             /* XXX Optimization for the future.  When we encounter a single bad extension,
07335              * bailing out on all of the extensions may not be the way to go.  We should
07336              * probably just bail on that single extension, then allow the user to enter
07337              * several more. XXX
07338              */
07339             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07340                free_user(receiver);
07341             }
07342             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07343             /* "I am sorry, that's not a valid extension.  Please try again." */
07344             res = ast_play_and_wait(chan, "pbx-invalid");
07345             valid_extensions = 0;
07346             break;
07347          }
07348 
07349          /* play name if available, else play extension number */
07350          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07351          RETRIEVE(fn, -1, s, receiver->context);
07352          if (ast_fileexists(fn, NULL, NULL) > 0) {
07353             res = ast_stream_and_wait(chan, fn, ecodes);
07354             if (res) {
07355                DISPOSE(fn, -1);
07356                return res;
07357             }
07358          } else {
07359             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07360          }
07361          DISPOSE(fn, -1);
07362 
07363          s = strsep(&stringp, "*");
07364       }
07365       /* break from the loop of reading the extensions */
07366       if (valid_extensions)
07367          break;
07368    }
07369    /* check if we're clear to proceed */
07370    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07371       return res;
07372    if (is_new_message == 1) {
07373       struct leave_vm_options leave_options;
07374       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07375       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07376 
07377       /* Send VoiceMail */
07378       memset(&leave_options, 0, sizeof(leave_options));
07379       leave_options.record_gain = record_gain;
07380       cmd = leave_voicemail(chan, mailbox, &leave_options);
07381    } else {
07382       /* Forward VoiceMail */
07383       long duration = 0;
07384       struct vm_state vmstmp;
07385       int copy_msg_result = 0;
07386       memcpy(&vmstmp, vms, sizeof(vmstmp));
07387 
07388       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07389 
07390       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07391       if (!cmd) {
07392          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07393 #ifdef IMAP_STORAGE
07394             int attach_user_voicemail;
07395             char *myserveremail = serveremail;
07396             
07397             /* get destination mailbox */
07398             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07399             if (!dstvms) {
07400                dstvms = create_vm_state_from_user(vmtmp);
07401             }
07402             if (dstvms) {
07403                init_mailstream(dstvms, 0);
07404                if (!dstvms->mailstream) {
07405                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07406                } else {
07407                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07408                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07409                }
07410             } else {
07411                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07412             }
07413             if (!ast_strlen_zero(vmtmp->serveremail))
07414                myserveremail = vmtmp->serveremail;
07415             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07416             /* NULL category for IMAP storage */
07417             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07418                dstvms->curbox,
07419                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07420                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07421                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07422                NULL, urgent_str);
07423 #else
07424             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07425 #endif
07426             saved_messages++;
07427             AST_LIST_REMOVE_CURRENT(list);
07428             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07429             free_user(vmtmp);
07430             if (res)
07431                break;
07432          }
07433          AST_LIST_TRAVERSE_SAFE_END;
07434          if (saved_messages > 0 && !copy_msg_result) {
07435             /* give confirmation that the message was saved */
07436             /* commented out since we can't forward batches yet
07437             if (saved_messages == 1)
07438                res = ast_play_and_wait(chan, "vm-message");
07439             else
07440                res = ast_play_and_wait(chan, "vm-messages");
07441             if (!res)
07442                res = ast_play_and_wait(chan, "vm-saved"); */
07443 #ifdef IMAP_STORAGE
07444             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07445             if (ast_strlen_zero(vmstmp.introfn))
07446 #endif
07447             res = ast_play_and_wait(chan, "vm-msgsaved");
07448          }
07449 #ifndef IMAP_STORAGE
07450          else {
07451             /* with IMAP, mailbox full warning played by imap_check_limits */
07452             res = ast_play_and_wait(chan, "vm-mailboxfull");
07453          }
07454          /* Restore original message without prepended message if backup exists */
07455          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07456          strcpy(textfile, msgfile);
07457          strcpy(backup, msgfile);
07458          strcpy(backup_textfile, msgfile);
07459          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07460          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07461          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07462          if (ast_fileexists(backup, NULL, NULL) > 0) {
07463             ast_filerename(backup, msgfile, NULL);
07464             rename(backup_textfile, textfile);
07465          }
07466 #endif
07467       }
07468       DISPOSE(dir, curmsg);
07469 #ifndef IMAP_STORAGE
07470       if (cmd) { /* assuming hangup, cleanup backup file */
07471          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07472          strcpy(textfile, msgfile);
07473          strcpy(backup_textfile, msgfile);
07474          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07475          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07476          rename(backup_textfile, textfile);
07477       }
07478 #endif
07479    }
07480 
07481    /* If anything failed above, we still have this list to free */
07482    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07483       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07484       free_user(vmtmp);
07485    }
07486    return res ? res : cmd;
07487 }
07488 
07489 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07490 {
07491    int res;
07492    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07493       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07494    return res;
07495 }
07496 
07497 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07498 {
07499    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07500    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);
07501 }
07502 
07503 static int play_message_category(struct ast_channel *chan, const char *category)
07504 {
07505    int res = 0;
07506 
07507    if (!ast_strlen_zero(category))
07508       res = ast_play_and_wait(chan, category);
07509 
07510    if (res) {
07511       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07512       res = 0;
07513    }
07514 
07515    return res;
07516 }
07517 
07518 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07519 {
07520    int res = 0;
07521    struct vm_zone *the_zone = NULL;
07522    time_t t;
07523 
07524    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07525       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07526       return 0;
07527    }
07528 
07529    /* Does this user have a timezone specified? */
07530    if (!ast_strlen_zero(vmu->zonetag)) {
07531       /* Find the zone in the list */
07532       struct vm_zone *z;
07533       AST_LIST_LOCK(&zones);
07534       AST_LIST_TRAVERSE(&zones, z, list) {
07535          if (!strcmp(z->name, vmu->zonetag)) {
07536             the_zone = z;
07537             break;
07538          }
07539       }
07540       AST_LIST_UNLOCK(&zones);
07541    }
07542 
07543 /* No internal variable parsing for now, so we'll comment it out for the time being */
07544 #if 0
07545    /* Set the DIFF_* variables */
07546    ast_localtime(&t, &time_now, NULL);
07547    tv_now = ast_tvnow();
07548    ast_localtime(&tv_now, &time_then, NULL);
07549 
07550    /* Day difference */
07551    if (time_now.tm_year == time_then.tm_year)
07552       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07553    else
07554       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07555    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07556 
07557    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07558 #endif
07559    if (the_zone) {
07560       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07561    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07562       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07563    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07564       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07565    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07566       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);
07567    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07568       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07569    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07570       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07571    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07572       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07573    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07574       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);
07575    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07576       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07577    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07578       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07579    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07580       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);
07581    } else {
07582       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07583    }
07584 #if 0
07585    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07586 #endif
07587    return res;
07588 }
07589 
07590 
07591 
07592 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07593 {
07594    int res = 0;
07595    int i;
07596    char *callerid, *name;
07597    char prefile[PATH_MAX] = "";
07598    
07599 
07600    /* If voicemail cid is not enabled, or we didn't get cid or context from
07601     * the attribute file, leave now.
07602     *
07603     * TODO Still need to change this so that if this function is called by the
07604     * message envelope (and someone is explicitly requesting to hear the CID),
07605     * it does not check to see if CID is enabled in the config file.
07606     */
07607    if ((cid == NULL)||(context == NULL))
07608       return res;
07609 
07610    /* Strip off caller ID number from name */
07611    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07612    ast_callerid_parse(cid, &name, &callerid);
07613    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07614       /* Check for internal contexts and only */
07615       /* say extension when the call didn't come from an internal context in the list */
07616       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07617          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07618          if ((strcmp(cidinternalcontexts[i], context) == 0))
07619             break;
07620       }
07621       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07622          if (!res) {
07623             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07624             if (!ast_strlen_zero(prefile)) {
07625             /* See if we can find a recorded name for this person instead of their extension number */
07626                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07627                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07628                   if (!callback)
07629                      res = wait_file2(chan, vms, "vm-from");
07630                   res = ast_stream_and_wait(chan, prefile, "");
07631                } else {
07632                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07633                   /* Say "from extension" as one saying to sound smoother */
07634                   if (!callback)
07635                      res = wait_file2(chan, vms, "vm-from-extension");
07636                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07637                }
07638             }
07639          }
07640       } else if (!res) {
07641          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07642          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07643          if (!callback)
07644             res = wait_file2(chan, vms, "vm-from-phonenumber");
07645          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07646       }
07647    } else {
07648       /* Number unknown */
07649       ast_debug(1, "VM-CID: From an unknown number\n");
07650       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07651       res = wait_file2(chan, vms, "vm-unknown-caller");
07652    }
07653    return res;
07654 }
07655 
07656 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07657 {
07658    int res = 0;
07659    int durationm;
07660    int durations;
07661    /* Verify that we have a duration for the message */
07662    if (duration == NULL)
07663       return res;
07664 
07665    /* Convert from seconds to minutes */
07666    durations = atoi(duration);
07667    durationm = (durations / 60);
07668 
07669    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07670 
07671    if ((!res) && (durationm >= minduration)) {
07672       res = wait_file2(chan, vms, "vm-duration");
07673 
07674       /* POLISH syntax */
07675       if (!strncasecmp(chan->language, "pl", 2)) {
07676          div_t num = div(durationm, 10);
07677 
07678          if (durationm == 1) {
07679             res = ast_play_and_wait(chan, "digits/1z");
07680             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07681          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07682             if (num.rem == 2) {
07683                if (!num.quot) {
07684                   res = ast_play_and_wait(chan, "digits/2-ie");
07685                } else {
07686                   res = say_and_wait(chan, durationm - 2 , chan->language);
07687                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07688                }
07689             } else {
07690                res = say_and_wait(chan, durationm, chan->language);
07691             }
07692             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07693          } else {
07694             res = say_and_wait(chan, durationm, chan->language);
07695             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07696          }
07697       /* DEFAULT syntax */
07698       } else {
07699          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07700          res = wait_file2(chan, vms, "vm-minutes");
07701       }
07702    }
07703    return res;
07704 }
07705 
07706 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07707 {
07708    int res = 0;
07709    char filename[256], *cid;
07710    const char *origtime, *context, *category, *duration, *flag;
07711    struct ast_config *msg_cfg;
07712    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07713 
07714    vms->starting = 0;
07715    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07716    adsi_message(chan, vms);
07717    if (!vms->curmsg) {
07718       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07719    } else if (vms->curmsg == vms->lastmsg) {
07720       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07721    }
07722 
07723    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07724    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07725    msg_cfg = ast_config_load(filename, config_flags);
07726    if (!valid_config(msg_cfg)) {
07727       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07728       return 0;
07729    }
07730    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07731 
07732    /* Play the word urgent if we are listening to urgent messages */
07733    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07734       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07735    }
07736 
07737    if (!res) {
07738       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07739       /* POLISH syntax */
07740       if (!strncasecmp(chan->language, "pl", 2)) {
07741          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07742             int ten, one;
07743             char nextmsg[256];
07744             ten = (vms->curmsg + 1) / 10;
07745             one = (vms->curmsg + 1) % 10;
07746 
07747             if (vms->curmsg < 20) {
07748                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07749                res = wait_file2(chan, vms, nextmsg);
07750             } else {
07751                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07752                res = wait_file2(chan, vms, nextmsg);
07753                if (one > 0) {
07754                   if (!res) {
07755                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07756                      res = wait_file2(chan, vms, nextmsg);
07757                   }
07758                }
07759             }
07760          }
07761          if (!res)
07762             res = wait_file2(chan, vms, "vm-message");
07763       /* HEBREW syntax */
07764       } else if (!strncasecmp(chan->language, "he", 2)) {
07765          if (!vms->curmsg) {
07766             res = wait_file2(chan, vms, "vm-message");
07767             res = wait_file2(chan, vms, "vm-first");
07768          } else if (vms->curmsg == vms->lastmsg) {
07769             res = wait_file2(chan, vms, "vm-message");
07770             res = wait_file2(chan, vms, "vm-last");
07771          } else {
07772             res = wait_file2(chan, vms, "vm-message");
07773             res = wait_file2(chan, vms, "vm-number");
07774             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07775          }
07776       /* VIETNAMESE syntax */
07777       } else if (!strncasecmp(chan->language, "vi", 2)) {
07778          if (!vms->curmsg) {
07779             res = wait_file2(chan, vms, "vm-message");
07780             res = wait_file2(chan, vms, "vm-first");
07781          } else if (vms->curmsg == vms->lastmsg) {
07782             res = wait_file2(chan, vms, "vm-message");
07783             res = wait_file2(chan, vms, "vm-last");
07784          } else {
07785             res = wait_file2(chan, vms, "vm-message");
07786             res = wait_file2(chan, vms, "vm-number");
07787             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07788          }
07789       } else {
07790          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07791             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07792          } else { /* DEFAULT syntax */
07793             res = wait_file2(chan, vms, "vm-message");
07794          }
07795          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07796             if (!res) {
07797                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07798                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07799             }
07800          }
07801       }
07802    }
07803 
07804    if (!valid_config(msg_cfg)) {
07805       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07806       return 0;
07807    }
07808 
07809    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07810       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07811       DISPOSE(vms->curdir, vms->curmsg);
07812       ast_config_destroy(msg_cfg);
07813       return 0;
07814    }
07815 
07816    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07817    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07818    category = ast_variable_retrieve(msg_cfg, "message", "category");
07819 
07820    context = ast_variable_retrieve(msg_cfg, "message", "context");
07821    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07822       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07823    if (!res) {
07824       res = play_message_category(chan, category);
07825    }
07826    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07827       res = play_message_datetime(chan, vmu, origtime, filename);
07828    }
07829    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07830       res = play_message_callerid(chan, vms, cid, context, 0);
07831    }
07832    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07833       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07834    }
07835    /* Allow pressing '1' to skip envelope / callerid */
07836    if (res == '1') {
07837       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07838       res = 0;
07839    }
07840    ast_config_destroy(msg_cfg);
07841 
07842    if (!res) {
07843       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07844 #ifdef IMAP_STORAGE
07845       ast_mutex_lock(&vms->lock);
07846 #endif
07847       vms->heard[vms->curmsg] = 1;
07848 #ifdef IMAP_STORAGE
07849       ast_mutex_unlock(&vms->lock);
07850       /*IMAP storage stores any prepended message from a forward
07851        * as a separate file from the rest of the message
07852        */
07853       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07854          wait_file(chan, vms, vms->introfn);
07855       }
07856 #endif
07857       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07858          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07859          res = 0;
07860       }
07861       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07862    }
07863    DISPOSE(vms->curdir, vms->curmsg);
07864    return res;
07865 }
07866 
07867 #ifdef IMAP_STORAGE
07868 static int imap_remove_file(char *dir, int msgnum)
07869 {
07870    char fn[PATH_MAX];
07871    char full_fn[PATH_MAX];
07872    char intro[PATH_MAX] = {0,};
07873    
07874    if (msgnum > -1) {
07875       make_file(fn, sizeof(fn), dir, msgnum);
07876       snprintf(intro, sizeof(intro), "%sintro", fn);
07877    } else
07878       ast_copy_string(fn, dir, sizeof(fn));
07879    
07880    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07881       ast_filedelete(fn, NULL);
07882       if (!ast_strlen_zero(intro)) {
07883          ast_filedelete(intro, NULL);
07884       }
07885       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07886       unlink(full_fn);
07887    }
07888    return 0;
07889 }
07890 
07891 
07892 
07893 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07894 {
07895    char *file, *filename;
07896    char *attachment;
07897    char arg[10];
07898    int i;
07899    BODY* body;
07900 
07901    file = strrchr(ast_strdupa(dir), '/');
07902    if (file) {
07903       *file++ = '\0';
07904    } else {
07905       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07906       return -1;
07907    }
07908 
07909    ast_mutex_lock(&vms->lock);
07910    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07911       mail_fetchstructure(vms->mailstream, i + 1, &body);
07912       /* We have the body, now we extract the file name of the first attachment. */
07913       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07914          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07915       } else {
07916          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07917          ast_mutex_unlock(&vms->lock);
07918          return -1;
07919       }
07920       filename = strsep(&attachment, ".");
07921       if (!strcmp(filename, file)) {
07922          sprintf(arg, "%d", i + 1);
07923          mail_setflag(vms->mailstream, arg, "\\DELETED");
07924       }
07925    }
07926    mail_expunge(vms->mailstream);
07927    ast_mutex_unlock(&vms->lock);
07928    return 0;
07929 }
07930 
07931 #elif !defined(IMAP_STORAGE)
07932 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07933 {
07934    int count_msg, last_msg;
07935 
07936    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07937 
07938    /* Rename the member vmbox HERE so that we don't try to return before
07939     * we know what's going on.
07940     */
07941    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07942 
07943    /* Faster to make the directory than to check if it exists. */
07944    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07945 
07946    /* traverses directory using readdir (or select query for ODBC) */
07947    count_msg = count_messages(vmu, vms->curdir);
07948    if (count_msg < 0) {
07949       return count_msg;
07950    } else {
07951       vms->lastmsg = count_msg - 1;
07952    }
07953 
07954    if (vm_allocate_dh(vms, vmu, count_msg)) {
07955       return -1;
07956    }
07957 
07958    /*
07959    The following test is needed in case sequencing gets messed up.
07960    There appears to be more than one way to mess up sequence, so
07961    we will not try to find all of the root causes--just fix it when
07962    detected.
07963    */
07964 
07965    if (vm_lock_path(vms->curdir)) {
07966       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07967       return ERROR_LOCK_PATH;
07968    }
07969 
07970    /* for local storage, checks directory for messages up to maxmsg limit */
07971    last_msg = last_message_index(vmu, vms->curdir);
07972    ast_unlock_path(vms->curdir);
07973 
07974    if (last_msg < -1) {
07975       return last_msg;
07976    } else if (vms->lastmsg != last_msg) {
07977       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);
07978       resequence_mailbox(vmu, vms->curdir, count_msg);
07979    }
07980 
07981    return 0;
07982 }
07983 #endif
07984 
07985 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07986 {
07987    int x = 0;
07988    int last_msg_idx = 0;
07989 
07990 #ifndef IMAP_STORAGE
07991    int res = 0, nummsg;
07992    char fn2[PATH_MAX];
07993 #endif
07994 
07995    if (vms->lastmsg <= -1) {
07996       goto done;
07997    }
07998 
07999    vms->curmsg = -1;
08000 #ifndef IMAP_STORAGE
08001    /* Get the deleted messages fixed */
08002    if (vm_lock_path(vms->curdir)) {
08003       return ERROR_LOCK_PATH;
08004    }
08005 
08006    /* update count as message may have arrived while we've got mailbox open */
08007    last_msg_idx = last_message_index(vmu, vms->curdir);
08008    if (last_msg_idx != vms->lastmsg) {
08009       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08010    }
08011 
08012    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08013    for (x = 0; x < last_msg_idx + 1; x++) {
08014       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08015          /* Save this message.  It's not in INBOX or hasn't been heard */
08016          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08017          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08018             break;
08019          }
08020          vms->curmsg++;
08021          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08022          if (strcmp(vms->fn, fn2)) {
08023             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08024          }
08025       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08026          /* Move to old folder before deleting */
08027          res = save_to_folder(vmu, vms, x, 1);
08028          if (res == ERROR_LOCK_PATH) {
08029             /* If save failed do not delete the message */
08030             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08031             vms->deleted[x] = 0;
08032             vms->heard[x] = 0;
08033             --x;
08034          }
08035       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08036          /* Move to deleted folder */
08037          res = save_to_folder(vmu, vms, x, 10);
08038          if (res == ERROR_LOCK_PATH) {
08039             /* If save failed do not delete the message */
08040             vms->deleted[x] = 0;
08041             vms->heard[x] = 0;
08042             --x;
08043          }
08044       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08045          /* If realtime storage enabled - we should explicitly delete this message,
08046          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08047          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08048          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08049             DELETE(vms->curdir, x, vms->fn, vmu);
08050          }
08051       }
08052    }
08053 
08054    /* Delete ALL remaining messages */
08055    nummsg = x - 1;
08056    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08057       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08058       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08059          DELETE(vms->curdir, x, vms->fn, vmu);
08060       }
08061    }
08062    ast_unlock_path(vms->curdir);
08063 #else /* defined(IMAP_STORAGE) */
08064    ast_mutex_lock(&vms->lock);
08065    if (vms->deleted) {
08066       /* Since we now expunge after each delete, deleting in reverse order
08067        * ensures that no reordering occurs between each step. */
08068       last_msg_idx = vms->dh_arraysize;
08069       for (x = last_msg_idx - 1; x >= 0; x--) {
08070          if (vms->deleted[x]) {
08071             ast_debug(3, "IMAP delete of %d\n", x);
08072             DELETE(vms->curdir, x, vms->fn, vmu);
08073          }
08074       }
08075    }
08076 #endif
08077 
08078 done:
08079    if (vms->deleted) {
08080       ast_free(vms->deleted);
08081       vms->deleted = NULL;
08082    }
08083    if (vms->heard) {
08084       ast_free(vms->heard);
08085       vms->heard = NULL;
08086    }
08087    vms->dh_arraysize = 0;
08088 #ifdef IMAP_STORAGE
08089    ast_mutex_unlock(&vms->lock);
08090 #endif
08091 
08092    return 0;
08093 }
08094 
08095 /* In Greek even though we CAN use a syntax like "friends messages"
08096  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08097  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08098  * syntax for the above three categories which is more elegant.
08099  */
08100 
08101 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08102 {
08103    int cmd;
08104    char *buf;
08105 
08106    buf = ast_alloca(strlen(box) + 2);
08107    strcpy(buf, box);
08108    strcat(buf, "s");
08109 
08110    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08111       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08112       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08113    } else {
08114       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08115       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08116    }
08117 }
08118 
08119 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08120 {
08121    int cmd;
08122 
08123    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08124       if (!strcasecmp(box, "vm-INBOX"))
08125          cmd = ast_play_and_wait(chan, "vm-new-e");
08126       else
08127          cmd = ast_play_and_wait(chan, "vm-old-e");
08128       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08129    } else {
08130       cmd = ast_play_and_wait(chan, "vm-messages");
08131       return cmd ? cmd : ast_play_and_wait(chan, box);
08132    }
08133 }
08134 
08135 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08136 {
08137    int cmd;
08138 
08139    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08140       cmd = ast_play_and_wait(chan, "vm-messages");
08141       return cmd ? cmd : ast_play_and_wait(chan, box);
08142    } else {
08143       cmd = ast_play_and_wait(chan, box);
08144       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08145    }
08146 }
08147 
08148 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08149 {
08150    int cmd;
08151 
08152    if (  !strncasecmp(chan->language, "it", 2) ||
08153         !strncasecmp(chan->language, "es", 2) ||
08154         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08155       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08156       return cmd ? cmd : ast_play_and_wait(chan, box);
08157    } else if (!strncasecmp(chan->language, "gr", 2)) {
08158       return vm_play_folder_name_gr(chan, box);
08159    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08160       return ast_play_and_wait(chan, box);
08161    } else if (!strncasecmp(chan->language, "pl", 2)) {
08162       return vm_play_folder_name_pl(chan, box);
08163    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08164       return vm_play_folder_name_ua(chan, box);
08165    } else if (!strncasecmp(chan->language, "vi", 2)) {
08166       return ast_play_and_wait(chan, box);
08167    } else {  /* Default English */
08168       cmd = ast_play_and_wait(chan, box);
08169       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08170    }
08171 }
08172 
08173 /* GREEK SYNTAX
08174    In greek the plural for old/new is
08175    different so we need the following files
08176    We also need vm-denExeteMynhmata because
08177    this syntax is different.
08178 
08179    -> vm-Olds.wav : "Palia"
08180    -> vm-INBOXs.wav : "Nea"
08181    -> vm-denExeteMynhmata : "den exete mynhmata"
08182 */
08183 
08184 
08185 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08186 {
08187    int res = 0;
08188 
08189    if (vms->newmessages) {
08190       res = ast_play_and_wait(chan, "vm-youhave");
08191       if (!res) 
08192          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08193       if (!res) {
08194          if ((vms->newmessages == 1)) {
08195             res = ast_play_and_wait(chan, "vm-INBOX");
08196             if (!res)
08197                res = ast_play_and_wait(chan, "vm-message");
08198          } else {
08199             res = ast_play_and_wait(chan, "vm-INBOXs");
08200             if (!res)
08201                res = ast_play_and_wait(chan, "vm-messages");
08202          }
08203       }
08204    } else if (vms->oldmessages){
08205       res = ast_play_and_wait(chan, "vm-youhave");
08206       if (!res)
08207          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08208       if ((vms->oldmessages == 1)){
08209          res = ast_play_and_wait(chan, "vm-Old");
08210          if (!res)
08211             res = ast_play_and_wait(chan, "vm-message");
08212       } else {
08213          res = ast_play_and_wait(chan, "vm-Olds");
08214          if (!res)
08215             res = ast_play_and_wait(chan, "vm-messages");
08216       }
08217    } else if (!vms->oldmessages && !vms->newmessages) 
08218       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08219    return res;
08220 }
08221 
08222 /* Version of vm_intro() designed to work for many languages.
08223  *
08224  * It is hoped that this function can prevent the proliferation of 
08225  * language-specific vm_intro() functions and in time replace the language-
08226  * specific functions which already exist.  An examination of the language-
08227  * specific functions revealed that they all corrected the same deficiencies
08228  * in vm_intro_en() (which was the default function). Namely:
08229  *
08230  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08231  *     wording of the voicemail greeting hides this problem.  For example,
08232  *     vm-INBOX contains only the word "new".  This means that both of these
08233  *     sequences produce valid utterances:
08234  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08235  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08236  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08237  *     in many languages) the first utterance becomes "you have 1 the new message".
08238  *  2) The function contains hardcoded rules for pluralizing the word "message".
08239  *     These rules are correct for English, but not for many other languages.
08240  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08241  *     required in many languages.
08242  *  4) The gender of the word for "message" is not specified. This is a problem
08243  *     because in many languages the gender of the number in phrases such
08244  *     as "you have one new message" must match the gender of the word
08245  *     meaning "message".
08246  *
08247  * Fixing these problems for each new language has meant duplication of effort.
08248  * This new function solves the problems in the following general ways:
08249  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08250  *     and vm-Old respectively for those languages where it makes sense.
08251  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08252  *     on vm-message.
08253  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08254  *     prefix on vm-new and vm-old (none for English).
08255  *  4) Pass the gender of the language's word for "message" as an agument to
08256  *     this function which is can in turn pass on to the functions which 
08257  *     say numbers and put endings on nounds and adjectives.
08258  *
08259  * All languages require these messages:
08260  *  vm-youhave    "You have..."
08261  *  vm-and     "and"
08262  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08263  *
08264  * To use it for English, you will need these additional sound files:
08265  *  vm-new     "new"
08266  *  vm-message    "message", singular
08267  *  vm-messages      "messages", plural
08268  *
08269  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08270  *
08271  *  vm-newn    "novoye" (singular, neuter)
08272  *  vm-newx    "novikh" (counting plural form, genative plural)
08273  *  vm-message    "sobsheniye" (singular form)
08274  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08275  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08276  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08277  *  digits/2n     "dva" (neuter singular)
08278  */
08279 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08280 {
08281    int res;
08282    int lastnum = 0;
08283 
08284    res = ast_play_and_wait(chan, "vm-youhave");
08285 
08286    if (!res && vms->newmessages) {
08287       lastnum = vms->newmessages;
08288 
08289       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08290          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08291       }
08292 
08293       if (!res && vms->oldmessages) {
08294          res = ast_play_and_wait(chan, "vm-and");
08295       }
08296    }
08297 
08298    if (!res && vms->oldmessages) {
08299       lastnum = vms->oldmessages;
08300 
08301       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08302          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08303       }
08304    }
08305 
08306    if (!res) {
08307       if (lastnum == 0) {
08308          res = ast_play_and_wait(chan, "vm-no");
08309       }
08310       if (!res) {
08311          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08312       }
08313    }
08314 
08315    return res;
08316 }
08317 
08318 /* Default Hebrew syntax */
08319 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08320 {
08321    int res = 0;
08322 
08323    /* Introduce messages they have */
08324    if (!res) {
08325       if ((vms->newmessages) || (vms->oldmessages)) {
08326          res = ast_play_and_wait(chan, "vm-youhave");
08327       }
08328       /*
08329        * The word "shtei" refers to the number 2 in hebrew when performing a count
08330        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08331        * an element, this is one of them.
08332        */
08333       if (vms->newmessages) {
08334          if (!res) {
08335             if (vms->newmessages == 1) {
08336                res = ast_play_and_wait(chan, "vm-INBOX1");
08337             } else {
08338                if (vms->newmessages == 2) {
08339                   res = ast_play_and_wait(chan, "vm-shtei");
08340                } else {
08341                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08342                }
08343                res = ast_play_and_wait(chan, "vm-INBOX");
08344             }
08345          }
08346          if (vms->oldmessages && !res) {
08347             res = ast_play_and_wait(chan, "vm-and");
08348             if (vms->oldmessages == 1) {
08349                res = ast_play_and_wait(chan, "vm-Old1");
08350             } else {
08351                if (vms->oldmessages == 2) {
08352                   res = ast_play_and_wait(chan, "vm-shtei");
08353                } else {
08354                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08355                }
08356                res = ast_play_and_wait(chan, "vm-Old");
08357             }
08358          }
08359       }
08360       if (!res && vms->oldmessages && !vms->newmessages) {
08361          if (!res) {
08362             if (vms->oldmessages == 1) {
08363                res = ast_play_and_wait(chan, "vm-Old1");
08364             } else {
08365                if (vms->oldmessages == 2) {
08366                   res = ast_play_and_wait(chan, "vm-shtei");
08367                } else {
08368                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08369                }
08370                res = ast_play_and_wait(chan, "vm-Old");
08371             }
08372          }
08373       }
08374       if (!res) {
08375          if (!vms->oldmessages && !vms->newmessages) {
08376             if (!res) {
08377                res = ast_play_and_wait(chan, "vm-nomessages");
08378             }
08379          }
08380       }
08381    }
08382    return res;
08383 }
08384    
08385 /* Default English syntax */
08386 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08387 {
08388    int res;
08389 
08390    /* Introduce messages they have */
08391    res = ast_play_and_wait(chan, "vm-youhave");
08392    if (!res) {
08393       if (vms->urgentmessages) {
08394          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08395          if (!res)
08396             res = ast_play_and_wait(chan, "vm-Urgent");
08397          if ((vms->oldmessages || vms->newmessages) && !res) {
08398             res = ast_play_and_wait(chan, "vm-and");
08399          } else if (!res) {
08400             if ((vms->urgentmessages == 1))
08401                res = ast_play_and_wait(chan, "vm-message");
08402             else
08403                res = ast_play_and_wait(chan, "vm-messages");
08404          }
08405       }
08406       if (vms->newmessages) {
08407          res = say_and_wait(chan, vms->newmessages, chan->language);
08408          if (!res)
08409             res = ast_play_and_wait(chan, "vm-INBOX");
08410          if (vms->oldmessages && !res)
08411             res = ast_play_and_wait(chan, "vm-and");
08412          else if (!res) {
08413             if ((vms->newmessages == 1))
08414                res = ast_play_and_wait(chan, "vm-message");
08415             else
08416                res = ast_play_and_wait(chan, "vm-messages");
08417          }
08418             
08419       }
08420       if (!res && vms->oldmessages) {
08421          res = say_and_wait(chan, vms->oldmessages, chan->language);
08422          if (!res)
08423             res = ast_play_and_wait(chan, "vm-Old");
08424          if (!res) {
08425             if (vms->oldmessages == 1)
08426                res = ast_play_and_wait(chan, "vm-message");
08427             else
08428                res = ast_play_and_wait(chan, "vm-messages");
08429          }
08430       }
08431       if (!res) {
08432          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08433             res = ast_play_and_wait(chan, "vm-no");
08434             if (!res)
08435                res = ast_play_and_wait(chan, "vm-messages");
08436          }
08437       }
08438    }
08439    return res;
08440 }
08441 
08442 /* ITALIAN syntax */
08443 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08444 {
08445    /* Introduce messages they have */
08446    int res;
08447    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08448       res = ast_play_and_wait(chan, "vm-no") ||
08449          ast_play_and_wait(chan, "vm-message");
08450    else
08451       res = ast_play_and_wait(chan, "vm-youhave");
08452    if (!res && vms->newmessages) {
08453       res = (vms->newmessages == 1) ?
08454          ast_play_and_wait(chan, "digits/un") ||
08455          ast_play_and_wait(chan, "vm-nuovo") ||
08456          ast_play_and_wait(chan, "vm-message") :
08457          /* 2 or more new messages */
08458          say_and_wait(chan, vms->newmessages, chan->language) ||
08459          ast_play_and_wait(chan, "vm-nuovi") ||
08460          ast_play_and_wait(chan, "vm-messages");
08461       if (!res && vms->oldmessages)
08462          res = ast_play_and_wait(chan, "vm-and");
08463    }
08464    if (!res && vms->oldmessages) {
08465       res = (vms->oldmessages == 1) ?
08466          ast_play_and_wait(chan, "digits/un") ||
08467          ast_play_and_wait(chan, "vm-vecchio") ||
08468          ast_play_and_wait(chan, "vm-message") :
08469          /* 2 or more old messages */
08470          say_and_wait(chan, vms->oldmessages, chan->language) ||
08471          ast_play_and_wait(chan, "vm-vecchi") ||
08472          ast_play_and_wait(chan, "vm-messages");
08473    }
08474    return res;
08475 }
08476 
08477 /* POLISH syntax */
08478 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08479 {
08480    /* Introduce messages they have */
08481    int res;
08482    div_t num;
08483 
08484    if (!vms->oldmessages && !vms->newmessages) {
08485       res = ast_play_and_wait(chan, "vm-no");
08486       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08487       return res;
08488    } else {
08489       res = ast_play_and_wait(chan, "vm-youhave");
08490    }
08491 
08492    if (vms->newmessages) {
08493       num = div(vms->newmessages, 10);
08494       if (vms->newmessages == 1) {
08495          res = ast_play_and_wait(chan, "digits/1-a");
08496          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08497          res = res ? res : ast_play_and_wait(chan, "vm-message");
08498       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08499          if (num.rem == 2) {
08500             if (!num.quot) {
08501                res = ast_play_and_wait(chan, "digits/2-ie");
08502             } else {
08503                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08504                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08505             }
08506          } else {
08507             res = say_and_wait(chan, vms->newmessages, chan->language);
08508          }
08509          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08510          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08511       } else {
08512          res = say_and_wait(chan, vms->newmessages, chan->language);
08513          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08514          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08515       }
08516       if (!res && vms->oldmessages)
08517          res = ast_play_and_wait(chan, "vm-and");
08518    }
08519    if (!res && vms->oldmessages) {
08520       num = div(vms->oldmessages, 10);
08521       if (vms->oldmessages == 1) {
08522          res = ast_play_and_wait(chan, "digits/1-a");
08523          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08524          res = res ? res : ast_play_and_wait(chan, "vm-message");
08525       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08526          if (num.rem == 2) {
08527             if (!num.quot) {
08528                res = ast_play_and_wait(chan, "digits/2-ie");
08529             } else {
08530                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08531                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08532             }
08533          } else {
08534             res = say_and_wait(chan, vms->oldmessages, chan->language);
08535          }
08536          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08537          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08538       } else {
08539          res = say_and_wait(chan, vms->oldmessages, chan->language);
08540          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08541          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08542       }
08543    }
08544 
08545    return res;
08546 }
08547 
08548 /* SWEDISH syntax */
08549 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08550 {
08551    /* Introduce messages they have */
08552    int res;
08553 
08554    res = ast_play_and_wait(chan, "vm-youhave");
08555    if (res)
08556       return res;
08557 
08558    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08559       res = ast_play_and_wait(chan, "vm-no");
08560       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08561       return res;
08562    }
08563 
08564    if (vms->newmessages) {
08565       if ((vms->newmessages == 1)) {
08566          res = ast_play_and_wait(chan, "digits/ett");
08567          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08568          res = res ? res : ast_play_and_wait(chan, "vm-message");
08569       } else {
08570          res = say_and_wait(chan, vms->newmessages, chan->language);
08571          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08572          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08573       }
08574       if (!res && vms->oldmessages)
08575          res = ast_play_and_wait(chan, "vm-and");
08576    }
08577    if (!res && vms->oldmessages) {
08578       if (vms->oldmessages == 1) {
08579          res = ast_play_and_wait(chan, "digits/ett");
08580          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08581          res = res ? res : ast_play_and_wait(chan, "vm-message");
08582       } else {
08583          res = say_and_wait(chan, vms->oldmessages, chan->language);
08584          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08585          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08586       }
08587    }
08588 
08589    return res;
08590 }
08591 
08592 /* NORWEGIAN syntax */
08593 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08594 {
08595    /* Introduce messages they have */
08596    int res;
08597 
08598    res = ast_play_and_wait(chan, "vm-youhave");
08599    if (res)
08600       return res;
08601 
08602    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08603       res = ast_play_and_wait(chan, "vm-no");
08604       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08605       return res;
08606    }
08607 
08608    if (vms->newmessages) {
08609       if ((vms->newmessages == 1)) {
08610          res = ast_play_and_wait(chan, "digits/1");
08611          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08612          res = res ? res : ast_play_and_wait(chan, "vm-message");
08613       } else {
08614          res = say_and_wait(chan, vms->newmessages, chan->language);
08615          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08616          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08617       }
08618       if (!res && vms->oldmessages)
08619          res = ast_play_and_wait(chan, "vm-and");
08620    }
08621    if (!res && vms->oldmessages) {
08622       if (vms->oldmessages == 1) {
08623          res = ast_play_and_wait(chan, "digits/1");
08624          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08625          res = res ? res : ast_play_and_wait(chan, "vm-message");
08626       } else {
08627          res = say_and_wait(chan, vms->oldmessages, chan->language);
08628          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08629          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08630       }
08631    }
08632 
08633    return res;
08634 }
08635 
08636 /* GERMAN syntax */
08637 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08638 {
08639    /* Introduce messages they have */
08640    int res;
08641    res = ast_play_and_wait(chan, "vm-youhave");
08642    if (!res) {
08643       if (vms->newmessages) {
08644          if ((vms->newmessages == 1))
08645             res = ast_play_and_wait(chan, "digits/1F");
08646          else
08647             res = say_and_wait(chan, vms->newmessages, chan->language);
08648          if (!res)
08649             res = ast_play_and_wait(chan, "vm-INBOX");
08650          if (vms->oldmessages && !res)
08651             res = ast_play_and_wait(chan, "vm-and");
08652          else if (!res) {
08653             if ((vms->newmessages == 1))
08654                res = ast_play_and_wait(chan, "vm-message");
08655             else
08656                res = ast_play_and_wait(chan, "vm-messages");
08657          }
08658             
08659       }
08660       if (!res && vms->oldmessages) {
08661          if (vms->oldmessages == 1)
08662             res = ast_play_and_wait(chan, "digits/1F");
08663          else
08664             res = say_and_wait(chan, vms->oldmessages, chan->language);
08665          if (!res)
08666             res = ast_play_and_wait(chan, "vm-Old");
08667          if (!res) {
08668             if (vms->oldmessages == 1)
08669                res = ast_play_and_wait(chan, "vm-message");
08670             else
08671                res = ast_play_and_wait(chan, "vm-messages");
08672          }
08673       }
08674       if (!res) {
08675          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08676             res = ast_play_and_wait(chan, "vm-no");
08677             if (!res)
08678                res = ast_play_and_wait(chan, "vm-messages");
08679          }
08680       }
08681    }
08682    return res;
08683 }
08684 
08685 /* SPANISH syntax */
08686 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08687 {
08688    /* Introduce messages they have */
08689    int res;
08690    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08691       res = ast_play_and_wait(chan, "vm-youhaveno");
08692       if (!res)
08693          res = ast_play_and_wait(chan, "vm-messages");
08694    } else {
08695       res = ast_play_and_wait(chan, "vm-youhave");
08696    }
08697    if (!res) {
08698       if (vms->newmessages) {
08699          if (!res) {
08700             if ((vms->newmessages == 1)) {
08701                res = ast_play_and_wait(chan, "digits/1M");
08702                if (!res)
08703                   res = ast_play_and_wait(chan, "vm-message");
08704                if (!res)
08705                   res = ast_play_and_wait(chan, "vm-INBOXs");
08706             } else {
08707                res = say_and_wait(chan, vms->newmessages, chan->language);
08708                if (!res)
08709                   res = ast_play_and_wait(chan, "vm-messages");
08710                if (!res)
08711                   res = ast_play_and_wait(chan, "vm-INBOX");
08712             }
08713          }
08714          if (vms->oldmessages && !res)
08715             res = ast_play_and_wait(chan, "vm-and");
08716       }
08717       if (vms->oldmessages) {
08718          if (!res) {
08719             if (vms->oldmessages == 1) {
08720                res = ast_play_and_wait(chan, "digits/1M");
08721                if (!res)
08722                   res = ast_play_and_wait(chan, "vm-message");
08723                if (!res)
08724                   res = ast_play_and_wait(chan, "vm-Olds");
08725             } else {
08726                res = say_and_wait(chan, vms->oldmessages, chan->language);
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       }
08734    }
08735 return res;
08736 }
08737 
08738 /* BRAZILIAN PORTUGUESE syntax */
08739 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08740    /* Introduce messages they have */
08741    int res;
08742    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08743       res = ast_play_and_wait(chan, "vm-nomessages");
08744       return res;
08745    } else {
08746       res = ast_play_and_wait(chan, "vm-youhave");
08747    }
08748    if (vms->newmessages) {
08749       if (!res)
08750          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08751       if ((vms->newmessages == 1)) {
08752          if (!res)
08753             res = ast_play_and_wait(chan, "vm-message");
08754          if (!res)
08755             res = ast_play_and_wait(chan, "vm-INBOXs");
08756       } else {
08757          if (!res)
08758             res = ast_play_and_wait(chan, "vm-messages");
08759          if (!res)
08760             res = ast_play_and_wait(chan, "vm-INBOX");
08761       }
08762       if (vms->oldmessages && !res)
08763          res = ast_play_and_wait(chan, "vm-and");
08764    }
08765    if (vms->oldmessages) {
08766       if (!res)
08767          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08768       if (vms->oldmessages == 1) {
08769          if (!res)
08770             res = ast_play_and_wait(chan, "vm-message");
08771          if (!res)
08772             res = ast_play_and_wait(chan, "vm-Olds");
08773       } else {
08774          if (!res)
08775             res = ast_play_and_wait(chan, "vm-messages");
08776          if (!res)
08777             res = ast_play_and_wait(chan, "vm-Old");
08778       }
08779    }
08780    return res;
08781 }
08782 
08783 /* FRENCH syntax */
08784 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08785 {
08786    /* Introduce messages they have */
08787    int res;
08788    res = ast_play_and_wait(chan, "vm-youhave");
08789    if (!res) {
08790       if (vms->newmessages) {
08791          res = say_and_wait(chan, vms->newmessages, chan->language);
08792          if (!res)
08793             res = ast_play_and_wait(chan, "vm-INBOX");
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             res = ast_play_and_wait(chan, "vm-Old");
08808          if (!res) {
08809             if (vms->oldmessages == 1)
08810                res = ast_play_and_wait(chan, "vm-message");
08811             else
08812                res = ast_play_and_wait(chan, "vm-messages");
08813          }
08814       }
08815       if (!res) {
08816          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08817             res = ast_play_and_wait(chan, "vm-no");
08818             if (!res)
08819                res = ast_play_and_wait(chan, "vm-messages");
08820          }
08821       }
08822    }
08823    return res;
08824 }
08825 
08826 /* DUTCH syntax */
08827 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08828 {
08829    /* Introduce messages they have */
08830    int res;
08831    res = ast_play_and_wait(chan, "vm-youhave");
08832    if (!res) {
08833       if (vms->newmessages) {
08834          res = say_and_wait(chan, vms->newmessages, chan->language);
08835          if (!res) {
08836             if (vms->newmessages == 1)
08837                res = ast_play_and_wait(chan, "vm-INBOXs");
08838             else
08839                res = ast_play_and_wait(chan, "vm-INBOX");
08840          }
08841          if (vms->oldmessages && !res)
08842             res = ast_play_and_wait(chan, "vm-and");
08843          else if (!res) {
08844             if ((vms->newmessages == 1))
08845                res = ast_play_and_wait(chan, "vm-message");
08846             else
08847                res = ast_play_and_wait(chan, "vm-messages");
08848          }
08849             
08850       }
08851       if (!res && vms->oldmessages) {
08852          res = say_and_wait(chan, vms->oldmessages, chan->language);
08853          if (!res) {
08854             if (vms->oldmessages == 1)
08855                res = ast_play_and_wait(chan, "vm-Olds");
08856             else
08857                res = ast_play_and_wait(chan, "vm-Old");
08858          }
08859          if (!res) {
08860             if (vms->oldmessages == 1)
08861                res = ast_play_and_wait(chan, "vm-message");
08862             else
08863                res = ast_play_and_wait(chan, "vm-messages");
08864          }
08865       }
08866       if (!res) {
08867          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08868             res = ast_play_and_wait(chan, "vm-no");
08869             if (!res)
08870                res = ast_play_and_wait(chan, "vm-messages");
08871          }
08872       }
08873    }
08874    return res;
08875 }
08876 
08877 /* PORTUGUESE syntax */
08878 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08879 {
08880    /* Introduce messages they have */
08881    int res;
08882    res = ast_play_and_wait(chan, "vm-youhave");
08883    if (!res) {
08884       if (vms->newmessages) {
08885          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08886          if (!res) {
08887             if ((vms->newmessages == 1)) {
08888                res = ast_play_and_wait(chan, "vm-message");
08889                if (!res)
08890                   res = ast_play_and_wait(chan, "vm-INBOXs");
08891             } else {
08892                res = ast_play_and_wait(chan, "vm-messages");
08893                if (!res)
08894                   res = ast_play_and_wait(chan, "vm-INBOX");
08895             }
08896          }
08897          if (vms->oldmessages && !res)
08898             res = ast_play_and_wait(chan, "vm-and");
08899       }
08900       if (!res && vms->oldmessages) {
08901          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08902          if (!res) {
08903             if (vms->oldmessages == 1) {
08904                res = ast_play_and_wait(chan, "vm-message");
08905                if (!res)
08906                   res = ast_play_and_wait(chan, "vm-Olds");
08907             } else {
08908                res = ast_play_and_wait(chan, "vm-messages");
08909                if (!res)
08910                   res = ast_play_and_wait(chan, "vm-Old");
08911             }
08912          }
08913       }
08914       if (!res) {
08915          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08916             res = ast_play_and_wait(chan, "vm-no");
08917             if (!res)
08918                res = ast_play_and_wait(chan, "vm-messages");
08919          }
08920       }
08921    }
08922    return res;
08923 }
08924 
08925 
08926 /* CZECH syntax */
08927 /* in czech there must be declension of word new and message
08928  * czech        : english        : czech      : english
08929  * --------------------------------------------------------
08930  * vm-youhave   : you have 
08931  * vm-novou     : one new        : vm-zpravu  : message
08932  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08933  * vm-novych    : 5-infinite new : vm-zprav   : messages
08934  * vm-starou   : one old
08935  * vm-stare     : 2-4 old 
08936  * vm-starych   : 5-infinite old
08937  * jednu        : one   - falling 4. 
08938  * vm-no        : no  ( no messages )
08939  */
08940 
08941 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08942 {
08943    int res;
08944    res = ast_play_and_wait(chan, "vm-youhave");
08945    if (!res) {
08946       if (vms->newmessages) {
08947          if (vms->newmessages == 1) {
08948             res = ast_play_and_wait(chan, "digits/jednu");
08949          } else {
08950             res = say_and_wait(chan, vms->newmessages, chan->language);
08951          }
08952          if (!res) {
08953             if ((vms->newmessages == 1))
08954                res = ast_play_and_wait(chan, "vm-novou");
08955             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08956                res = ast_play_and_wait(chan, "vm-nove");
08957             if (vms->newmessages > 4)
08958                res = ast_play_and_wait(chan, "vm-novych");
08959          }
08960          if (vms->oldmessages && !res)
08961             res = ast_play_and_wait(chan, "vm-and");
08962          else if (!res) {
08963             if ((vms->newmessages == 1))
08964                res = ast_play_and_wait(chan, "vm-zpravu");
08965             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08966                res = ast_play_and_wait(chan, "vm-zpravy");
08967             if (vms->newmessages > 4)
08968                res = ast_play_and_wait(chan, "vm-zprav");
08969          }
08970       }
08971       if (!res && vms->oldmessages) {
08972          res = say_and_wait(chan, vms->oldmessages, chan->language);
08973          if (!res) {
08974             if ((vms->oldmessages == 1))
08975                res = ast_play_and_wait(chan, "vm-starou");
08976             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08977                res = ast_play_and_wait(chan, "vm-stare");
08978             if (vms->oldmessages > 4)
08979                res = ast_play_and_wait(chan, "vm-starych");
08980          }
08981          if (!res) {
08982             if ((vms->oldmessages == 1))
08983                res = ast_play_and_wait(chan, "vm-zpravu");
08984             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08985                res = ast_play_and_wait(chan, "vm-zpravy");
08986             if (vms->oldmessages > 4)
08987                res = ast_play_and_wait(chan, "vm-zprav");
08988          }
08989       }
08990       if (!res) {
08991          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08992             res = ast_play_and_wait(chan, "vm-no");
08993             if (!res)
08994                res = ast_play_and_wait(chan, "vm-zpravy");
08995          }
08996       }
08997    }
08998    return res;
08999 }
09000 
09001 /* CHINESE (Taiwan) syntax */
09002 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09003 {
09004    int res;
09005    /* Introduce messages they have */
09006    res = ast_play_and_wait(chan, "vm-you");
09007 
09008    if (!res && vms->newmessages) {
09009       res = ast_play_and_wait(chan, "vm-have");
09010       if (!res)
09011          res = say_and_wait(chan, vms->newmessages, chan->language);
09012       if (!res)
09013          res = ast_play_and_wait(chan, "vm-tong");
09014       if (!res)
09015          res = ast_play_and_wait(chan, "vm-INBOX");
09016       if (vms->oldmessages && !res)
09017          res = ast_play_and_wait(chan, "vm-and");
09018       else if (!res) 
09019          res = ast_play_and_wait(chan, "vm-messages");
09020    }
09021    if (!res && vms->oldmessages) {
09022       res = ast_play_and_wait(chan, "vm-have");
09023       if (!res)
09024          res = say_and_wait(chan, vms->oldmessages, chan->language);
09025       if (!res)
09026          res = ast_play_and_wait(chan, "vm-tong");
09027       if (!res)
09028          res = ast_play_and_wait(chan, "vm-Old");
09029       if (!res)
09030          res = ast_play_and_wait(chan, "vm-messages");
09031    }
09032    if (!res && !vms->oldmessages && !vms->newmessages) {
09033       res = ast_play_and_wait(chan, "vm-haveno");
09034       if (!res)
09035          res = ast_play_and_wait(chan, "vm-messages");
09036    }
09037    return res;
09038 }
09039 
09040 /* Vietnamese syntax */
09041 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09042 {
09043    int res;
09044 
09045    /* Introduce messages they have */
09046    res = ast_play_and_wait(chan, "vm-youhave");
09047    if (!res) {
09048       if (vms->newmessages) {
09049          res = say_and_wait(chan, vms->newmessages, chan->language);
09050          if (!res)
09051             res = ast_play_and_wait(chan, "vm-INBOX");
09052          if (vms->oldmessages && !res)
09053             res = ast_play_and_wait(chan, "vm-and");
09054       }
09055       if (!res && vms->oldmessages) {
09056          res = say_and_wait(chan, vms->oldmessages, chan->language);
09057          if (!res)
09058             res = ast_play_and_wait(chan, "vm-Old");        
09059       }
09060       if (!res) {
09061          if (!vms->oldmessages && !vms->newmessages) {
09062             res = ast_play_and_wait(chan, "vm-no");
09063             if (!res)
09064                res = ast_play_and_wait(chan, "vm-message");
09065          }
09066       }
09067    }
09068    return res;
09069 }
09070 
09071 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09072 {
09073    char prefile[256];
09074    
09075    /* Notify the user that the temp greeting is set and give them the option to remove it */
09076    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09077    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09078       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09079       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09080          ast_play_and_wait(chan, "vm-tempgreetactive");
09081       }
09082       DISPOSE(prefile, -1);
09083    }
09084 
09085    /* Play voicemail intro - syntax is different for different languages */
09086    if (0) {
09087       return 0;
09088    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09089       return vm_intro_cs(chan, vms);
09090    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09091       static int deprecation_warning = 0;
09092       if (deprecation_warning++ % 10 == 0) {
09093          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09094       }
09095       return vm_intro_cs(chan, vms);
09096    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09097       return vm_intro_de(chan, vms);
09098    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09099       return vm_intro_es(chan, vms);
09100    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09101       return vm_intro_fr(chan, vms);
09102    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09103       return vm_intro_gr(chan, vms);
09104    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09105       return vm_intro_he(chan, vms);
09106    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09107       return vm_intro_it(chan, vms);
09108    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09109       return vm_intro_nl(chan, vms);
09110    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09111       return vm_intro_no(chan, vms);
09112    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09113       return vm_intro_pl(chan, vms);
09114    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09115       return vm_intro_pt_BR(chan, vms);
09116    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09117       return vm_intro_pt(chan, vms);
09118    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09119       return vm_intro_multilang(chan, vms, "n");
09120    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09121       return vm_intro_se(chan, vms);
09122    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09123       return vm_intro_multilang(chan, vms, "n");
09124    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09125       return vm_intro_vi(chan, vms);
09126    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09127       return vm_intro_zh(chan, vms);
09128    } else {                                             /* Default to ENGLISH */
09129       return vm_intro_en(chan, vms);
09130    }
09131 }
09132 
09133 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09134 {
09135    int res = 0;
09136    /* Play instructions and wait for new command */
09137    while (!res) {
09138       if (vms->starting) {
09139          if (vms->lastmsg > -1) {
09140             if (skipadvanced)
09141                res = ast_play_and_wait(chan, "vm-onefor-full");
09142             else
09143                res = ast_play_and_wait(chan, "vm-onefor");
09144             if (!res)
09145                res = vm_play_folder_name(chan, vms->vmbox);
09146          }
09147          if (!res) {
09148             if (skipadvanced)
09149                res = ast_play_and_wait(chan, "vm-opts-full");
09150             else
09151                res = ast_play_and_wait(chan, "vm-opts");
09152          }
09153       } else {
09154          /* Added for additional help */
09155          if (skipadvanced) {
09156             res = ast_play_and_wait(chan, "vm-onefor-full");
09157             if (!res)
09158                res = vm_play_folder_name(chan, vms->vmbox);
09159             res = ast_play_and_wait(chan, "vm-opts-full");
09160          }
09161          /* Logic:
09162           * If the current message is not the first OR
09163           * if we're listening to the first new message and there are
09164           * also urgent messages, then prompt for navigation to the
09165           * previous message
09166           */
09167          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09168             res = ast_play_and_wait(chan, "vm-prev");
09169          }
09170          if (!res && !skipadvanced)
09171             res = ast_play_and_wait(chan, "vm-advopts");
09172          if (!res)
09173             res = ast_play_and_wait(chan, "vm-repeat");
09174          /* Logic:
09175           * If we're not listening to the last message OR
09176           * we're listening to the last urgent message and there are
09177           * also new non-urgent messages, then prompt for navigation
09178           * to the next message
09179           */
09180          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09181             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09182             res = ast_play_and_wait(chan, "vm-next");
09183          }
09184          if (!res) {
09185             int curmsg_deleted;
09186 #ifdef IMAP_STORAGE
09187             ast_mutex_lock(&vms->lock);
09188 #endif
09189             curmsg_deleted = vms->deleted[vms->curmsg];
09190 #ifdef IMAP_STORAGE
09191             ast_mutex_unlock(&vms->lock);
09192 #endif
09193             if (!curmsg_deleted) {
09194                res = ast_play_and_wait(chan, "vm-delete");
09195             } else {
09196                res = ast_play_and_wait(chan, "vm-undelete");
09197             }
09198             if (!res) {
09199                res = ast_play_and_wait(chan, "vm-toforward");
09200             }
09201             if (!res) {
09202                res = ast_play_and_wait(chan, "vm-savemessage");
09203             }
09204          }
09205       }
09206       if (!res) {
09207          res = ast_play_and_wait(chan, "vm-helpexit");
09208       }
09209       if (!res)
09210          res = ast_waitfordigit(chan, 6000);
09211       if (!res) {
09212          vms->repeats++;
09213          if (vms->repeats > 2) {
09214             res = 't';
09215          }
09216       }
09217    }
09218    return res;
09219 }
09220 
09221 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09222 {
09223    int res = 0;
09224    /* Play instructions and wait for new command */
09225    while (!res) {
09226       if (vms->lastmsg > -1) {
09227          res = ast_play_and_wait(chan, "vm-listen");
09228          if (!res)
09229             res = vm_play_folder_name(chan, vms->vmbox);
09230          if (!res)
09231             res = ast_play_and_wait(chan, "press");
09232          if (!res)
09233             res = ast_play_and_wait(chan, "digits/1");
09234       }
09235       if (!res)
09236          res = ast_play_and_wait(chan, "vm-opts");
09237       if (!res) {
09238          vms->starting = 0;
09239          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09240       }
09241    }
09242    return res;
09243 }
09244 
09245 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09246 {
09247    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09248       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09249    } else {             /* Default to ENGLISH */
09250       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09251    }
09252 }
09253 
09254 
09255 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09256 {
09257    int cmd = 0;
09258    int duration = 0;
09259    int tries = 0;
09260    char newpassword[80] = "";
09261    char newpassword2[80] = "";
09262    char prefile[PATH_MAX] = "";
09263    unsigned char buf[256];
09264    int bytes = 0;
09265 
09266    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09267    if (ast_adsi_available(chan)) {
09268       bytes += adsi_logo(buf + bytes);
09269       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09270       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09271       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09272       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09273       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09274    }
09275 
09276    /* If forcename is set, have the user record their name */
09277    if (ast_test_flag(vmu, VM_FORCENAME)) {
09278       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09279       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09280          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09281          if (cmd < 0 || cmd == 't' || cmd == '#')
09282             return cmd;
09283       }
09284    }
09285 
09286    /* If forcegreetings is set, have the user record their greetings */
09287    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09288       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09289       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09290          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09291          if (cmd < 0 || cmd == 't' || cmd == '#')
09292             return cmd;
09293       }
09294 
09295       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09296       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09297          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09298          if (cmd < 0 || cmd == 't' || cmd == '#')
09299             return cmd;
09300       }
09301    }
09302 
09303    /*
09304     * Change the password last since new users will be able to skip over any steps this one comes before
09305     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09306     */
09307    for (;;) {
09308       newpassword[1] = '\0';
09309       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09310       if (cmd == '#')
09311          newpassword[0] = '\0';
09312       if (cmd < 0 || cmd == 't' || cmd == '#')
09313          return cmd;
09314       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09315       if (cmd < 0 || cmd == 't' || cmd == '#')
09316          return cmd;
09317       cmd = check_password(vmu, newpassword); /* perform password validation */
09318       if (cmd != 0) {
09319          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09320          cmd = ast_play_and_wait(chan, vm_invalid_password);
09321       } else {
09322          newpassword2[1] = '\0';
09323          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09324          if (cmd == '#')
09325             newpassword2[0] = '\0';
09326          if (cmd < 0 || cmd == 't' || cmd == '#')
09327             return cmd;
09328          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09329          if (cmd < 0 || cmd == 't' || cmd == '#')
09330             return cmd;
09331          if (!strcmp(newpassword, newpassword2))
09332             break;
09333          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09334          cmd = ast_play_and_wait(chan, vm_mismatch);
09335       }
09336       if (++tries == 3)
09337          return -1;
09338       if (cmd != 0) {
09339          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09340       }
09341    }
09342    if (pwdchange & PWDCHANGE_INTERNAL)
09343       vm_change_password(vmu, newpassword);
09344    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09345       vm_change_password_shell(vmu, newpassword);
09346 
09347    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09348    cmd = ast_play_and_wait(chan, vm_passchanged);
09349 
09350    return cmd;
09351 }
09352 
09353 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09354 {
09355    int cmd = 0;
09356    int retries = 0;
09357    int duration = 0;
09358    char newpassword[80] = "";
09359    char newpassword2[80] = "";
09360    char prefile[PATH_MAX] = "";
09361    unsigned char buf[256];
09362    int bytes = 0;
09363 
09364    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09365    if (ast_adsi_available(chan)) {
09366       bytes += adsi_logo(buf + bytes);
09367       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09368       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09369       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09370       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09371       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09372    }
09373    while ((cmd >= 0) && (cmd != 't')) {
09374       if (cmd)
09375          retries = 0;
09376       switch (cmd) {
09377       case '1': /* Record your unavailable message */
09378          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09379          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09380          break;
09381       case '2':  /* Record your busy message */
09382          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09383          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09384          break;
09385       case '3': /* Record greeting */
09386          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09387          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09388          break;
09389       case '4':  /* manage the temporary greeting */
09390          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09391          break;
09392       case '5': /* change password */
09393          if (vmu->password[0] == '-') {
09394             cmd = ast_play_and_wait(chan, "vm-no");
09395             break;
09396          }
09397          newpassword[1] = '\0';
09398          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09399          if (cmd == '#')
09400             newpassword[0] = '\0';
09401          else {
09402             if (cmd < 0)
09403                break;
09404             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09405                break;
09406             }
09407          }
09408          cmd = check_password(vmu, newpassword); /* perform password validation */
09409          if (cmd != 0) {
09410             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09411             cmd = ast_play_and_wait(chan, vm_invalid_password);
09412             if (!cmd) {
09413                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09414             }
09415             break;
09416          }
09417          newpassword2[1] = '\0';
09418          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09419          if (cmd == '#')
09420             newpassword2[0] = '\0';
09421          else {
09422             if (cmd < 0)
09423                break;
09424 
09425             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09426                break;
09427             }
09428          }
09429          if (strcmp(newpassword, newpassword2)) {
09430             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09431             cmd = ast_play_and_wait(chan, vm_mismatch);
09432             if (!cmd) {
09433                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09434             }
09435             break;
09436          }
09437 
09438          if (pwdchange & PWDCHANGE_INTERNAL) {
09439             vm_change_password(vmu, newpassword);
09440          }
09441          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09442             vm_change_password_shell(vmu, newpassword);
09443          }
09444 
09445          ast_debug(1, "User %s set password to %s of length %d\n",
09446             vms->username, newpassword, (int) strlen(newpassword));
09447          cmd = ast_play_and_wait(chan, vm_passchanged);
09448          break;
09449       case '*': 
09450          cmd = 't';
09451          break;
09452       default: 
09453          cmd = 0;
09454          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09455          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09456          if (ast_fileexists(prefile, NULL, NULL)) {
09457             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09458          }
09459          DISPOSE(prefile, -1);
09460          if (!cmd) {
09461             cmd = ast_play_and_wait(chan, "vm-options");
09462          }
09463          if (!cmd) {
09464             cmd = ast_waitfordigit(chan, 6000);
09465          }
09466          if (!cmd) {
09467             retries++;
09468          }
09469          if (retries > 3) {
09470             cmd = 't';
09471          }
09472          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09473       }
09474    }
09475    if (cmd == 't')
09476       cmd = 0;
09477    return cmd;
09478 }
09479 
09480 /*!
09481  * \brief The handler for 'record a temporary greeting'. 
09482  * \param chan
09483  * \param vmu
09484  * \param vms
09485  * \param fmtc
09486  * \param record_gain
09487  *
09488  * This is option 4 from the mailbox options menu.
09489  * This function manages the following promptings:
09490  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09491  * 2: remove (delete) the temporary greeting.
09492  * *: return to the main menu.
09493  *
09494  * \return zero on success, -1 on error.
09495  */
09496 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09497 {
09498    int cmd = 0;
09499    int retries = 0;
09500    int duration = 0;
09501    char prefile[PATH_MAX] = "";
09502    unsigned char buf[256];
09503    int bytes = 0;
09504 
09505    if (ast_adsi_available(chan)) {
09506       bytes += adsi_logo(buf + bytes);
09507       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09508       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09509       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09510       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09511       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09512    }
09513 
09514    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09515    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09516    while ((cmd >= 0) && (cmd != 't')) {
09517       if (cmd)
09518          retries = 0;
09519       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09520       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09521          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09522          if (cmd == -1) {
09523             break;
09524          }
09525          cmd = 't';  
09526       } else {
09527          switch (cmd) {
09528          case '1':
09529             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09530             break;
09531          case '2':
09532             DELETE(prefile, -1, prefile, vmu);
09533             ast_play_and_wait(chan, "vm-tempremoved");
09534             cmd = 't';  
09535             break;
09536          case '*': 
09537             cmd = 't';
09538             break;
09539          default:
09540             cmd = ast_play_and_wait(chan,
09541                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09542                   "vm-tempgreeting2" : "vm-tempgreeting");
09543             if (!cmd) {
09544                cmd = ast_waitfordigit(chan, 6000);
09545             }
09546             if (!cmd) {
09547                retries++;
09548             }
09549             if (retries > 3) {
09550                cmd = 't';
09551             }
09552             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09553          }
09554       }
09555       DISPOSE(prefile, -1);
09556    }
09557    if (cmd == 't')
09558       cmd = 0;
09559    return cmd;
09560 }
09561 
09562 /*!
09563  * \brief Greek syntax for 'You have N messages' greeting.
09564  * \param chan
09565  * \param vms
09566  * \param vmu
09567  *
09568  * \return zero on success, -1 on error.
09569  */   
09570 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09571 {
09572    int cmd = 0;
09573 
09574    if (vms->lastmsg > -1) {
09575       cmd = play_message(chan, vmu, vms);
09576    } else {
09577       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09578       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09579          if (!cmd) {
09580             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09581             cmd = ast_play_and_wait(chan, vms->fn);
09582          }
09583          if (!cmd)
09584             cmd = ast_play_and_wait(chan, "vm-messages");
09585       } else {
09586          if (!cmd)
09587             cmd = ast_play_and_wait(chan, "vm-messages");
09588          if (!cmd) {
09589             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09590             cmd = ast_play_and_wait(chan, vms->fn);
09591          }
09592       }
09593    } 
09594    return cmd;
09595 }
09596 
09597 /* Hebrew Syntax */
09598 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09599 {
09600    int cmd = 0;
09601 
09602    if (vms->lastmsg > -1) {
09603       cmd = play_message(chan, vmu, vms);
09604    } else {
09605       if (!strcasecmp(vms->fn, "INBOX")) {
09606          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09607       } else {
09608          cmd = ast_play_and_wait(chan, "vm-nomessages");
09609       }
09610    }
09611    return cmd;
09612 }
09613 
09614 /*! 
09615  * \brief Default English syntax for 'You have N messages' greeting.
09616  * \param chan
09617  * \param vms
09618  * \param vmu
09619  *
09620  * \return zero on success, -1 on error.
09621  */
09622 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09623 {
09624    int cmd = 0;
09625 
09626    if (vms->lastmsg > -1) {
09627       cmd = play_message(chan, vmu, vms);
09628    } else {
09629       cmd = ast_play_and_wait(chan, "vm-youhave");
09630       if (!cmd) 
09631          cmd = ast_play_and_wait(chan, "vm-no");
09632       if (!cmd) {
09633          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09634          cmd = ast_play_and_wait(chan, vms->fn);
09635       }
09636       if (!cmd)
09637          cmd = ast_play_and_wait(chan, "vm-messages");
09638    }
09639    return cmd;
09640 }
09641 
09642 /*! 
09643  *\brief Italian syntax for 'You have N messages' greeting.
09644  * \param chan
09645  * \param vms
09646  * \param vmu
09647  *
09648  * \return zero on success, -1 on error.
09649  */
09650 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09651 {
09652    int cmd;
09653 
09654    if (vms->lastmsg > -1) {
09655       cmd = play_message(chan, vmu, vms);
09656    } else {
09657       cmd = ast_play_and_wait(chan, "vm-no");
09658       if (!cmd)
09659          cmd = ast_play_and_wait(chan, "vm-message");
09660       if (!cmd) {
09661          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09662          cmd = ast_play_and_wait(chan, vms->fn);
09663       }
09664    }
09665    return cmd;
09666 }
09667 
09668 /*! 
09669  * \brief Spanish syntax for 'You have N messages' greeting.
09670  * \param chan
09671  * \param vms
09672  * \param vmu
09673  *
09674  * \return zero on success, -1 on error.
09675  */
09676 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09677 {
09678    int cmd;
09679 
09680    if (vms->lastmsg > -1) {
09681       cmd = play_message(chan, vmu, vms);
09682    } else {
09683       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09684       if (!cmd)
09685          cmd = ast_play_and_wait(chan, "vm-messages");
09686       if (!cmd) {
09687          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09688          cmd = ast_play_and_wait(chan, vms->fn);
09689       }
09690    }
09691    return cmd;
09692 }
09693 
09694 /*! 
09695  * \brief Portuguese syntax for 'You have N messages' greeting.
09696  * \param chan
09697  * \param vms
09698  * \param vmu
09699  *
09700  * \return zero on success, -1 on error.
09701  */
09702 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09703 {
09704    int cmd;
09705 
09706    if (vms->lastmsg > -1) {
09707       cmd = play_message(chan, vmu, vms);
09708    } else {
09709       cmd = ast_play_and_wait(chan, "vm-no");
09710       if (!cmd) {
09711          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09712          cmd = ast_play_and_wait(chan, vms->fn);
09713       }
09714       if (!cmd)
09715          cmd = ast_play_and_wait(chan, "vm-messages");
09716    }
09717    return cmd;
09718 }
09719 
09720 /*! 
09721  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09722  * \param chan
09723  * \param vms
09724  * \param vmu
09725  *
09726  * \return zero on success, -1 on error.
09727  */
09728 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09729 {
09730    int cmd;
09731 
09732    if (vms->lastmsg > -1) {
09733       cmd = play_message(chan, vmu, vms);
09734    } else {
09735       cmd = ast_play_and_wait(chan, "vm-you");
09736       if (!cmd) 
09737          cmd = ast_play_and_wait(chan, "vm-haveno");
09738       if (!cmd)
09739          cmd = ast_play_and_wait(chan, "vm-messages");
09740       if (!cmd) {
09741          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09742          cmd = ast_play_and_wait(chan, vms->fn);
09743       }
09744    }
09745    return cmd;
09746 }
09747 
09748 /*! 
09749  * \brief Vietnamese syntax for 'You have N messages' greeting.
09750  * \param chan
09751  * \param vms
09752  * \param vmu
09753  *
09754  * \return zero on success, -1 on error.
09755  */
09756 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09757 {
09758    int cmd = 0;
09759 
09760    if (vms->lastmsg > -1) {
09761       cmd = play_message(chan, vmu, vms);
09762    } else {
09763       cmd = ast_play_and_wait(chan, "vm-no");
09764       if (!cmd) {
09765          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09766          cmd = ast_play_and_wait(chan, vms->fn);
09767       }
09768    }
09769    return cmd;
09770 }
09771 
09772 /*!
09773  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09774  * \param chan The channel for the current user. We read the language property from this.
09775  * \param vms passed into the language-specific vm_browse_messages function.
09776  * \param vmu passed into the language-specific vm_browse_messages function.
09777  * 
09778  * The method to be invoked is determined by the value of language code property in the user's channel.
09779  * The default (when unable to match) is to use english.
09780  *
09781  * \return zero on success, -1 on error.
09782  */
09783 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09784 {
09785    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09786       return vm_browse_messages_es(chan, vms, vmu);
09787    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09788       return vm_browse_messages_gr(chan, vms, vmu);
09789    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09790       return vm_browse_messages_he(chan, vms, vmu);
09791    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09792       return vm_browse_messages_it(chan, vms, vmu);
09793    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09794       return vm_browse_messages_pt(chan, vms, vmu);
09795    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09796       return vm_browse_messages_vi(chan, vms, vmu);
09797    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09798       return vm_browse_messages_zh(chan, vms, vmu);
09799    } else {                                             /* Default to English syntax */
09800       return vm_browse_messages_en(chan, vms, vmu);
09801    }
09802 }
09803 
09804 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09805          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09806          int skipuser, int max_logins, int silent)
09807 {
09808    int useadsi = 0, valid = 0, logretries = 0;
09809    char password[AST_MAX_EXTENSION]="", *passptr;
09810    struct ast_vm_user vmus, *vmu = NULL;
09811 
09812    /* If ADSI is supported, setup login screen */
09813    adsi_begin(chan, &useadsi);
09814    if (!skipuser && useadsi)
09815       adsi_login(chan);
09816    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09817       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09818       return -1;
09819    }
09820 
09821    /* Authenticate them and get their mailbox/password */
09822 
09823    while (!valid && (logretries < max_logins)) {
09824       /* Prompt for, and read in the username */
09825       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09826          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09827          return -1;
09828       }
09829       if (ast_strlen_zero(mailbox)) {
09830          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09831             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09832          } else {
09833             ast_verb(3, "Username not entered\n"); 
09834             return -1;
09835          }
09836       } else if (mailbox[0] == '*') {
09837          /* user entered '*' */
09838          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09839          if (ast_exists_extension(chan, chan->context, "a", 1,
09840             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09841             return -1;
09842          }
09843          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09844          mailbox[0] = '\0';
09845       }
09846 
09847       if (useadsi)
09848          adsi_password(chan);
09849 
09850       if (!ast_strlen_zero(prefix)) {
09851          char fullusername[80] = "";
09852          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09853          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09854          ast_copy_string(mailbox, fullusername, mailbox_size);
09855       }
09856 
09857       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09858       vmu = find_user(&vmus, context, mailbox);
09859       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09860          /* saved password is blank, so don't bother asking */
09861          password[0] = '\0';
09862       } else {
09863          if (ast_streamfile(chan, vm_password, chan->language)) {
09864             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09865             return -1;
09866          }
09867          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09868             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09869             return -1;
09870          } else if (password[0] == '*') {
09871             /* user entered '*' */
09872             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09873             if (ast_exists_extension(chan, chan->context, "a", 1,
09874                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09875                mailbox[0] = '*';
09876                return -1;
09877             }
09878             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09879             mailbox[0] = '\0';
09880             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09881             vmu = NULL;
09882          }
09883       }
09884 
09885       if (vmu) {
09886          passptr = vmu->password;
09887          if (passptr[0] == '-') passptr++;
09888       }
09889       if (vmu && !strcmp(passptr, password))
09890          valid++;
09891       else {
09892          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09893          if (!ast_strlen_zero(prefix))
09894             mailbox[0] = '\0';
09895       }
09896       logretries++;
09897       if (!valid) {
09898          if (skipuser || logretries >= max_logins) {
09899             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09900                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09901                return -1;
09902             }
09903          } else {
09904             if (useadsi)
09905                adsi_login(chan);
09906             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09907                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09908                return -1;
09909             }
09910          }
09911          if (ast_waitstream(chan, "")) /* Channel is hung up */
09912             return -1;
09913       }
09914    }
09915    if (!valid && (logretries >= max_logins)) {
09916       ast_stopstream(chan);
09917       ast_play_and_wait(chan, "vm-goodbye");
09918       return -1;
09919    }
09920    if (vmu && !skipuser) {
09921       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09922    }
09923    return 0;
09924 }
09925 
09926 static int vm_execmain(struct ast_channel *chan, const char *data)
09927 {
09928    /* XXX This is, admittedly, some pretty horrendous code.  For some
09929       reason it just seemed a lot easier to do with GOTO's.  I feel
09930       like I'm back in my GWBASIC days. XXX */
09931    int res = -1;
09932    int cmd = 0;
09933    int valid = 0;
09934    char prefixstr[80] ="";
09935    char ext_context[256]="";
09936    int box;
09937    int useadsi = 0;
09938    int skipuser = 0;
09939    struct vm_state vms;
09940    struct ast_vm_user *vmu = NULL, vmus;
09941    char *context = NULL;
09942    int silentexit = 0;
09943    struct ast_flags flags = { 0 };
09944    signed char record_gain = 0;
09945    int play_auto = 0;
09946    int play_folder = 0;
09947    int in_urgent = 0;
09948 #ifdef IMAP_STORAGE
09949    int deleted = 0;
09950 #endif
09951 
09952    /* Add the vm_state to the active list and keep it active */
09953    memset(&vms, 0, sizeof(vms));
09954 
09955    vms.lastmsg = -1;
09956 
09957    memset(&vmus, 0, sizeof(vmus));
09958 
09959    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09960    if (chan->_state != AST_STATE_UP) {
09961       ast_debug(1, "Before ast_answer\n");
09962       ast_answer(chan);
09963    }
09964 
09965    if (!ast_strlen_zero(data)) {
09966       char *opts[OPT_ARG_ARRAY_SIZE];
09967       char *parse;
09968       AST_DECLARE_APP_ARGS(args,
09969          AST_APP_ARG(argv0);
09970          AST_APP_ARG(argv1);
09971       );
09972 
09973       parse = ast_strdupa(data);
09974 
09975       AST_STANDARD_APP_ARGS(args, parse);
09976 
09977       if (args.argc == 2) {
09978          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09979             return -1;
09980          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09981             int gain;
09982             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09983                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09984                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09985                   return -1;
09986                } else {
09987                   record_gain = (signed char) gain;
09988                }
09989             } else {
09990                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09991             }
09992          }
09993          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09994             play_auto = 1;
09995             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09996                /* See if it is a folder name first */
09997                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09998                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09999                      play_folder = -1;
10000                   }
10001                } else {
10002                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10003                }
10004             } else {
10005                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10006             }
10007             if (play_folder > 9 || play_folder < 0) {
10008                ast_log(AST_LOG_WARNING,
10009                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10010                   opts[OPT_ARG_PLAYFOLDER]);
10011                play_folder = 0;
10012             }
10013          }
10014       } else {
10015          /* old style options parsing */
10016          while (*(args.argv0)) {
10017             if (*(args.argv0) == 's')
10018                ast_set_flag(&flags, OPT_SILENT);
10019             else if (*(args.argv0) == 'p')
10020                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10021             else 
10022                break;
10023             (args.argv0)++;
10024          }
10025 
10026       }
10027 
10028       valid = ast_test_flag(&flags, OPT_SILENT);
10029 
10030       if ((context = strchr(args.argv0, '@')))
10031          *context++ = '\0';
10032 
10033       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10034          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10035       else
10036          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10037 
10038       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10039          skipuser++;
10040       else
10041          valid = 0;
10042    }
10043 
10044    if (!valid)
10045       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10046 
10047    ast_debug(1, "After vm_authenticate\n");
10048 
10049    if (vms.username[0] == '*') {
10050       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10051 
10052       /* user entered '*' */
10053       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10054          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10055          res = 0; /* prevent hangup */
10056          goto out;
10057       }
10058    }
10059 
10060    if (!res) {
10061       valid = 1;
10062       if (!skipuser)
10063          vmu = &vmus;
10064    } else {
10065       res = 0;
10066    }
10067 
10068    /* If ADSI is supported, setup login screen */
10069    adsi_begin(chan, &useadsi);
10070 
10071    ast_test_suite_assert(valid);
10072    if (!valid) {
10073       goto out;
10074    }
10075    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10076 
10077 #ifdef IMAP_STORAGE
10078    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10079    pthread_setspecific(ts_vmstate.key, &vms);
10080 
10081    vms.interactive = 1;
10082    vms.updated = 1;
10083    if (vmu)
10084       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10085    vmstate_insert(&vms);
10086    init_vm_state(&vms);
10087 #endif
10088    
10089    /* Set language from config to override channel language */
10090    if (!ast_strlen_zero(vmu->language))
10091       ast_string_field_set(chan, language, vmu->language);
10092 
10093    /* Retrieve urgent, old and new message counts */
10094    ast_debug(1, "Before open_mailbox\n");
10095    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10096    if (res < 0)
10097       goto out;
10098    vms.oldmessages = vms.lastmsg + 1;
10099    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10100    /* check INBOX */
10101    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10102    if (res < 0)
10103       goto out;
10104    vms.newmessages = vms.lastmsg + 1;
10105    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10106    /* Start in Urgent */
10107    in_urgent = 1;
10108    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10109    if (res < 0)
10110       goto out;
10111    vms.urgentmessages = vms.lastmsg + 1;
10112    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10113 
10114    /* Select proper mailbox FIRST!! */
10115    if (play_auto) {
10116       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10117       if (vms.urgentmessages) {
10118          in_urgent = 1;
10119          res = open_mailbox(&vms, vmu, 11);
10120       } else {
10121          in_urgent = 0;
10122          res = open_mailbox(&vms, vmu, play_folder);
10123       }
10124       if (res < 0)
10125          goto out;
10126 
10127       /* If there are no new messages, inform the user and hangup */
10128       if (vms.lastmsg == -1) {
10129          in_urgent = 0;
10130          cmd = vm_browse_messages(chan, &vms, vmu);
10131          res = 0;
10132          goto out;
10133       }
10134    } else {
10135       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10136          /* If we only have old messages start here */
10137          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10138          in_urgent = 0;
10139          play_folder = 1;
10140          if (res < 0)
10141             goto out;
10142       } else if (!vms.urgentmessages && vms.newmessages) {
10143          /* If we have new messages but none are urgent */
10144          in_urgent = 0;
10145          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10146          if (res < 0)
10147             goto out;
10148       }
10149    }
10150 
10151    if (useadsi)
10152       adsi_status(chan, &vms);
10153    res = 0;
10154 
10155    /* Check to see if this is a new user */
10156    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10157       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10158       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10159          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10160       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10161       if ((cmd == 't') || (cmd == '#')) {
10162          /* Timeout */
10163          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10164          res = 0;
10165          goto out;
10166       } else if (cmd < 0) {
10167          /* Hangup */
10168          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10169          res = -1;
10170          goto out;
10171       }
10172    }
10173 #ifdef IMAP_STORAGE
10174       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10175       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10176          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10177          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10178       }
10179       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10180       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10181          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10182          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10183       }
10184 #endif
10185 
10186    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10187    if (play_auto) {
10188       cmd = '1';
10189    } else {
10190       cmd = vm_intro(chan, vmu, &vms);
10191    }
10192    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10193 
10194    vms.repeats = 0;
10195    vms.starting = 1;
10196    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10197       /* Run main menu */
10198       switch (cmd) {
10199       case '1': /* First message */
10200          vms.curmsg = 0;
10201          /* Fall through */
10202       case '5': /* Play current message */
10203          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10204          cmd = vm_browse_messages(chan, &vms, vmu);
10205          break;
10206       case '2': /* Change folders */
10207          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10208          if (useadsi)
10209             adsi_folders(chan, 0, "Change to folder...");
10210 
10211          cmd = get_folder2(chan, "vm-changeto", 0);
10212          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10213          if (cmd == '#') {
10214             cmd = 0;
10215          } else if (cmd > 0) {
10216             cmd = cmd - '0';
10217             res = close_mailbox(&vms, vmu);
10218             if (res == ERROR_LOCK_PATH)
10219                goto out;
10220             /* If folder is not urgent, set in_urgent to zero! */
10221             if (cmd != 11) in_urgent = 0;
10222             res = open_mailbox(&vms, vmu, cmd);
10223             if (res < 0)
10224                goto out;
10225             play_folder = cmd;
10226             cmd = 0;
10227          }
10228          if (useadsi)
10229             adsi_status2(chan, &vms);
10230 
10231          if (!cmd) {
10232             cmd = vm_play_folder_name(chan, vms.vmbox);
10233          }
10234 
10235          vms.starting = 1;
10236          vms.curmsg = 0;
10237          break;
10238       case '3': /* Advanced options */
10239          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10240          cmd = 0;
10241          vms.repeats = 0;
10242          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10243             switch (cmd) {
10244             case '1': /* Reply */
10245                if (vms.lastmsg > -1 && !vms.starting) {
10246                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10247                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10248                      res = cmd;
10249                      goto out;
10250                   }
10251                } else {
10252                   cmd = ast_play_and_wait(chan, "vm-sorry");
10253                }
10254                cmd = 't';
10255                break;
10256             case '2': /* Callback */
10257                if (!vms.starting)
10258                   ast_verb(3, "Callback Requested\n");
10259                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10260                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10261                   if (cmd == 9) {
10262                      silentexit = 1;
10263                      goto out;
10264                   } else if (cmd == ERROR_LOCK_PATH) {
10265                      res = cmd;
10266                      goto out;
10267                   }
10268                } else {
10269                   cmd = ast_play_and_wait(chan, "vm-sorry");
10270                }
10271                cmd = 't';
10272                break;
10273             case '3': /* Envelope */
10274                if (vms.lastmsg > -1 && !vms.starting) {
10275                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10276                   if (cmd == ERROR_LOCK_PATH) {
10277                      res = cmd;
10278                      goto out;
10279                   }
10280                } else {
10281                   cmd = ast_play_and_wait(chan, "vm-sorry");
10282                }
10283                cmd = 't';
10284                break;
10285             case '4': /* Dialout */
10286                if (!ast_strlen_zero(vmu->dialout)) {
10287                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10288                   if (cmd == 9) {
10289                      silentexit = 1;
10290                      goto out;
10291                   }
10292                } else {
10293                   cmd = ast_play_and_wait(chan, "vm-sorry");
10294                }
10295                cmd = 't';
10296                break;
10297 
10298             case '5': /* Leave VoiceMail */
10299                if (ast_test_flag(vmu, VM_SVMAIL)) {
10300                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10301                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10302                      res = cmd;
10303                      goto out;
10304                   }
10305                } else {
10306                   cmd = ast_play_and_wait(chan, "vm-sorry");
10307                }
10308                cmd = 't';
10309                break;
10310 
10311             case '*': /* Return to main menu */
10312                cmd = 't';
10313                break;
10314 
10315             default:
10316                cmd = 0;
10317                if (!vms.starting) {
10318                   cmd = ast_play_and_wait(chan, "vm-toreply");
10319                }
10320                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10321                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10322                }
10323                if (!cmd && !vms.starting) {
10324                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10325                }
10326                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10327                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10328                }
10329                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10330                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10331                }
10332                if (!cmd) {
10333                   cmd = ast_play_and_wait(chan, "vm-starmain");
10334                }
10335                if (!cmd) {
10336                   cmd = ast_waitfordigit(chan, 6000);
10337                }
10338                if (!cmd) {
10339                   vms.repeats++;
10340                }
10341                if (vms.repeats > 3) {
10342                   cmd = 't';
10343                }
10344                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10345             }
10346          }
10347          if (cmd == 't') {
10348             cmd = 0;
10349             vms.repeats = 0;
10350          }
10351          break;
10352       case '4': /* Go to the previous message */
10353          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10354          if (vms.curmsg > 0) {
10355             vms.curmsg--;
10356             cmd = play_message(chan, vmu, &vms);
10357          } else {
10358             /* Check if we were listening to new
10359                messages.  If so, go to Urgent messages
10360                instead of saying "no more messages"
10361             */
10362             if (in_urgent == 0 && vms.urgentmessages > 0) {
10363                /* Check for Urgent messages */
10364                in_urgent = 1;
10365                res = close_mailbox(&vms, vmu);
10366                if (res == ERROR_LOCK_PATH)
10367                   goto out;
10368                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10369                if (res < 0)
10370                   goto out;
10371                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10372                vms.curmsg = vms.lastmsg;
10373                if (vms.lastmsg < 0) {
10374                   cmd = ast_play_and_wait(chan, "vm-nomore");
10375                }
10376             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10377                vms.curmsg = vms.lastmsg;
10378                cmd = play_message(chan, vmu, &vms);
10379             } else {
10380                cmd = ast_play_and_wait(chan, "vm-nomore");
10381             }
10382          }
10383          break;
10384       case '6': /* Go to the next message */
10385          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10386          if (vms.curmsg < vms.lastmsg) {
10387             vms.curmsg++;
10388             cmd = play_message(chan, vmu, &vms);
10389          } else {
10390             if (in_urgent && vms.newmessages > 0) {
10391                /* Check if we were listening to urgent
10392                 * messages.  If so, go to regular new messages
10393                 * instead of saying "no more messages"
10394                 */
10395                in_urgent = 0;
10396                res = close_mailbox(&vms, vmu);
10397                if (res == ERROR_LOCK_PATH)
10398                   goto out;
10399                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10400                if (res < 0)
10401                   goto out;
10402                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10403                vms.curmsg = -1;
10404                if (vms.lastmsg < 0) {
10405                   cmd = ast_play_and_wait(chan, "vm-nomore");
10406                }
10407             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10408                vms.curmsg = 0;
10409                cmd = play_message(chan, vmu, &vms);
10410             } else {
10411                cmd = ast_play_and_wait(chan, "vm-nomore");
10412             }
10413          }
10414          break;
10415       case '7': /* Delete the current message */
10416          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10417             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10418             if (useadsi)
10419                adsi_delete(chan, &vms);
10420             if (vms.deleted[vms.curmsg]) {
10421                if (play_folder == 0) {
10422                   if (in_urgent) {
10423                      vms.urgentmessages--;
10424                   } else {
10425                      vms.newmessages--;
10426                   }
10427                }
10428                else if (play_folder == 1)
10429                   vms.oldmessages--;
10430                cmd = ast_play_and_wait(chan, "vm-deleted");
10431             } else {
10432                if (play_folder == 0) {
10433                   if (in_urgent) {
10434                      vms.urgentmessages++;
10435                   } else {
10436                      vms.newmessages++;
10437                   }
10438                }
10439                else if (play_folder == 1)
10440                   vms.oldmessages++;
10441                cmd = ast_play_and_wait(chan, "vm-undeleted");
10442             }
10443             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10444                if (vms.curmsg < vms.lastmsg) {
10445                   vms.curmsg++;
10446                   cmd = play_message(chan, vmu, &vms);
10447                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10448                   vms.curmsg = 0;
10449                   cmd = play_message(chan, vmu, &vms);
10450                } else {
10451                   /* Check if we were listening to urgent
10452                      messages.  If so, go to regular new messages
10453                      instead of saying "no more messages"
10454                   */
10455                   if (in_urgent == 1) {
10456                      /* Check for new messages */
10457                      in_urgent = 0;
10458                      res = close_mailbox(&vms, vmu);
10459                      if (res == ERROR_LOCK_PATH)
10460                         goto out;
10461                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10462                      if (res < 0)
10463                         goto out;
10464                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10465                      vms.curmsg = -1;
10466                      if (vms.lastmsg < 0) {
10467                         cmd = ast_play_and_wait(chan, "vm-nomore");
10468                      }
10469                   } else {
10470                      cmd = ast_play_and_wait(chan, "vm-nomore");
10471                   }
10472                }
10473             }
10474          } else /* Delete not valid if we haven't selected a message */
10475             cmd = 0;
10476 #ifdef IMAP_STORAGE
10477          deleted = 1;
10478 #endif
10479          break;
10480    
10481       case '8': /* Forward the current message */
10482          if (vms.lastmsg > -1) {
10483             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10484             if (cmd == ERROR_LOCK_PATH) {
10485                res = cmd;
10486                goto out;
10487             }
10488          } else {
10489             /* Check if we were listening to urgent
10490                messages.  If so, go to regular new messages
10491                instead of saying "no more messages"
10492             */
10493             if (in_urgent == 1 && vms.newmessages > 0) {
10494                /* Check for new messages */
10495                in_urgent = 0;
10496                res = close_mailbox(&vms, vmu);
10497                if (res == ERROR_LOCK_PATH)
10498                   goto out;
10499                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10500                if (res < 0)
10501                   goto out;
10502                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10503                vms.curmsg = -1;
10504                if (vms.lastmsg < 0) {
10505                   cmd = ast_play_and_wait(chan, "vm-nomore");
10506                }
10507             } else {
10508                cmd = ast_play_and_wait(chan, "vm-nomore");
10509             }
10510          }
10511          break;
10512       case '9': /* Save message to folder */
10513          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10514          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10515             /* No message selected */
10516             cmd = 0;
10517             break;
10518          }
10519          if (useadsi)
10520             adsi_folders(chan, 1, "Save to folder...");
10521          cmd = get_folder2(chan, "vm-savefolder", 1);
10522          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10523          box = 0; /* Shut up compiler */
10524          if (cmd == '#') {
10525             cmd = 0;
10526             break;
10527          } else if (cmd > 0) {
10528             box = cmd = cmd - '0';
10529             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10530             if (cmd == ERROR_LOCK_PATH) {
10531                res = cmd;
10532                goto out;
10533 #ifndef IMAP_STORAGE
10534             } else if (!cmd) {
10535                vms.deleted[vms.curmsg] = 1;
10536 #endif
10537             } else {
10538                vms.deleted[vms.curmsg] = 0;
10539                vms.heard[vms.curmsg] = 0;
10540             }
10541          }
10542          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10543          if (useadsi)
10544             adsi_message(chan, &vms);
10545          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10546          if (!cmd) {
10547             cmd = ast_play_and_wait(chan, "vm-message");
10548             if (!cmd) 
10549                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10550             if (!cmd)
10551                cmd = ast_play_and_wait(chan, "vm-savedto");
10552             if (!cmd)
10553                cmd = vm_play_folder_name(chan, vms.fn);
10554          } else {
10555             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10556          }
10557          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10558             if (vms.curmsg < vms.lastmsg) {
10559                vms.curmsg++;
10560                cmd = play_message(chan, vmu, &vms);
10561             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10562                vms.curmsg = 0;
10563                cmd = play_message(chan, vmu, &vms);
10564             } else {
10565                /* Check if we were listening to urgent
10566                   messages.  If so, go to regular new messages
10567                   instead of saying "no more messages"
10568                */
10569                if (in_urgent == 1 && vms.newmessages > 0) {
10570                   /* Check for new messages */
10571                   in_urgent = 0;
10572                   res = close_mailbox(&vms, vmu);
10573                   if (res == ERROR_LOCK_PATH)
10574                      goto out;
10575                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10576                   if (res < 0)
10577                      goto out;
10578                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10579                   vms.curmsg = -1;
10580                   if (vms.lastmsg < 0) {
10581                      cmd = ast_play_and_wait(chan, "vm-nomore");
10582                   }
10583                } else {
10584                   cmd = ast_play_and_wait(chan, "vm-nomore");
10585                }
10586             }
10587          }
10588          break;
10589       case '*': /* Help */
10590          if (!vms.starting) {
10591             cmd = ast_play_and_wait(chan, "vm-onefor");
10592             if (!strncasecmp(chan->language, "he", 2)) {
10593                cmd = ast_play_and_wait(chan, "vm-for");
10594             }
10595             if (!cmd)
10596                cmd = vm_play_folder_name(chan, vms.vmbox);
10597             if (!cmd)
10598                cmd = ast_play_and_wait(chan, "vm-opts");
10599             if (!cmd)
10600                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10601          } else
10602             cmd = 0;
10603          break;
10604       case '0': /* Mailbox options */
10605          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10606          if (useadsi)
10607             adsi_status(chan, &vms);
10608          break;
10609       default: /* Nothing */
10610          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10611          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10612          break;
10613       }
10614    }
10615    if ((cmd == 't') || (cmd == '#')) {
10616       /* Timeout */
10617       res = 0;
10618    } else {
10619       /* Hangup */
10620       res = -1;
10621    }
10622 
10623 out:
10624    if (res > -1) {
10625       ast_stopstream(chan);
10626       adsi_goodbye(chan);
10627       if (valid && res != OPERATOR_EXIT) {
10628          if (silentexit)
10629             res = ast_play_and_wait(chan, "vm-dialout");
10630          else 
10631             res = ast_play_and_wait(chan, "vm-goodbye");
10632       }
10633       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10634          res = 0;
10635       }
10636       if (useadsi)
10637          ast_adsi_unload_session(chan);
10638    }
10639    if (vmu)
10640       close_mailbox(&vms, vmu);
10641    if (valid) {
10642       int new = 0, old = 0, urgent = 0;
10643       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10644       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10645       /* Urgent flag not passwd to externnotify here */
10646       run_externnotify(vmu->context, vmu->mailbox, NULL);
10647       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10648       queue_mwi_event(ext_context, urgent, new, old);
10649    }
10650 #ifdef IMAP_STORAGE
10651    /* expunge message - use UID Expunge if supported on IMAP server*/
10652    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10653    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10654       ast_mutex_lock(&vms.lock);
10655 #ifdef HAVE_IMAP_TK2006
10656       if (LEVELUIDPLUS (vms.mailstream)) {
10657          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10658       } else 
10659 #endif
10660          mail_expunge(vms.mailstream);
10661       ast_mutex_unlock(&vms.lock);
10662    }
10663    /*  before we delete the state, we should copy pertinent info
10664     *  back to the persistent model */
10665    if (vmu) {
10666       vmstate_delete(&vms);
10667    }
10668 #endif
10669    if (vmu)
10670       free_user(vmu);
10671 
10672 #ifdef IMAP_STORAGE
10673    pthread_setspecific(ts_vmstate.key, NULL);
10674 #endif
10675    return res;
10676 }
10677 
10678 static int vm_exec(struct ast_channel *chan, const char *data)
10679 {
10680    int res = 0;
10681    char *tmp;
10682    struct leave_vm_options leave_options;
10683    struct ast_flags flags = { 0 };
10684    char *opts[OPT_ARG_ARRAY_SIZE];
10685    AST_DECLARE_APP_ARGS(args,
10686       AST_APP_ARG(argv0);
10687       AST_APP_ARG(argv1);
10688    );
10689    
10690    memset(&leave_options, 0, sizeof(leave_options));
10691 
10692    if (chan->_state != AST_STATE_UP)
10693       ast_answer(chan);
10694 
10695    if (!ast_strlen_zero(data)) {
10696       tmp = ast_strdupa(data);
10697       AST_STANDARD_APP_ARGS(args, tmp);
10698       if (args.argc == 2) {
10699          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10700             return -1;
10701          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10702          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10703             int gain;
10704 
10705             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10706                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10707                return -1;
10708             } else {
10709                leave_options.record_gain = (signed char) gain;
10710             }
10711          }
10712          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10713             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10714                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10715          }
10716       }
10717    } else {
10718       char temp[256];
10719       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10720       if (res < 0)
10721          return res;
10722       if (ast_strlen_zero(temp))
10723          return 0;
10724       args.argv0 = ast_strdupa(temp);
10725    }
10726 
10727    res = leave_voicemail(chan, args.argv0, &leave_options);
10728    if (res == 't') {
10729       ast_play_and_wait(chan, "vm-goodbye");
10730       res = 0;
10731    }
10732 
10733    if (res == OPERATOR_EXIT) {
10734       res = 0;
10735    }
10736 
10737    if (res == ERROR_LOCK_PATH) {
10738       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10739       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10740       res = 0;
10741    }
10742 
10743    return res;
10744 }
10745 
10746 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10747 {
10748    struct ast_vm_user *vmu;
10749 
10750    if (!ast_strlen_zero(box) && box[0] == '*') {
10751       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10752             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10753             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10754             "\n\tand will be ignored.\n", box, context);
10755       return NULL;
10756    }
10757 
10758    AST_LIST_TRAVERSE(&users, vmu, list) {
10759       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10760          if (strcasecmp(vmu->context, context)) {
10761             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10762                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10763                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10764                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10765          }
10766          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10767          return NULL;
10768       }
10769       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10770          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10771          return NULL;
10772       }
10773    }
10774    
10775    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10776       return NULL;
10777    
10778    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10779    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10780 
10781    AST_LIST_INSERT_TAIL(&users, vmu, list);
10782    
10783    return vmu;
10784 }
10785 
10786 static int append_mailbox(const char *context, const char *box, const char *data)
10787 {
10788    /* Assumes lock is already held */
10789    char *tmp;
10790    char *stringp;
10791    char *s;
10792    struct ast_vm_user *vmu;
10793    char *mailbox_full;
10794    int new = 0, old = 0, urgent = 0;
10795    char secretfn[PATH_MAX] = "";
10796 
10797    tmp = ast_strdupa(data);
10798 
10799    if (!(vmu = find_or_create(context, box)))
10800       return -1;
10801 
10802    populate_defaults(vmu);
10803 
10804    stringp = tmp;
10805    if ((s = strsep(&stringp, ","))) {
10806       if (!ast_strlen_zero(s) && s[0] == '*') {
10807          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10808             "\n\tmust be reset in voicemail.conf.\n", box);
10809       }
10810       /* assign password regardless of validity to prevent NULL password from being assigned */
10811       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10812    }
10813    if (stringp && (s = strsep(&stringp, ","))) {
10814       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10815    }
10816    if (stringp && (s = strsep(&stringp, ","))) {
10817       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10818    }
10819    if (stringp && (s = strsep(&stringp, ","))) {
10820       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10821    }
10822    if (stringp && (s = strsep(&stringp, ","))) {
10823       apply_options(vmu, s);
10824    }
10825 
10826    switch (vmu->passwordlocation) {
10827    case OPT_PWLOC_SPOOLDIR:
10828       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10829       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10830    }
10831 
10832    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10833    strcpy(mailbox_full, box);
10834    strcat(mailbox_full, "@");
10835    strcat(mailbox_full, context);
10836 
10837    inboxcount2(mailbox_full, &urgent, &new, &old);
10838    queue_mwi_event(mailbox_full, urgent, new, old);
10839 
10840    return 0;
10841 }
10842 
10843 AST_TEST_DEFINE(test_voicemail_vmuser)
10844 {
10845    int res = 0;
10846    struct ast_vm_user *vmu;
10847    /* language parameter seems to only be used for display in manager action */
10848    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10849       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10850       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10851       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10852       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10853       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10854       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10855       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10856       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10857 #ifdef IMAP_STORAGE
10858    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10859       "imapfolder=INBOX|imapvmshareid=6000";
10860 #endif
10861 
10862    switch (cmd) {
10863    case TEST_INIT:
10864       info->name = "vmuser";
10865       info->category = "/apps/app_voicemail/";
10866       info->summary = "Vmuser unit test";
10867       info->description =
10868          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10869       return AST_TEST_NOT_RUN;
10870    case TEST_EXECUTE:
10871       break;
10872    }
10873 
10874    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10875       return AST_TEST_NOT_RUN;
10876    }
10877    populate_defaults(vmu);
10878    ast_set_flag(vmu, VM_ALLOCED);
10879 
10880    apply_options(vmu, options_string);
10881 
10882    if (!ast_test_flag(vmu, VM_ATTACH)) {
10883       ast_test_status_update(test, "Parse failure for attach option\n");
10884       res = 1;
10885    }
10886    if (strcasecmp(vmu->attachfmt, "wav49")) {
10887       ast_test_status_update(test, "Parse failure for attachftm option\n");
10888       res = 1;
10889    }
10890    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10891       ast_test_status_update(test, "Parse failure for serveremail option\n");
10892       res = 1;
10893    }
10894    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10895       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10896       res = 1;
10897    }
10898    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10899       ast_test_status_update(test, "Parse failure for emailbody option\n");
10900       res = 1;
10901    }
10902    if (strcasecmp(vmu->zonetag, "central")) {
10903       ast_test_status_update(test, "Parse failure for tz option\n");
10904       res = 1;
10905    }
10906    if (!ast_test_flag(vmu, VM_DELETE)) {
10907       ast_test_status_update(test, "Parse failure for delete option\n");
10908       res = 1;
10909    }
10910    if (!ast_test_flag(vmu, VM_SAYCID)) {
10911       ast_test_status_update(test, "Parse failure for saycid option\n");
10912       res = 1;
10913    }
10914    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10915       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10916       res = 1;
10917    }
10918    if (!ast_test_flag(vmu, VM_REVIEW)) {
10919       ast_test_status_update(test, "Parse failure for review option\n");
10920       res = 1;
10921    }
10922    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10923       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10924       res = 1;
10925    }
10926    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10927       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10928       res = 1;
10929    }
10930    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10931       ast_test_status_update(test, "Parse failure for operator option\n");
10932       res = 1;
10933    }
10934    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10935       ast_test_status_update(test, "Parse failure for envelope option\n");
10936       res = 1;
10937    }
10938    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10939       ast_test_status_update(test, "Parse failure for moveheard option\n");
10940       res = 1;
10941    }
10942    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10943       ast_test_status_update(test, "Parse failure for sayduration option\n");
10944       res = 1;
10945    }
10946    if (vmu->saydurationm != 5) {
10947       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10948       res = 1;
10949    }
10950    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10951       ast_test_status_update(test, "Parse failure for forcename option\n");
10952       res = 1;
10953    }
10954    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10955       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10956       res = 1;
10957    }
10958    if (strcasecmp(vmu->callback, "somecontext")) {
10959       ast_test_status_update(test, "Parse failure for callbacks option\n");
10960       res = 1;
10961    }
10962    if (strcasecmp(vmu->dialout, "somecontext2")) {
10963       ast_test_status_update(test, "Parse failure for dialout option\n");
10964       res = 1;
10965    }
10966    if (strcasecmp(vmu->exit, "somecontext3")) {
10967       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10968       res = 1;
10969    }
10970    if (vmu->minsecs != 10) {
10971       ast_test_status_update(test, "Parse failure for minsecs option\n");
10972       res = 1;
10973    }
10974    if (vmu->maxsecs != 100) {
10975       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10976       res = 1;
10977    }
10978    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10979       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10980       res = 1;
10981    }
10982    if (vmu->maxdeletedmsg != 50) {
10983       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10984       res = 1;
10985    }
10986    if (vmu->volgain != 1.3) {
10987       ast_test_status_update(test, "Parse failure for volgain option\n");
10988       res = 1;
10989    }
10990    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10991       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10992       res = 1;
10993    }
10994 #ifdef IMAP_STORAGE
10995    apply_options(vmu, option_string2);
10996 
10997    if (strcasecmp(vmu->imapuser, "imapuser")) {
10998       ast_test_status_update(test, "Parse failure for imapuser option\n");
10999       res = 1;
11000    }
11001    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11002       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11003       res = 1;
11004    }
11005    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11006       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11007       res = 1;
11008    }
11009    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11010       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11011       res = 1;
11012    }
11013 #endif
11014 
11015    free_user(vmu);
11016    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11017 }
11018 
11019 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11020 {
11021    struct ast_vm_user svm;
11022    char *context, *box;
11023    AST_DECLARE_APP_ARGS(args,
11024       AST_APP_ARG(mbox);
11025       AST_APP_ARG(options);
11026    );
11027    static int dep_warning = 0;
11028 
11029    if (ast_strlen_zero(data)) {
11030       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11031       return -1;
11032    }
11033 
11034    if (!dep_warning) {
11035       dep_warning = 1;
11036       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11037    }
11038 
11039    box = ast_strdupa(data);
11040 
11041    AST_STANDARD_APP_ARGS(args, box);
11042 
11043    if (args.options) {
11044    }
11045 
11046    if ((context = strchr(args.mbox, '@'))) {
11047       *context = '\0';
11048       context++;
11049    }
11050 
11051    if (find_user(&svm, context, args.mbox)) {
11052       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11053    } else
11054       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11055 
11056    return 0;
11057 }
11058 
11059 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11060 {
11061    struct ast_vm_user svm;
11062    AST_DECLARE_APP_ARGS(arg,
11063       AST_APP_ARG(mbox);
11064       AST_APP_ARG(context);
11065    );
11066 
11067    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11068 
11069    if (ast_strlen_zero(arg.mbox)) {
11070       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11071       return -1;
11072    }
11073 
11074    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11075    return 0;
11076 }
11077 
11078 static struct ast_custom_function mailbox_exists_acf = {
11079    .name = "MAILBOX_EXISTS",
11080    .read = acf_mailbox_exists,
11081 };
11082 
11083 static int vmauthenticate(struct ast_channel *chan, const char *data)
11084 {
11085    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11086    struct ast_vm_user vmus;
11087    char *options = NULL;
11088    int silent = 0, skipuser = 0;
11089    int res = -1;
11090    
11091    if (data) {
11092       s = ast_strdupa(data);
11093       user = strsep(&s, ",");
11094       options = strsep(&s, ",");
11095       if (user) {
11096          s = user;
11097          user = strsep(&s, "@");
11098          context = strsep(&s, "");
11099          if (!ast_strlen_zero(user))
11100             skipuser++;
11101          ast_copy_string(mailbox, user, sizeof(mailbox));
11102       }
11103    }
11104 
11105    if (options) {
11106       silent = (strchr(options, 's')) != NULL;
11107    }
11108 
11109    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11110       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11111       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11112       ast_play_and_wait(chan, "auth-thankyou");
11113       res = 0;
11114    } else if (mailbox[0] == '*') {
11115       /* user entered '*' */
11116       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11117          res = 0; /* prevent hangup */
11118       }
11119    }
11120 
11121    return res;
11122 }
11123 
11124 static char *show_users_realtime(int fd, const char *context)
11125 {
11126    struct ast_config *cfg;
11127    const char *cat = NULL;
11128 
11129    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11130       "context", context, SENTINEL))) {
11131       return CLI_FAILURE;
11132    }
11133 
11134    ast_cli(fd,
11135       "\n"
11136       "=============================================================\n"
11137       "=== Configured Voicemail Users ==============================\n"
11138       "=============================================================\n"
11139       "===\n");
11140 
11141    while ((cat = ast_category_browse(cfg, cat))) {
11142       struct ast_variable *var = NULL;
11143       ast_cli(fd,
11144          "=== Mailbox ...\n"
11145          "===\n");
11146       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11147          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11148       ast_cli(fd,
11149          "===\n"
11150          "=== ---------------------------------------------------------\n"
11151          "===\n");
11152    }
11153 
11154    ast_cli(fd,
11155       "=============================================================\n"
11156       "\n");
11157 
11158    ast_config_destroy(cfg);
11159 
11160    return CLI_SUCCESS;
11161 }
11162 
11163 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11164 {
11165    int which = 0;
11166    int wordlen;
11167    struct ast_vm_user *vmu;
11168    const char *context = "";
11169 
11170    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11171    if (pos > 4)
11172       return NULL;
11173    if (pos == 3)
11174       return (state == 0) ? ast_strdup("for") : NULL;
11175    wordlen = strlen(word);
11176    AST_LIST_TRAVERSE(&users, vmu, list) {
11177       if (!strncasecmp(word, vmu->context, wordlen)) {
11178          if (context && strcmp(context, vmu->context) && ++which > state)
11179             return ast_strdup(vmu->context);
11180          /* ignore repeated contexts ? */
11181          context = vmu->context;
11182       }
11183    }
11184    return NULL;
11185 }
11186 
11187 /*! \brief Show a list of voicemail users in the CLI */
11188 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11189 {
11190    struct ast_vm_user *vmu;
11191 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11192    const char *context = NULL;
11193    int users_counter = 0;
11194 
11195    switch (cmd) {
11196    case CLI_INIT:
11197       e->command = "voicemail show users";
11198       e->usage =
11199          "Usage: voicemail show users [for <context>]\n"
11200          "       Lists all mailboxes currently set up\n";
11201       return NULL;
11202    case CLI_GENERATE:
11203       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11204    }  
11205 
11206    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11207       return CLI_SHOWUSAGE;
11208    if (a->argc == 5) {
11209       if (strcmp(a->argv[3],"for"))
11210          return CLI_SHOWUSAGE;
11211       context = a->argv[4];
11212    }
11213 
11214    if (ast_check_realtime("voicemail")) {
11215       if (!context) {
11216          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11217          return CLI_SHOWUSAGE;
11218       }
11219       return show_users_realtime(a->fd, context);
11220    }
11221 
11222    AST_LIST_LOCK(&users);
11223    if (AST_LIST_EMPTY(&users)) {
11224       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11225       AST_LIST_UNLOCK(&users);
11226       return CLI_FAILURE;
11227    }
11228    if (!context) {
11229       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11230    } else {
11231       int count = 0;
11232       AST_LIST_TRAVERSE(&users, vmu, list) {
11233          if (!strcmp(context, vmu->context)) {
11234             count++;
11235             break;
11236          }
11237       }
11238       if (count) {
11239          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11240       } else {
11241          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11242          AST_LIST_UNLOCK(&users);
11243          return CLI_FAILURE;
11244       }
11245    }
11246    AST_LIST_TRAVERSE(&users, vmu, list) {
11247       int newmsgs = 0, oldmsgs = 0;
11248       char count[12], tmp[256] = "";
11249 
11250       if (!context || !strcmp(context, vmu->context)) {
11251          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11252          inboxcount(tmp, &newmsgs, &oldmsgs);
11253          snprintf(count, sizeof(count), "%d", newmsgs);
11254          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11255          users_counter++;
11256       }
11257    }
11258    AST_LIST_UNLOCK(&users);
11259    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11260    return CLI_SUCCESS;
11261 }
11262 
11263 /*! \brief Show a list of voicemail zones in the CLI */
11264 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11265 {
11266    struct vm_zone *zone;
11267 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11268    char *res = CLI_SUCCESS;
11269 
11270    switch (cmd) {
11271    case CLI_INIT:
11272       e->command = "voicemail show zones";
11273       e->usage =
11274          "Usage: voicemail show zones\n"
11275          "       Lists zone message formats\n";
11276       return NULL;
11277    case CLI_GENERATE:
11278       return NULL;
11279    }
11280 
11281    if (a->argc != 3)
11282       return CLI_SHOWUSAGE;
11283 
11284    AST_LIST_LOCK(&zones);
11285    if (!AST_LIST_EMPTY(&zones)) {
11286       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11287       AST_LIST_TRAVERSE(&zones, zone, list) {
11288          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11289       }
11290    } else {
11291       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11292       res = CLI_FAILURE;
11293    }
11294    AST_LIST_UNLOCK(&zones);
11295 
11296    return res;
11297 }
11298 
11299 /*! \brief Reload voicemail configuration from the CLI */
11300 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11301 {
11302    switch (cmd) {
11303    case CLI_INIT:
11304       e->command = "voicemail reload";
11305       e->usage =
11306          "Usage: voicemail reload\n"
11307          "       Reload voicemail configuration\n";
11308       return NULL;
11309    case CLI_GENERATE:
11310       return NULL;
11311    }
11312 
11313    if (a->argc != 2)
11314       return CLI_SHOWUSAGE;
11315 
11316    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11317    load_config(1);
11318    
11319    return CLI_SUCCESS;
11320 }
11321 
11322 static struct ast_cli_entry cli_voicemail[] = {
11323    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11324    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11325    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11326 };
11327 
11328 #ifdef IMAP_STORAGE
11329    #define DATA_EXPORT_VM_USERS(USER)              \
11330       USER(ast_vm_user, context, AST_DATA_STRING)        \
11331       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11332       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11333       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11334       USER(ast_vm_user, email, AST_DATA_STRING)       \
11335       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11336       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11337       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11338       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11339       USER(ast_vm_user, language, AST_DATA_STRING)       \
11340       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11341       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11342       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11343       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11344       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11345       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11346       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11347       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11348       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11349       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11350       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11351       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11352       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11353       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11354       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11355 #else
11356    #define DATA_EXPORT_VM_USERS(USER)              \
11357       USER(ast_vm_user, context, AST_DATA_STRING)        \
11358       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11359       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11360       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11361       USER(ast_vm_user, email, AST_DATA_STRING)       \
11362       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11363       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11364       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11365       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11366       USER(ast_vm_user, language, AST_DATA_STRING)       \
11367       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11368       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11369       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11370       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11371       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11372       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11373       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11374       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11375       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11376       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11377       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11378       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11379 #endif
11380 
11381 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11382 
11383 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11384    ZONE(vm_zone, name, AST_DATA_STRING)      \
11385    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11386    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11387 
11388 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11389 
11390 /*!
11391  * \internal
11392  * \brief Add voicemail user to the data_root.
11393  * \param[in] search The search tree.
11394  * \param[in] data_root The main result node.
11395  * \param[in] user The voicemail user.
11396  */
11397 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11398     struct ast_data *data_root, struct ast_vm_user *user)
11399 {
11400    struct ast_data *data_user, *data_zone;
11401    struct ast_data *data_state;
11402    struct vm_zone *zone = NULL;
11403    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11404    char ext_context[256] = "";
11405 
11406    data_user = ast_data_add_node(data_root, "user");
11407    if (!data_user) {
11408       return -1;
11409    }
11410 
11411    ast_data_add_structure(ast_vm_user, data_user, user);
11412 
11413    AST_LIST_LOCK(&zones);
11414    AST_LIST_TRAVERSE(&zones, zone, list) {
11415       if (!strcmp(zone->name, user->zonetag)) {
11416          break;
11417       }
11418    }
11419    AST_LIST_UNLOCK(&zones);
11420 
11421    /* state */
11422    data_state = ast_data_add_node(data_user, "state");
11423    if (!data_state) {
11424       return -1;
11425    }
11426    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11427    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11428    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11429    ast_data_add_int(data_state, "newmsg", newmsg);
11430    ast_data_add_int(data_state, "oldmsg", oldmsg);
11431 
11432    if (zone) {
11433       data_zone = ast_data_add_node(data_user, "zone");
11434       ast_data_add_structure(vm_zone, data_zone, zone);
11435    }
11436 
11437    if (!ast_data_search_match(search, data_user)) {
11438       ast_data_remove_node(data_root, data_user);
11439    }
11440 
11441    return 0;
11442 }
11443 
11444 static int vm_users_data_provider_get(const struct ast_data_search *search,
11445    struct ast_data *data_root)
11446 {
11447    struct ast_vm_user *user;
11448 
11449    AST_LIST_LOCK(&users);
11450    AST_LIST_TRAVERSE(&users, user, list) {
11451       vm_users_data_provider_get_helper(search, data_root, user);
11452    }
11453    AST_LIST_UNLOCK(&users);
11454 
11455    return 0;
11456 }
11457 
11458 static const struct ast_data_handler vm_users_data_provider = {
11459    .version = AST_DATA_HANDLER_VERSION,
11460    .get = vm_users_data_provider_get
11461 };
11462 
11463 static const struct ast_data_entry vm_data_providers[] = {
11464    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11465 };
11466 
11467 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11468 {
11469    int new = 0, old = 0, urgent = 0;
11470 
11471    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11472 
11473    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11474       mwi_sub->old_urgent = urgent;
11475       mwi_sub->old_new = new;
11476       mwi_sub->old_old = old;
11477       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11478       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11479    }
11480 }
11481 
11482 static void poll_subscribed_mailboxes(void)
11483 {
11484    struct mwi_sub *mwi_sub;
11485 
11486    AST_RWLIST_RDLOCK(&mwi_subs);
11487    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11488       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11489          poll_subscribed_mailbox(mwi_sub);
11490       }
11491    }
11492    AST_RWLIST_UNLOCK(&mwi_subs);
11493 }
11494 
11495 static void *mb_poll_thread(void *data)
11496 {
11497    while (poll_thread_run) {
11498       struct timespec ts = { 0, };
11499       struct timeval wait;
11500 
11501       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11502       ts.tv_sec = wait.tv_sec;
11503       ts.tv_nsec = wait.tv_usec * 1000;
11504 
11505       ast_mutex_lock(&poll_lock);
11506       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11507       ast_mutex_unlock(&poll_lock);
11508 
11509       if (!poll_thread_run)
11510          break;
11511 
11512       poll_subscribed_mailboxes();
11513    }
11514 
11515    return NULL;
11516 }
11517 
11518 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11519 {
11520    ast_free(mwi_sub);
11521 }
11522 
11523 static int handle_unsubscribe(void *datap)
11524 {
11525    struct mwi_sub *mwi_sub;
11526    uint32_t *uniqueid = datap;
11527    
11528    AST_RWLIST_WRLOCK(&mwi_subs);
11529    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11530       if (mwi_sub->uniqueid == *uniqueid) {
11531          AST_LIST_REMOVE_CURRENT(entry);
11532          break;
11533       }
11534    }
11535    AST_RWLIST_TRAVERSE_SAFE_END
11536    AST_RWLIST_UNLOCK(&mwi_subs);
11537 
11538    if (mwi_sub)
11539       mwi_sub_destroy(mwi_sub);
11540 
11541    ast_free(uniqueid);  
11542    return 0;
11543 }
11544 
11545 static int handle_subscribe(void *datap)
11546 {
11547    unsigned int len;
11548    struct mwi_sub *mwi_sub;
11549    struct mwi_sub_task *p = datap;
11550 
11551    len = sizeof(*mwi_sub);
11552    if (!ast_strlen_zero(p->mailbox))
11553       len += strlen(p->mailbox);
11554 
11555    if (!ast_strlen_zero(p->context))
11556       len += strlen(p->context) + 1; /* Allow for seperator */
11557 
11558    if (!(mwi_sub = ast_calloc(1, len)))
11559       return -1;
11560 
11561    mwi_sub->uniqueid = p->uniqueid;
11562    if (!ast_strlen_zero(p->mailbox))
11563       strcpy(mwi_sub->mailbox, p->mailbox);
11564 
11565    if (!ast_strlen_zero(p->context)) {
11566       strcat(mwi_sub->mailbox, "@");
11567       strcat(mwi_sub->mailbox, p->context);
11568    }
11569 
11570    AST_RWLIST_WRLOCK(&mwi_subs);
11571    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11572    AST_RWLIST_UNLOCK(&mwi_subs);
11573    ast_free((void *) p->mailbox);
11574    ast_free((void *) p->context);
11575    ast_free(p);
11576    poll_subscribed_mailbox(mwi_sub);
11577    return 0;
11578 }
11579 
11580 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11581 {
11582    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11583 
11584    if (!uniqueid) {
11585       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11586       return;
11587    }
11588 
11589    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11590       ast_free(uniqueid);
11591       return;
11592    }
11593 
11594    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11595       ast_free(uniqueid);
11596       return;
11597    }
11598 
11599    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11600    *uniqueid = u;
11601    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11602       ast_free(uniqueid);
11603    }
11604 }
11605 
11606 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11607 {
11608    struct mwi_sub_task *mwist;
11609    
11610    if (ast_event_get_type(event) != AST_EVENT_SUB)
11611       return;
11612 
11613    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11614       return;
11615 
11616    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11617       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11618       return;
11619    }
11620    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11621    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11622    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11623    
11624    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11625       ast_free(mwist);
11626    }
11627 }
11628 
11629 static void start_poll_thread(void)
11630 {
11631    int errcode;
11632    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11633       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11634       AST_EVENT_IE_END);
11635 
11636    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11637       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11638       AST_EVENT_IE_END);
11639 
11640    if (mwi_sub_sub)
11641       ast_event_report_subs(mwi_sub_sub);
11642 
11643    poll_thread_run = 1;
11644 
11645    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11646       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11647    }
11648 }
11649 
11650 static void stop_poll_thread(void)
11651 {
11652    poll_thread_run = 0;
11653 
11654    if (mwi_sub_sub) {
11655       ast_event_unsubscribe(mwi_sub_sub);
11656       mwi_sub_sub = NULL;
11657    }
11658 
11659    if (mwi_unsub_sub) {
11660       ast_event_unsubscribe(mwi_unsub_sub);
11661       mwi_unsub_sub = NULL;
11662    }
11663 
11664    ast_mutex_lock(&poll_lock);
11665    ast_cond_signal(&poll_cond);
11666    ast_mutex_unlock(&poll_lock);
11667 
11668    pthread_join(poll_thread, NULL);
11669 
11670    poll_thread = AST_PTHREADT_NULL;
11671 }
11672 
11673 /*! \brief Manager list voicemail users command */
11674 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11675 {
11676    struct ast_vm_user *vmu = NULL;
11677    const char *id = astman_get_header(m, "ActionID");
11678    char actionid[128] = "";
11679 
11680    if (!ast_strlen_zero(id))
11681       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11682 
11683    AST_LIST_LOCK(&users);
11684 
11685    if (AST_LIST_EMPTY(&users)) {
11686       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11687       AST_LIST_UNLOCK(&users);
11688       return RESULT_SUCCESS;
11689    }
11690    
11691    astman_send_ack(s, m, "Voicemail user list will follow");
11692    
11693    AST_LIST_TRAVERSE(&users, vmu, list) {
11694       char dirname[256];
11695 
11696 #ifdef IMAP_STORAGE
11697       int new, old;
11698       inboxcount(vmu->mailbox, &new, &old);
11699 #endif
11700       
11701       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11702       astman_append(s,
11703          "%s"
11704          "Event: VoicemailUserEntry\r\n"
11705          "VMContext: %s\r\n"
11706          "VoiceMailbox: %s\r\n"
11707          "Fullname: %s\r\n"
11708          "Email: %s\r\n"
11709          "Pager: %s\r\n"
11710          "ServerEmail: %s\r\n"
11711          "MailCommand: %s\r\n"
11712          "Language: %s\r\n"
11713          "TimeZone: %s\r\n"
11714          "Callback: %s\r\n"
11715          "Dialout: %s\r\n"
11716          "UniqueID: %s\r\n"
11717          "ExitContext: %s\r\n"
11718          "SayDurationMinimum: %d\r\n"
11719          "SayEnvelope: %s\r\n"
11720          "SayCID: %s\r\n"
11721          "AttachMessage: %s\r\n"
11722          "AttachmentFormat: %s\r\n"
11723          "DeleteMessage: %s\r\n"
11724          "VolumeGain: %.2f\r\n"
11725          "CanReview: %s\r\n"
11726          "CallOperator: %s\r\n"
11727          "MaxMessageCount: %d\r\n"
11728          "MaxMessageLength: %d\r\n"
11729          "NewMessageCount: %d\r\n"
11730 #ifdef IMAP_STORAGE
11731          "OldMessageCount: %d\r\n"
11732          "IMAPUser: %s\r\n"
11733 #endif
11734          "\r\n",
11735          actionid,
11736          vmu->context,
11737          vmu->mailbox,
11738          vmu->fullname,
11739          vmu->email,
11740          vmu->pager,
11741          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11742          mailcmd,
11743          vmu->language,
11744          vmu->zonetag,
11745          vmu->callback,
11746          vmu->dialout,
11747          vmu->uniqueid,
11748          vmu->exit,
11749          vmu->saydurationm,
11750          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11751          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11752          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11753          vmu->attachfmt,
11754          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11755          vmu->volgain,
11756          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11757          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11758          vmu->maxmsg,
11759          vmu->maxsecs,
11760 #ifdef IMAP_STORAGE
11761          new, old, vmu->imapuser
11762 #else
11763          count_messages(vmu, dirname)
11764 #endif
11765          );
11766    }     
11767    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11768 
11769    AST_LIST_UNLOCK(&users);
11770 
11771    return RESULT_SUCCESS;
11772 }
11773 
11774 /*! \brief Free the users structure. */
11775 static void free_vm_users(void) 
11776 {
11777    struct ast_vm_user *current;
11778    AST_LIST_LOCK(&users);
11779    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11780       ast_set_flag(current, VM_ALLOCED);
11781       free_user(current);
11782    }
11783    AST_LIST_UNLOCK(&users);
11784 }
11785 
11786 /*! \brief Free the zones structure. */
11787 static void free_vm_zones(void)
11788 {
11789    struct vm_zone *zcur;
11790    AST_LIST_LOCK(&zones);
11791    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11792       free_zone(zcur);
11793    AST_LIST_UNLOCK(&zones);
11794 }
11795 
11796 static const char *substitute_escapes(const char *value)
11797 {
11798    char *current;
11799 
11800    /* Add 16 for fudge factor */
11801    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11802 
11803    ast_str_reset(str);
11804    
11805    /* Substitute strings \r, \n, and \t into the appropriate characters */
11806    for (current = (char *) value; *current; current++) {
11807       if (*current == '\\') {
11808          current++;
11809          if (!*current) {
11810             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11811             break;
11812          }
11813          switch (*current) {
11814          case '\\':
11815             ast_str_append(&str, 0, "\\");
11816             break;
11817          case 'r':
11818             ast_str_append(&str, 0, "\r");
11819             break;
11820          case 'n':
11821 #ifdef IMAP_STORAGE
11822             if (!str->used || str->str[str->used - 1] != '\r') {
11823                ast_str_append(&str, 0, "\r");
11824             }
11825 #endif
11826             ast_str_append(&str, 0, "\n");
11827             break;
11828          case 't':
11829             ast_str_append(&str, 0, "\t");
11830             break;
11831          default:
11832             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11833             break;
11834          }
11835       } else {
11836          ast_str_append(&str, 0, "%c", *current);
11837       }
11838    }
11839 
11840    return ast_str_buffer(str);
11841 }
11842 
11843 static int load_config(int reload)
11844 {
11845    struct ast_config *cfg, *ucfg;
11846    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11847    int res;
11848 
11849    ast_unload_realtime("voicemail");
11850    ast_unload_realtime("voicemail_data");
11851 
11852    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11853       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11854          return 0;
11855       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11856          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11857          ucfg = NULL;
11858       }
11859       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11860       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11861          ast_config_destroy(ucfg);
11862          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11863          return 0;
11864       }
11865    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11866       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11867       return 0;
11868    } else {
11869       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11870       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11871          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11872          ucfg = NULL;
11873       }
11874    }
11875 
11876    res = actual_load_config(reload, cfg, ucfg);
11877 
11878    ast_config_destroy(cfg);
11879    ast_config_destroy(ucfg);
11880 
11881    return res;
11882 }
11883 
11884 #ifdef TEST_FRAMEWORK
11885 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11886 {
11887    ast_unload_realtime("voicemail");
11888    ast_unload_realtime("voicemail_data");
11889    return actual_load_config(reload, cfg, ucfg);
11890 }
11891 #endif
11892 
11893 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11894 {
11895    struct ast_vm_user *current;
11896    char *cat;
11897    struct ast_variable *var;
11898    const char *val;
11899    char *q, *stringp, *tmp;
11900    int x;
11901    unsigned int tmpadsi[4];
11902    char secretfn[PATH_MAX] = "";
11903 
11904 #ifdef IMAP_STORAGE
11905    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11906 #endif
11907    /* set audio control prompts */
11908    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11909    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11910    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11911    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11912    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11913 
11914    /* Free all the users structure */  
11915    free_vm_users();
11916 
11917    /* Free all the zones structure */
11918    free_vm_zones();
11919 
11920    AST_LIST_LOCK(&users);  
11921 
11922    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11923    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11924 
11925    if (cfg) {
11926       /* General settings */
11927 
11928       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11929          val = "default";
11930       ast_copy_string(userscontext, val, sizeof(userscontext));
11931       /* Attach voice message to mail message ? */
11932       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11933          val = "yes";
11934       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11935 
11936       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11937          val = "no";
11938       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11939 
11940       volgain = 0.0;
11941       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11942          sscanf(val, "%30lf", &volgain);
11943 
11944 #ifdef ODBC_STORAGE
11945       strcpy(odbc_database, "asterisk");
11946       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11947          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11948       }
11949       strcpy(odbc_table, "voicemessages");
11950       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11951          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11952       }
11953 #endif      
11954       /* Mail command */
11955       strcpy(mailcmd, SENDMAIL);
11956       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11957          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11958 
11959       maxsilence = 0;
11960       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11961          maxsilence = atoi(val);
11962          if (maxsilence > 0)
11963             maxsilence *= 1000;
11964       }
11965       
11966       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11967          maxmsg = MAXMSG;
11968       } else {
11969          maxmsg = atoi(val);
11970          if (maxmsg < 0) {
11971             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11972             maxmsg = MAXMSG;
11973          } else if (maxmsg > MAXMSGLIMIT) {
11974             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11975             maxmsg = MAXMSGLIMIT;
11976          }
11977       }
11978 
11979       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11980          maxdeletedmsg = 0;
11981       } else {
11982          if (sscanf(val, "%30d", &x) == 1)
11983             maxdeletedmsg = x;
11984          else if (ast_true(val))
11985             maxdeletedmsg = MAXMSG;
11986          else
11987             maxdeletedmsg = 0;
11988 
11989          if (maxdeletedmsg < 0) {
11990             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11991             maxdeletedmsg = MAXMSG;
11992          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11993             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11994             maxdeletedmsg = MAXMSGLIMIT;
11995          }
11996       }
11997 
11998       /* Load date format config for voicemail mail */
11999       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
12000          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
12001       }
12002 
12003       /* Load date format config for voicemail pager mail */
12004       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12005          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12006       }
12007 
12008       /* External password changing command */
12009       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12010          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12011          pwdchange = PWDCHANGE_EXTERNAL;
12012       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12013          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12014          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12015       }
12016  
12017       /* External password validation command */
12018       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12019          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12020          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12021       }
12022 
12023 #ifdef IMAP_STORAGE
12024       /* IMAP server address */
12025       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12026          ast_copy_string(imapserver, val, sizeof(imapserver));
12027       } else {
12028          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12029       }
12030       /* IMAP server port */
12031       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12032          ast_copy_string(imapport, val, sizeof(imapport));
12033       } else {
12034          ast_copy_string(imapport, "143", sizeof(imapport));
12035       }
12036       /* IMAP server flags */
12037       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12038          ast_copy_string(imapflags, val, sizeof(imapflags));
12039       }
12040       /* IMAP server master username */
12041       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12042          ast_copy_string(authuser, val, sizeof(authuser));
12043       }
12044       /* IMAP server master password */
12045       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12046          ast_copy_string(authpassword, val, sizeof(authpassword));
12047       }
12048       /* Expunge on exit */
12049       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12050          if (ast_false(val))
12051             expungeonhangup = 0;
12052          else
12053             expungeonhangup = 1;
12054       } else {
12055          expungeonhangup = 1;
12056       }
12057       /* IMAP voicemail folder */
12058       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12059          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12060       } else {
12061          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12062       }
12063       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12064          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12065       }
12066       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12067          imapgreetings = ast_true(val);
12068       } else {
12069          imapgreetings = 0;
12070       }
12071       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12072          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12073       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12074          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12075          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12076       } else {
12077          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12078       }
12079 
12080       /* There is some very unorthodox casting done here. This is due
12081        * to the way c-client handles the argument passed in. It expects a 
12082        * void pointer and casts the pointer directly to a long without
12083        * first dereferencing it. */
12084       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12085          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12086       } else {
12087          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12088       }
12089 
12090       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12091          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12092       } else {
12093          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12094       }
12095 
12096       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12097          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12098       } else {
12099          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12100       }
12101 
12102       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12103          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12104       } else {
12105          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12106       }
12107 
12108       /* Increment configuration version */
12109       imapversion++;
12110 #endif
12111       /* External voicemail notify application */
12112       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12113          ast_copy_string(externnotify, val, sizeof(externnotify));
12114          ast_debug(1, "found externnotify: %s\n", externnotify);
12115       } else {
12116          externnotify[0] = '\0';
12117       }
12118 
12119       /* SMDI voicemail notification */
12120       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12121          ast_debug(1, "Enabled SMDI voicemail notification\n");
12122          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12123             smdi_iface = ast_smdi_interface_find(val);
12124          } else {
12125             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12126             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12127          }
12128          if (!smdi_iface) {
12129             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12130          } 
12131       }
12132 
12133       /* Silence treshold */
12134       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12135       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12136          silencethreshold = atoi(val);
12137       
12138       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12139          val = ASTERISK_USERNAME;
12140       ast_copy_string(serveremail, val, sizeof(serveremail));
12141       
12142       vmmaxsecs = 0;
12143       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12144          if (sscanf(val, "%30d", &x) == 1) {
12145             vmmaxsecs = x;
12146          } else {
12147             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12148          }
12149       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12150          static int maxmessage_deprecate = 0;
12151          if (maxmessage_deprecate == 0) {
12152             maxmessage_deprecate = 1;
12153             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12154          }
12155          if (sscanf(val, "%30d", &x) == 1) {
12156             vmmaxsecs = x;
12157          } else {
12158             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12159          }
12160       }
12161 
12162       vmminsecs = 0;
12163       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12164          if (sscanf(val, "%30d", &x) == 1) {
12165             vmminsecs = x;
12166             if (maxsilence / 1000 >= vmminsecs) {
12167                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12168             }
12169          } else {
12170             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12171          }
12172       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12173          static int maxmessage_deprecate = 0;
12174          if (maxmessage_deprecate == 0) {
12175             maxmessage_deprecate = 1;
12176             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12177          }
12178          if (sscanf(val, "%30d", &x) == 1) {
12179             vmminsecs = x;
12180             if (maxsilence / 1000 >= vmminsecs) {
12181                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12182             }
12183          } else {
12184             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12185          }
12186       }
12187 
12188       val = ast_variable_retrieve(cfg, "general", "format");
12189       if (!val) {
12190          val = "wav";   
12191       } else {
12192          tmp = ast_strdupa(val);
12193          val = ast_format_str_reduce(tmp);
12194          if (!val) {
12195             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12196             val = "wav";
12197          }
12198       }
12199       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12200 
12201       skipms = 3000;
12202       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12203          if (sscanf(val, "%30d", &x) == 1) {
12204             maxgreet = x;
12205          } else {
12206             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12207          }
12208       }
12209 
12210       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12211          if (sscanf(val, "%30d", &x) == 1) {
12212             skipms = x;
12213          } else {
12214             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12215          }
12216       }
12217 
12218       maxlogins = 3;
12219       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12220          if (sscanf(val, "%30d", &x) == 1) {
12221             maxlogins = x;
12222          } else {
12223             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12224          }
12225       }
12226 
12227       minpassword = MINPASSWORD;
12228       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12229          if (sscanf(val, "%30d", &x) == 1) {
12230             minpassword = x;
12231          } else {
12232             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12233          }
12234       }
12235 
12236       /* Force new user to record name ? */
12237       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12238          val = "no";
12239       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12240 
12241       /* Force new user to record greetings ? */
12242       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12243          val = "no";
12244       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12245 
12246       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12247          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12248          stringp = ast_strdupa(val);
12249          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12250             if (!ast_strlen_zero(stringp)) {
12251                q = strsep(&stringp, ",");
12252                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12253                   q++;
12254                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12255                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12256             } else {
12257                cidinternalcontexts[x][0] = '\0';
12258             }
12259          }
12260       }
12261       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12262          ast_debug(1, "VM Review Option disabled globally\n");
12263          val = "no";
12264       }
12265       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12266 
12267       /* Temporary greeting reminder */
12268       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12269          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12270          val = "no";
12271       } else {
12272          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12273       }
12274       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12275       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12276          ast_debug(1, "VM next message wrap disabled globally\n");
12277          val = "no";
12278       }
12279       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12280 
12281       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12282          ast_debug(1, "VM Operator break disabled globally\n");
12283          val = "no";
12284       }
12285       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12286 
12287       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12288          ast_debug(1, "VM CID Info before msg disabled globally\n");
12289          val = "no";
12290       } 
12291       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12292 
12293       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12294          ast_debug(1, "Send Voicemail msg disabled globally\n");
12295          val = "no";
12296       }
12297       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12298    
12299       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12300          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12301          val = "yes";
12302       }
12303       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12304 
12305       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12306          ast_debug(1, "Move Heard enabled globally\n");
12307          val = "yes";
12308       }
12309       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12310 
12311       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12312          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12313          val = "no";
12314       }
12315       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12316 
12317       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12318          ast_debug(1, "Duration info before msg enabled globally\n");
12319          val = "yes";
12320       }
12321       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12322 
12323       saydurationminfo = 2;
12324       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12325          if (sscanf(val, "%30d", &x) == 1) {
12326             saydurationminfo = x;
12327          } else {
12328             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12329          }
12330       }
12331 
12332       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12333          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12334          val = "no";
12335       }
12336       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12337 
12338       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12339          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12340          ast_debug(1, "found dialout context: %s\n", dialcontext);
12341       } else {
12342          dialcontext[0] = '\0';  
12343       }
12344       
12345       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12346          ast_copy_string(callcontext, val, sizeof(callcontext));
12347          ast_debug(1, "found callback context: %s\n", callcontext);
12348       } else {
12349          callcontext[0] = '\0';
12350       }
12351 
12352       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12353          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12354          ast_debug(1, "found operator context: %s\n", exitcontext);
12355       } else {
12356          exitcontext[0] = '\0';
12357       }
12358       
12359       /* load password sounds configuration */
12360       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12361          ast_copy_string(vm_password, val, sizeof(vm_password));
12362       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12363          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12364       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12365          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12366       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12367          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12368       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12369          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12370       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12371          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12372       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12373          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12374       }
12375       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12376          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12377       }
12378       /* load configurable audio prompts */
12379       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12380          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12381       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12382          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12383       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12384          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12385       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12386          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12387       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12388          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12389 
12390       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12391          val = "no";
12392       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12393 
12394       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12395          val = "voicemail.conf";
12396       }
12397       if (!(strcmp(val, "spooldir"))) {
12398          passwordlocation = OPT_PWLOC_SPOOLDIR;
12399       } else {
12400          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12401       }
12402 
12403       poll_freq = DEFAULT_POLL_FREQ;
12404       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12405          if (sscanf(val, "%30u", &poll_freq) != 1) {
12406             poll_freq = DEFAULT_POLL_FREQ;
12407             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12408          }
12409       }
12410 
12411       poll_mailboxes = 0;
12412       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12413          poll_mailboxes = ast_true(val);
12414 
12415       memset(fromstring, 0, sizeof(fromstring));
12416       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12417       strcpy(charset, "ISO-8859-1");
12418       if (emailbody) {
12419          ast_free(emailbody);
12420          emailbody = NULL;
12421       }
12422       if (emailsubject) {
12423          ast_free(emailsubject);
12424          emailsubject = NULL;
12425       }
12426       if (pagerbody) {
12427          ast_free(pagerbody);
12428          pagerbody = NULL;
12429       }
12430       if (pagersubject) {
12431          ast_free(pagersubject);
12432          pagersubject = NULL;
12433       }
12434       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12435          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12436       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12437          ast_copy_string(fromstring, val, sizeof(fromstring));
12438       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12439          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12440       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12441          ast_copy_string(charset, val, sizeof(charset));
12442       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12443          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12444          for (x = 0; x < 4; x++) {
12445             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12446          }
12447       }
12448       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12449          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12450          for (x = 0; x < 4; x++) {
12451             memcpy(&adsisec[x], &tmpadsi[x], 1);
12452          }
12453       }
12454       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12455          if (atoi(val)) {
12456             adsiver = atoi(val);
12457          }
12458       }
12459       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12460          ast_copy_string(zonetag, val, sizeof(zonetag));
12461       }
12462       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12463          ast_copy_string(locale, val, sizeof(locale));
12464       }
12465       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12466          emailsubject = ast_strdup(substitute_escapes(val));
12467       }
12468       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12469          emailbody = ast_strdup(substitute_escapes(val));
12470       }
12471       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12472          pagersubject = ast_strdup(substitute_escapes(val));
12473       }
12474       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12475          pagerbody = ast_strdup(substitute_escapes(val));
12476       }
12477 
12478       /* load mailboxes from users.conf */
12479       if (ucfg) { 
12480          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12481             if (!strcasecmp(cat, "general")) {
12482                continue;
12483             }
12484             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12485                continue;
12486             if ((current = find_or_create(userscontext, cat))) {
12487                populate_defaults(current);
12488                apply_options_full(current, ast_variable_browse(ucfg, cat));
12489                ast_copy_string(current->context, userscontext, sizeof(current->context));
12490                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12491                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12492                }
12493 
12494                switch (current->passwordlocation) {
12495                case OPT_PWLOC_SPOOLDIR:
12496                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12497                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12498                }
12499             }
12500          }
12501       }
12502 
12503       /* load mailboxes from voicemail.conf */
12504       cat = ast_category_browse(cfg, NULL);
12505       while (cat) {
12506          if (strcasecmp(cat, "general")) {
12507             var = ast_variable_browse(cfg, cat);
12508             if (strcasecmp(cat, "zonemessages")) {
12509                /* Process mailboxes in this context */
12510                while (var) {
12511                   append_mailbox(cat, var->name, var->value);
12512                   var = var->next;
12513                }
12514             } else {
12515                /* Timezones in this context */
12516                while (var) {
12517                   struct vm_zone *z;
12518                   if ((z = ast_malloc(sizeof(*z)))) {
12519                      char *msg_format, *tzone;
12520                      msg_format = ast_strdupa(var->value);
12521                      tzone = strsep(&msg_format, "|,");
12522                      if (msg_format) {
12523                         ast_copy_string(z->name, var->name, sizeof(z->name));
12524                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12525                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12526                         AST_LIST_LOCK(&zones);
12527                         AST_LIST_INSERT_HEAD(&zones, z, list);
12528                         AST_LIST_UNLOCK(&zones);
12529                      } else {
12530                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12531                         ast_free(z);
12532                      }
12533                   } else {
12534                      AST_LIST_UNLOCK(&users);
12535                      return -1;
12536                   }
12537                   var = var->next;
12538                }
12539             }
12540          }
12541          cat = ast_category_browse(cfg, cat);
12542       }
12543 
12544       AST_LIST_UNLOCK(&users);
12545 
12546       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12547          start_poll_thread();
12548       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12549          stop_poll_thread();;
12550 
12551       return 0;
12552    } else {
12553       AST_LIST_UNLOCK(&users);
12554       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12555       return 0;
12556    }
12557 }
12558 
12559 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12560 {
12561    int res = -1;
12562    char dir[PATH_MAX];
12563    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12564    ast_debug(2, "About to try retrieving name file %s\n", dir);
12565    RETRIEVE(dir, -1, mailbox, context);
12566    if (ast_fileexists(dir, NULL, NULL)) {
12567       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12568    }
12569    DISPOSE(dir, -1);
12570    return res;
12571 }
12572 
12573 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12574    struct ast_config *pwconf;
12575    struct ast_flags config_flags = { 0 };
12576 
12577    pwconf = ast_config_load(secretfn, config_flags);
12578    if (valid_config(pwconf)) {
12579       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12580       if (val) {
12581          ast_copy_string(password, val, passwordlen);
12582          ast_config_destroy(pwconf);
12583          return;
12584       }
12585       ast_config_destroy(pwconf);
12586    }
12587    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12588 }
12589 
12590 static int write_password_to_file(const char *secretfn, const char *password) {
12591    struct ast_config *conf;
12592    struct ast_category *cat;
12593    struct ast_variable *var;
12594    int res = -1;
12595 
12596    if (!(conf = ast_config_new())) {
12597       ast_log(LOG_ERROR, "Error creating new config structure\n");
12598       return res;
12599    }
12600    if (!(cat = ast_category_new("general", "", 1))) {
12601       ast_log(LOG_ERROR, "Error creating new category structure\n");
12602       ast_config_destroy(conf);
12603       return res;
12604    }
12605    if (!(var = ast_variable_new("password", password, ""))) {
12606       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12607       ast_config_destroy(conf);
12608       ast_category_destroy(cat);
12609       return res;
12610    }
12611    ast_category_append(conf, cat);
12612    ast_variable_append(cat, var);
12613    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12614       res = 0;
12615    } else {
12616       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12617    }
12618 
12619    ast_config_destroy(conf);
12620    return res;
12621 }
12622 
12623 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12624 {
12625    char *context;
12626    char *args_copy;
12627    int res;
12628 
12629    if (ast_strlen_zero(data)) {
12630       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12631       return -1;
12632    }
12633 
12634    args_copy = ast_strdupa(data);
12635    if ((context = strchr(args_copy, '@'))) {
12636       *context++ = '\0';
12637    } else {
12638       context = "default";
12639    }
12640 
12641    if ((res = sayname(chan, args_copy, context) < 0)) {
12642       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12643       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12644       if (!res) {
12645          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12646       }
12647    }
12648 
12649    return res;
12650 }
12651 
12652 #ifdef TEST_FRAMEWORK
12653 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12654 {
12655    return 0;
12656 }
12657 
12658 static struct ast_frame *fake_read(struct ast_channel *ast)
12659 {
12660    return &ast_null_frame;
12661 }
12662 
12663 AST_TEST_DEFINE(test_voicemail_vmsayname)
12664 {
12665    char dir[PATH_MAX];
12666    char dir2[PATH_MAX];
12667    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12668    static const char TEST_EXTENSION[] = "1234";
12669 
12670    struct ast_channel *test_channel1 = NULL;
12671    int res = -1;
12672 
12673    static const struct ast_channel_tech fake_tech = {
12674       .write = fake_write,
12675       .read = fake_read,
12676    };
12677 
12678    switch (cmd) {
12679    case TEST_INIT:
12680       info->name = "vmsayname_exec";
12681       info->category = "/apps/app_voicemail/";
12682       info->summary = "Vmsayname unit test";
12683       info->description =
12684          "This tests passing various parameters to vmsayname";
12685       return AST_TEST_NOT_RUN;
12686    case TEST_EXECUTE:
12687       break;
12688    }
12689 
12690    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12691         NULL, NULL, 0, 0, "TestChannel1"))) {
12692       goto exit_vmsayname_test;
12693    }
12694 
12695    /* normally this is done in the channel driver */
12696    test_channel1->nativeformats = AST_FORMAT_GSM;
12697    test_channel1->writeformat = AST_FORMAT_GSM;
12698    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12699    test_channel1->readformat = AST_FORMAT_GSM;
12700    test_channel1->rawreadformat = AST_FORMAT_GSM;
12701    test_channel1->tech = &fake_tech;
12702 
12703    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12704    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12705    if (!(res = vmsayname_exec(test_channel1, dir))) {
12706       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12707       if (ast_fileexists(dir, NULL, NULL)) {
12708          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12709          res = -1;
12710          goto exit_vmsayname_test;
12711       } else {
12712          /* no greeting already exists as expected, let's create one to fully test sayname */
12713          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12714             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12715             goto exit_vmsayname_test;
12716          }
12717          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12718          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12719          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12720          if ((res = symlink(dir, dir2))) {
12721             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12722             goto exit_vmsayname_test;
12723          }
12724          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12725          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12726          res = vmsayname_exec(test_channel1, dir);
12727 
12728          /* TODO: there may be a better way to do this */
12729          unlink(dir2);
12730          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12731          rmdir(dir2);
12732          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12733          rmdir(dir2);
12734       }
12735    }
12736 
12737 exit_vmsayname_test:
12738 
12739    if (test_channel1) {
12740       ast_hangup(test_channel1);
12741    }
12742 
12743    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12744 }
12745 
12746 AST_TEST_DEFINE(test_voicemail_msgcount)
12747 {
12748    int i, j, res = AST_TEST_PASS, syserr;
12749    struct ast_vm_user *vmu;
12750    struct ast_vm_user svm;
12751    struct vm_state vms;
12752 #ifdef IMAP_STORAGE
12753    struct ast_channel *chan = NULL;
12754 #endif
12755    struct {
12756       char dir[256];
12757       char file[256];
12758       char txtfile[256];
12759    } tmp[3];
12760    char syscmd[256];
12761    const char origweasels[] = "tt-weasels";
12762    const char testcontext[] = "test";
12763    const char testmailbox[] = "00000000";
12764    const char testspec[] = "00000000@test";
12765    FILE *txt;
12766    int new, old, urgent;
12767    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12768    const int folder2mbox[3] = { 1, 11, 0 };
12769    const int expected_results[3][12] = {
12770       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12771       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12772       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12773       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12774    };
12775 
12776    switch (cmd) {
12777    case TEST_INIT:
12778       info->name = "test_voicemail_msgcount";
12779       info->category = "/apps/app_voicemail/";
12780       info->summary = "Test Voicemail status checks";
12781       info->description =
12782          "Verify that message counts are correct when retrieved through the public API";
12783       return AST_TEST_NOT_RUN;
12784    case TEST_EXECUTE:
12785       break;
12786    }
12787 
12788    /* Make sure the original path was completely empty */
12789    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12790    if ((syserr = ast_safe_system(syscmd))) {
12791       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12792          syserr > 0 ? strerror(syserr) : "unable to fork()");
12793       return AST_TEST_FAIL;
12794    }
12795 
12796 #ifdef IMAP_STORAGE
12797    if (!(chan = ast_dummy_channel_alloc())) {
12798       ast_test_status_update(test, "Unable to create dummy channel\n");
12799       return AST_TEST_FAIL;
12800    }
12801 #endif
12802 
12803    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12804       !(vmu = find_or_create(testcontext, testmailbox))) {
12805       ast_test_status_update(test, "Cannot create vmu structure\n");
12806       ast_unreplace_sigchld();
12807 #ifdef IMAP_STORAGE
12808       chan = ast_channel_unref(chan);
12809 #endif
12810       return AST_TEST_FAIL;
12811    }
12812 
12813    populate_defaults(vmu);
12814    memset(&vms, 0, sizeof(vms));
12815 
12816    /* Create temporary voicemail */
12817    for (i = 0; i < 3; i++) {
12818       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12819       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12820       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12821 
12822       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12823          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12824             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12825          if ((syserr = ast_safe_system(syscmd))) {
12826             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12827                syserr > 0 ? strerror(syserr) : "unable to fork()");
12828             ast_unreplace_sigchld();
12829 #ifdef IMAP_STORAGE
12830             chan = ast_channel_unref(chan);
12831 #endif
12832             return AST_TEST_FAIL;
12833          }
12834       }
12835 
12836       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12837          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12838          fclose(txt);
12839       } else {
12840          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12841          res = AST_TEST_FAIL;
12842          break;
12843       }
12844       open_mailbox(&vms, vmu, folder2mbox[i]);
12845       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12846 
12847       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12848       for (j = 0; j < 3; j++) {
12849          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12850          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12851             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12852                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12853             res = AST_TEST_FAIL;
12854          }
12855       }
12856 
12857       new = old = urgent = 0;
12858       if (ast_app_inboxcount(testspec, &new, &old)) {
12859          ast_test_status_update(test, "inboxcount returned failure\n");
12860          res = AST_TEST_FAIL;
12861       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12862          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12863             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12864          res = AST_TEST_FAIL;
12865       }
12866 
12867       new = old = urgent = 0;
12868       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12869          ast_test_status_update(test, "inboxcount2 returned failure\n");
12870          res = AST_TEST_FAIL;
12871       } else if (old != expected_results[i][6 + 0] ||
12872             urgent != expected_results[i][6 + 1] ||
12873                new != expected_results[i][6 + 2]    ) {
12874          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12875             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12876          res = AST_TEST_FAIL;
12877       }
12878 
12879       new = old = urgent = 0;
12880       for (j = 0; j < 3; j++) {
12881          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12882             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12883                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12884             res = AST_TEST_FAIL;
12885          }
12886       }
12887    }
12888 
12889    for (i = 0; i < 3; i++) {
12890       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12891        * server, in which case, the rm below will not affect the
12892        * voicemails. */
12893       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12894       DISPOSE(tmp[i].dir, 0);
12895    }
12896 
12897    if (vms.deleted) {
12898       ast_free(vms.deleted);
12899    }
12900    if (vms.heard) {
12901       ast_free(vms.heard);
12902    }
12903 
12904 #ifdef IMAP_STORAGE
12905    chan = ast_channel_unref(chan);
12906 #endif
12907 
12908    /* And remove test directory */
12909    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12910    if ((syserr = ast_safe_system(syscmd))) {
12911       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12912          syserr > 0 ? strerror(syserr) : "unable to fork()");
12913    }
12914 
12915    return res;
12916 }
12917 
12918 AST_TEST_DEFINE(test_voicemail_notify_endl)
12919 {
12920    int res = AST_TEST_PASS;
12921    char testcontext[] = "test";
12922    char testmailbox[] = "00000000";
12923    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12924    char attach[256], attach2[256];
12925    char buf[256] = ""; /* No line should actually be longer than 80 */
12926    struct ast_channel *chan = NULL;
12927    struct ast_vm_user *vmu, vmus = {
12928       .flags = 0,
12929    };
12930    FILE *file;
12931    struct {
12932       char *name;
12933       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12934       void *location;
12935       union {
12936          int intval;
12937          char *strval;
12938       } u;
12939    } test_items[] = {
12940       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12941       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12942       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12943       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12944       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12945       { "attach2", STRPTR, attach2, .u.strval = "" },
12946       { "attach", STRPTR, attach, .u.strval = "" },
12947    };
12948    int which;
12949 
12950    switch (cmd) {
12951    case TEST_INIT:
12952       info->name = "test_voicemail_notify_endl";
12953       info->category = "/apps/app_voicemail/";
12954       info->summary = "Test Voicemail notification end-of-line";
12955       info->description =
12956          "Verify that notification emails use a consistent end-of-line character";
12957       return AST_TEST_NOT_RUN;
12958    case TEST_EXECUTE:
12959       break;
12960    }
12961 
12962    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12963    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12964 
12965    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12966       !(vmu = find_or_create(testcontext, testmailbox))) {
12967       ast_test_status_update(test, "Cannot create vmu structure\n");
12968       return AST_TEST_NOT_RUN;
12969    }
12970 
12971    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12972       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12973       return AST_TEST_NOT_RUN;
12974    }
12975 
12976    populate_defaults(vmu);
12977    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12978 #ifdef IMAP_STORAGE
12979    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12980 #endif
12981 
12982    file = tmpfile();
12983    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12984       /* Kill previous test, if any */
12985       rewind(file);
12986       if (ftruncate(fileno(file), 0)) {
12987          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12988          res = AST_TEST_FAIL;
12989          break;
12990       }
12991 
12992       /* Make each change, in order, to the test mailbox */
12993       if (test_items[which].type == INT) {
12994          *((int *) test_items[which].location) = test_items[which].u.intval;
12995       } else if (test_items[which].type == FLAGVAL) {
12996          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12997             ast_clear_flag(vmu, test_items[which].u.intval);
12998          } else {
12999             ast_set_flag(vmu, test_items[which].u.intval);
13000          }
13001       } else if (test_items[which].type == STATIC) {
13002          strcpy(test_items[which].location, test_items[which].u.strval);
13003       } else if (test_items[which].type == STRPTR) {
13004          test_items[which].location = test_items[which].u.strval;
13005       }
13006 
13007       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13008       rewind(file);
13009       while (fgets(buf, sizeof(buf), file)) {
13010          if (
13011 #ifdef IMAP_STORAGE
13012          buf[strlen(buf) - 2] != '\r'
13013 #else
13014          buf[strlen(buf) - 2] == '\r'
13015 #endif
13016          || buf[strlen(buf) - 1] != '\n') {
13017             res = AST_TEST_FAIL;
13018          }
13019       }
13020    }
13021    fclose(file);
13022    return res;
13023 }
13024 
13025 AST_TEST_DEFINE(test_voicemail_load_config)
13026 {
13027    int res = AST_TEST_PASS;
13028    struct ast_vm_user *vmu;
13029    struct ast_config *cfg;
13030    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13031    int fd;
13032    FILE *file;
13033    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13034 
13035    switch (cmd) {
13036    case TEST_INIT:
13037       info->name = "test_voicemail_load_config";
13038       info->category = "/apps/app_voicemail/";
13039       info->summary = "Test loading Voicemail config";
13040       info->description =
13041          "Verify that configuration is loaded consistently. "
13042          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13043          "some options were loaded after the mailboxes were instantiated, causing "
13044          "those options not to be set correctly.";
13045       return AST_TEST_NOT_RUN;
13046    case TEST_EXECUTE:
13047       break;
13048    }
13049 
13050    /* build a config file by hand... */
13051    if ((fd = mkstemp(config_filename)) < 0) {
13052       return AST_TEST_FAIL;
13053    }
13054    if (!(file = fdopen(fd, "w"))) {
13055       close(fd);
13056       unlink(config_filename);
13057       return AST_TEST_FAIL;
13058    }
13059    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13060    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13061    fputs("00000002 => 9999,Mrs. Test\n", file);
13062    fclose(file);
13063 
13064    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13065       res = AST_TEST_FAIL;
13066       goto cleanup;
13067    }
13068 
13069    load_config_from_memory(1, cfg, NULL);
13070    ast_config_destroy(cfg);
13071 
13072 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13073    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13074    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13075 
13076    AST_LIST_LOCK(&users);
13077    AST_LIST_TRAVERSE(&users, vmu, list) {
13078       if (!strcmp(vmu->mailbox, "00000001")) {
13079          if (0); /* trick to get CHECK to work */
13080          CHECK(vmu, callback, "othercontext")
13081          CHECK(vmu, locale, "nl_NL.UTF-8")
13082          CHECK(vmu, zonetag, "central")
13083       } else if (!strcmp(vmu->mailbox, "00000002")) {
13084          if (0); /* trick to get CHECK to work */
13085          CHECK(vmu, callback, "somecontext")
13086          CHECK(vmu, locale, "de_DE.UTF-8")
13087          CHECK(vmu, zonetag, "european")
13088       }
13089    }
13090    AST_LIST_UNLOCK(&users);
13091 
13092 #undef CHECK
13093 
13094    /* restore config */
13095    load_config(1); /* this might say "Failed to load configuration file." */
13096 
13097 cleanup:
13098    unlink(config_filename);
13099    return res;
13100 }
13101 
13102 #endif /* defined(TEST_FRAMEWORK) */
13103 
13104 static int reload(void)
13105 {
13106    return load_config(1);
13107 }
13108 
13109 static int unload_module(void)
13110 {
13111    int res;
13112 
13113    res = ast_unregister_application(app);
13114    res |= ast_unregister_application(app2);
13115    res |= ast_unregister_application(app3);
13116    res |= ast_unregister_application(app4);
13117    res |= ast_unregister_application(sayname_app);
13118    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13119    res |= ast_manager_unregister("VoicemailUsersList");
13120    res |= ast_data_unregister(NULL);
13121 #ifdef TEST_FRAMEWORK
13122    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13123    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13124    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13125    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13126    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13127 #endif
13128    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13129    ast_uninstall_vm_functions();
13130    ao2_ref(inprocess_container, -1);
13131 
13132    if (poll_thread != AST_PTHREADT_NULL)
13133       stop_poll_thread();
13134 
13135    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13136    ast_unload_realtime("voicemail");
13137    ast_unload_realtime("voicemail_data");
13138 
13139    free_vm_users();
13140    free_vm_zones();
13141    return res;
13142 }
13143 
13144 static int load_module(void)
13145 {
13146    int res;
13147    my_umask = umask(0);
13148    umask(my_umask);
13149 
13150    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13151       return AST_MODULE_LOAD_DECLINE;
13152    }
13153 
13154    /* compute the location of the voicemail spool directory */
13155    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13156    
13157    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13158       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13159    }
13160 
13161    if ((res = load_config(0)))
13162       return res;
13163 
13164    res = ast_register_application_xml(app, vm_exec);
13165    res |= ast_register_application_xml(app2, vm_execmain);
13166    res |= ast_register_application_xml(app3, vm_box_exists);
13167    res |= ast_register_application_xml(app4, vmauthenticate);
13168    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13169    res |= ast_custom_function_register(&mailbox_exists_acf);
13170    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13171 #ifdef TEST_FRAMEWORK
13172    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13173    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13174    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13175    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13176    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13177 #endif
13178 
13179    if (res)
13180       return res;
13181 
13182    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13183    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13184 
13185    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13186    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13187    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13188 
13189    return res;
13190 }
13191 
13192 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13193 {
13194    int cmd = 0;
13195    char destination[80] = "";
13196    int retries = 0;
13197 
13198    if (!num) {
13199       ast_verb(3, "Destination number will be entered manually\n");
13200       while (retries < 3 && cmd != 't') {
13201          destination[1] = '\0';
13202          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13203          if (!cmd)
13204             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13205          if (!cmd)
13206             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13207          if (!cmd) {
13208             cmd = ast_waitfordigit(chan, 6000);
13209             if (cmd)
13210                destination[0] = cmd;
13211          }
13212          if (!cmd) {
13213             retries++;
13214          } else {
13215 
13216             if (cmd < 0)
13217                return 0;
13218             if (cmd == '*') {
13219                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13220                return 0;
13221             }
13222             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13223                retries++;
13224             else
13225                cmd = 't';
13226          }
13227          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13228       }
13229       if (retries >= 3) {
13230          return 0;
13231       }
13232       
13233    } else {
13234       if (option_verbose > 2)
13235          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13236       ast_copy_string(destination, num, sizeof(destination));
13237    }
13238 
13239    if (!ast_strlen_zero(destination)) {
13240       if (destination[strlen(destination) -1 ] == '*')
13241          return 0; 
13242       if (option_verbose > 2)
13243          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13244       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13245       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13246       chan->priority = 0;
13247       return 9;
13248    }
13249    return 0;
13250 }
13251 
13252 /*!
13253  * \brief The advanced options within a message.
13254  * \param chan
13255  * \param vmu 
13256  * \param vms
13257  * \param msg
13258  * \param option
13259  * \param record_gain
13260  *
13261  * Provides handling for the play message envelope, call the person back, or reply to message. 
13262  *
13263  * \return zero on success, -1 on error.
13264  */
13265 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)
13266 {
13267    int res = 0;
13268    char filename[PATH_MAX];
13269    struct ast_config *msg_cfg = NULL;
13270    const char *origtime, *context;
13271    char *name, *num;
13272    int retries = 0;
13273    char *cid;
13274    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13275 
13276    vms->starting = 0; 
13277 
13278    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13279 
13280    /* Retrieve info from VM attribute file */
13281    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13282    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13283    msg_cfg = ast_config_load(filename, config_flags);
13284    DISPOSE(vms->curdir, vms->curmsg);
13285    if (!valid_config(msg_cfg)) {
13286       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13287       return 0;
13288    }
13289 
13290    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13291       ast_config_destroy(msg_cfg);
13292       return 0;
13293    }
13294 
13295    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13296 
13297    context = ast_variable_retrieve(msg_cfg, "message", "context");
13298    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13299       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13300    switch (option) {
13301    case 3: /* Play message envelope */
13302       if (!res)
13303          res = play_message_datetime(chan, vmu, origtime, filename);
13304       if (!res)
13305          res = play_message_callerid(chan, vms, cid, context, 0);
13306 
13307       res = 't';
13308       break;
13309 
13310    case 2:  /* Call back */
13311 
13312       if (ast_strlen_zero(cid))
13313          break;
13314 
13315       ast_callerid_parse(cid, &name, &num);
13316       while ((res > -1) && (res != 't')) {
13317          switch (res) {
13318          case '1':
13319             if (num) {
13320                /* Dial the CID number */
13321                res = dialout(chan, vmu, num, vmu->callback);
13322                if (res) {
13323                   ast_config_destroy(msg_cfg);
13324                   return 9;
13325                }
13326             } else {
13327                res = '2';
13328             }
13329             break;
13330 
13331          case '2':
13332             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13333             if (!ast_strlen_zero(vmu->dialout)) {
13334                res = dialout(chan, vmu, NULL, vmu->dialout);
13335                if (res) {
13336                   ast_config_destroy(msg_cfg);
13337                   return 9;
13338                }
13339             } else {
13340                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13341                res = ast_play_and_wait(chan, "vm-sorry");
13342             }
13343             ast_config_destroy(msg_cfg);
13344             return res;
13345          case '*':
13346             res = 't';
13347             break;
13348          case '3':
13349          case '4':
13350          case '5':
13351          case '6':
13352          case '7':
13353          case '8':
13354          case '9':
13355          case '0':
13356 
13357             res = ast_play_and_wait(chan, "vm-sorry");
13358             retries++;
13359             break;
13360          default:
13361             if (num) {
13362                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13363                res = ast_play_and_wait(chan, "vm-num-i-have");
13364                if (!res)
13365                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13366                if (!res)
13367                   res = ast_play_and_wait(chan, "vm-tocallnum");
13368                /* Only prompt for a caller-specified number if there is a dialout context specified */
13369                if (!ast_strlen_zero(vmu->dialout)) {
13370                   if (!res)
13371                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13372                }
13373             } else {
13374                res = ast_play_and_wait(chan, "vm-nonumber");
13375                if (!ast_strlen_zero(vmu->dialout)) {
13376                   if (!res)
13377                      res = ast_play_and_wait(chan, "vm-toenternumber");
13378                }
13379             }
13380             if (!res) {
13381                res = ast_play_and_wait(chan, "vm-star-cancel");
13382             }
13383             if (!res) {
13384                res = ast_waitfordigit(chan, 6000);
13385             }
13386             if (!res) {
13387                retries++;
13388                if (retries > 3) {
13389                   res = 't';
13390                }
13391             }
13392             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13393             break; 
13394             
13395          }
13396          if (res == 't')
13397             res = 0;
13398          else if (res == '*')
13399             res = -1;
13400       }
13401       break;
13402       
13403    case 1:  /* Reply */
13404       /* Send reply directly to sender */
13405       if (ast_strlen_zero(cid))
13406          break;
13407 
13408       ast_callerid_parse(cid, &name, &num);
13409       if (!num) {
13410          ast_verb(3, "No CID number available, no reply sent\n");
13411          if (!res)
13412             res = ast_play_and_wait(chan, "vm-nonumber");
13413          ast_config_destroy(msg_cfg);
13414          return res;
13415       } else {
13416          struct ast_vm_user vmu2;
13417          if (find_user(&vmu2, vmu->context, num)) {
13418             struct leave_vm_options leave_options;
13419             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13420             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13421 
13422             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13423             
13424             memset(&leave_options, 0, sizeof(leave_options));
13425             leave_options.record_gain = record_gain;
13426             res = leave_voicemail(chan, mailbox, &leave_options);
13427             if (!res)
13428                res = 't';
13429             ast_config_destroy(msg_cfg);
13430             return res;
13431          } else {
13432             /* Sender has no mailbox, can't reply */
13433             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13434             ast_play_and_wait(chan, "vm-nobox");
13435             res = 't';
13436             ast_config_destroy(msg_cfg);
13437             return res;
13438          }
13439       } 
13440       res = 0;
13441 
13442       break;
13443    }
13444 
13445    ast_config_destroy(msg_cfg);
13446 
13447 #ifndef IMAP_STORAGE
13448    if (!res) {
13449       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13450       vms->heard[msg] = 1;
13451       res = wait_file(chan, vms, vms->fn);
13452    }
13453 #endif
13454    return res;
13455 }
13456 
13457 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13458          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13459          signed char record_gain, struct vm_state *vms, char *flag)
13460 {
13461    /* Record message & let caller review or re-record it, or set options if applicable */
13462    int res = 0;
13463    int cmd = 0;
13464    int max_attempts = 3;
13465    int attempts = 0;
13466    int recorded = 0;
13467    int msg_exists = 0;
13468    signed char zero_gain = 0;
13469    char tempfile[PATH_MAX];
13470    char *acceptdtmf = "#";
13471    char *canceldtmf = "";
13472    int canceleddtmf = 0;
13473 
13474    /* Note that urgent and private are for flagging messages as such in the future */
13475 
13476    /* barf if no pointer passed to store duration in */
13477    if (duration == NULL) {
13478       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13479       return -1;
13480    }
13481 
13482    if (!outsidecaller)
13483       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13484    else
13485       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13486 
13487    cmd = '3';  /* Want to start by recording */
13488 
13489    while ((cmd >= 0) && (cmd != 't')) {
13490       switch (cmd) {
13491       case '1':
13492          if (!msg_exists) {
13493             /* In this case, 1 is to record a message */
13494             cmd = '3';
13495             break;
13496          } else {
13497             /* Otherwise 1 is to save the existing message */
13498             ast_verb(3, "Saving message as is\n");
13499             if (!outsidecaller) 
13500                ast_filerename(tempfile, recordfile, NULL);
13501             ast_stream_and_wait(chan, "vm-msgsaved", "");
13502             if (!outsidecaller) {
13503                /* Saves to IMAP server only if imapgreeting=yes */
13504                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13505                DISPOSE(recordfile, -1);
13506             }
13507             cmd = 't';
13508             return res;
13509          }
13510       case '2':
13511          /* Review */
13512          ast_verb(3, "Reviewing the message\n");
13513          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13514          break;
13515       case '3':
13516          msg_exists = 0;
13517          /* Record */
13518          if (recorded == 1) 
13519             ast_verb(3, "Re-recording the message\n");
13520          else  
13521             ast_verb(3, "Recording the message\n");
13522          
13523          if (recorded && outsidecaller) {
13524             cmd = ast_play_and_wait(chan, INTRO);
13525             cmd = ast_play_and_wait(chan, "beep");
13526          }
13527          recorded = 1;
13528          /* 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 */
13529          if (record_gain)
13530             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13531          if (ast_test_flag(vmu, VM_OPERATOR))
13532             canceldtmf = "0";
13533          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13534          if (strchr(canceldtmf, cmd)) {
13535          /* need this flag here to distinguish between pressing '0' during message recording or after */
13536             canceleddtmf = 1;
13537          }
13538          if (record_gain)
13539             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13540          if (cmd == -1) {
13541             /* User has hung up, no options to give */
13542             if (!outsidecaller) {
13543                /* user was recording a greeting and they hung up, so let's delete the recording. */
13544                ast_filedelete(tempfile, NULL);
13545             }     
13546             return cmd;
13547          }
13548          if (cmd == '0') {
13549             break;
13550          } else if (cmd == '*') {
13551             break;
13552 #if 0
13553          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13554             /* Message is too short */
13555             ast_verb(3, "Message too short\n");
13556             cmd = ast_play_and_wait(chan, "vm-tooshort");
13557             cmd = ast_filedelete(tempfile, NULL);
13558             break;
13559          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13560             /* Message is all silence */
13561             ast_verb(3, "Nothing recorded\n");
13562             cmd = ast_filedelete(tempfile, NULL);
13563             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13564             if (!cmd)
13565                cmd = ast_play_and_wait(chan, "vm-speakup");
13566             break;
13567 #endif
13568          } else {
13569             /* If all is well, a message exists */
13570             msg_exists = 1;
13571             cmd = 0;
13572          }
13573          break;
13574       case '4':
13575          if (outsidecaller) {  /* only mark vm messages */
13576             /* Mark Urgent */
13577             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13578                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13579                res = ast_play_and_wait(chan, "vm-marked-urgent");
13580                strcpy(flag, "Urgent");
13581             } else if (flag) {
13582                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13583                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13584                strcpy(flag, "");
13585             } else {
13586                ast_play_and_wait(chan, "vm-sorry");
13587             }
13588             cmd = 0;
13589          } else {
13590             cmd = ast_play_and_wait(chan, "vm-sorry");
13591          }
13592          break;
13593       case '5':
13594       case '6':
13595       case '7':
13596       case '8':
13597       case '9':
13598       case '*':
13599       case '#':
13600          cmd = ast_play_and_wait(chan, "vm-sorry");
13601          break;
13602 #if 0 
13603 /*  XXX Commented out for the moment because of the dangers of deleting
13604     a message while recording (can put the message numbers out of sync) */
13605       case '*':
13606          /* Cancel recording, delete message, offer to take another message*/
13607          cmd = ast_play_and_wait(chan, "vm-deleted");
13608          cmd = ast_filedelete(tempfile, NULL);
13609          if (outsidecaller) {
13610             res = vm_exec(chan, NULL);
13611             return res;
13612          }
13613          else
13614             return 1;
13615 #endif
13616       case '0':
13617          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13618             cmd = ast_play_and_wait(chan, "vm-sorry");
13619             break;
13620          }
13621          if (msg_exists || recorded) {
13622             cmd = ast_play_and_wait(chan, "vm-saveoper");
13623             if (!cmd)
13624                cmd = ast_waitfordigit(chan, 3000);
13625             if (cmd == '1') {
13626                ast_filerename(tempfile, recordfile, NULL);
13627                ast_play_and_wait(chan, "vm-msgsaved");
13628                cmd = '0';
13629             } else if (cmd == '4') {
13630                if (flag) {
13631                   ast_play_and_wait(chan, "vm-marked-urgent");
13632                   strcpy(flag, "Urgent");
13633                }
13634                ast_play_and_wait(chan, "vm-msgsaved");
13635                cmd = '0';
13636             } else {
13637                ast_play_and_wait(chan, "vm-deleted");
13638                DELETE(tempfile, -1, tempfile, vmu);
13639                cmd = '0';
13640             }
13641          }
13642          return cmd;
13643       default:
13644          /* If the caller is an ouside caller, and the review option is enabled,
13645             allow them to review the message, but let the owner of the box review
13646             their OGM's */
13647          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13648             return cmd;
13649          if (msg_exists) {
13650             cmd = ast_play_and_wait(chan, "vm-review");
13651             if (!cmd && outsidecaller) {
13652                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13653                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13654                } else if (flag) {
13655                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13656                }
13657             }
13658          } else {
13659             cmd = ast_play_and_wait(chan, "vm-torerecord");
13660             if (!cmd)
13661                cmd = ast_waitfordigit(chan, 600);
13662          }
13663          
13664          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13665             cmd = ast_play_and_wait(chan, "vm-reachoper");
13666             if (!cmd)
13667                cmd = ast_waitfordigit(chan, 600);
13668          }
13669 #if 0
13670          if (!cmd)
13671             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13672 #endif
13673          if (!cmd)
13674             cmd = ast_waitfordigit(chan, 6000);
13675          if (!cmd) {
13676             attempts++;
13677          }
13678          if (attempts > max_attempts) {
13679             cmd = 't';
13680          }
13681       }
13682    }
13683    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13684       /* Hang up or timeout, so delete the recording. */
13685       ast_filedelete(tempfile, NULL);
13686    }
13687 
13688    if (cmd != 't' && outsidecaller)
13689       ast_play_and_wait(chan, "vm-goodbye");
13690 
13691    return cmd;
13692 }
13693 
13694 /* This is a workaround so that menuselect displays a proper description
13695  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13696  */
13697 
13698 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13699       .load = load_module,
13700       .unload = unload_module,
13701       .reload = reload,
13702       .nonoptreq = "res_adsi,res_smdi",
13703       );

Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1