Wed Jan 27 20:02:03 2016

Asterisk developer's documentation


app_voicemail_imapstorage.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00048    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00049       <conflict>ODBC_STORAGE</conflict>
00050       <conflict>IMAP_STORAGE</conflict>
00051       <defaultenabled>yes</defaultenabled>
00052       <support_level>core</support_level>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>generic_odbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00063       <depend>imap_tk</depend>
00064       <conflict>ODBC_STORAGE</conflict>
00065       <conflict>FILE_STORAGE</conflict>
00066       <use>openssl</use>
00067       <defaultenabled>no</defaultenabled>
00068       <support_level>core</support_level>
00069    </member>
00070 </category>
00071 ***/
00072 
00073 #include "asterisk.h"
00074 
00075 #ifdef IMAP_STORAGE
00076 #include <ctype.h>
00077 #include <signal.h>
00078 #include <pwd.h>
00079 #ifdef USE_SYSTEM_IMAP
00080 #include <imap/c-client.h>
00081 #include <imap/imap4r1.h>
00082 #include <imap/linkage.h>
00083 #elif defined (USE_SYSTEM_CCLIENT)
00084 #include <c-client/c-client.h>
00085 #include <c-client/imap4r1.h>
00086 #include <c-client/linkage.h>
00087 #else
00088 #include "c-client.h"
00089 #include "imap4r1.h"
00090 #include "linkage.h"
00091 #endif
00092 #endif
00093 
00094 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426691 $")
00095 
00096 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00097 #include <sys/time.h>
00098 #include <sys/stat.h>
00099 #include <sys/mman.h>
00100 #include <time.h>
00101 #include <dirent.h>
00102 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00103 #include <sys/wait.h>
00104 #endif
00105 
00106 #include "asterisk/logger.h"
00107 #include "asterisk/lock.h"
00108 #include "asterisk/file.h"
00109 #include "asterisk/channel.h"
00110 #include "asterisk/pbx.h"
00111 #include "asterisk/config.h"
00112 #include "asterisk/say.h"
00113 #include "asterisk/module.h"
00114 #include "asterisk/adsi.h"
00115 #include "asterisk/app.h"
00116 #include "asterisk/manager.h"
00117 #include "asterisk/dsp.h"
00118 #include "asterisk/localtime.h"
00119 #include "asterisk/cli.h"
00120 #include "asterisk/utils.h"
00121 #include "asterisk/stringfields.h"
00122 #include "asterisk/smdi.h"
00123 #include "asterisk/astobj2.h"
00124 #include "asterisk/event.h"
00125 #include "asterisk/taskprocessor.h"
00126 #include "asterisk/test.h"
00127 
00128 #ifdef ODBC_STORAGE
00129 #include "asterisk/res_odbc.h"
00130 #endif
00131 
00132 #ifdef IMAP_STORAGE
00133 #include "asterisk/threadstorage.h"
00134 #endif
00135 
00136 /*** DOCUMENTATION
00137    <application name="VoiceMail" language="en_US">
00138       <synopsis>
00139          Leave a Voicemail message.
00140       </synopsis>
00141       <syntax>
00142          <parameter name="mailboxs" argsep="&amp;" required="true">
00143             <argument name="mailbox1" argsep="@" required="true">
00144                <argument name="mailbox" required="true" />
00145                <argument name="context" />
00146             </argument>
00147             <argument name="mailbox2" argsep="@" multiple="true">
00148                <argument name="mailbox" required="true" />
00149                <argument name="context" />
00150             </argument>
00151          </parameter>
00152          <parameter name="options">
00153             <optionlist>
00154                <option name="b">
00155                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00156                </option>
00157                <option name="d">
00158                   <argument name="c" />
00159                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00160                   if played during the greeting. Context defaults to the current context.</para>
00161                </option>
00162                <option name="g">
00163                   <argument name="#" required="true" />
00164                   <para>Use the specified amount of gain when recording the voicemail
00165                   message. The units are whole-number decibels (dB). Only works on supported
00166                   technologies, which is DAHDI only.</para>
00167                </option>
00168                <option name="s">
00169                   <para>Skip the playback of instructions for leaving a message to the
00170                   calling party.</para>
00171                </option>
00172                <option name="u">
00173                   <para>Play the <literal>unavailable</literal> greeting.</para>
00174                </option>
00175                <option name="U">
00176                   <para>Mark message as <literal>URGENT</literal>.</para>
00177                </option>
00178                <option name="P">
00179                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00180                </option>
00181             </optionlist>
00182          </parameter>
00183       </syntax>
00184       <description>
00185          <para>This application allows the calling party to leave a message for the specified
00186          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00187          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00188          exist.</para>
00189          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00190          <enumlist>
00191             <enum name="0">
00192                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00193             </enum>
00194             <enum name="*">
00195                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00196             </enum>
00197          </enumlist>
00198          <para>This application will set the following channel variable upon completion:</para>
00199          <variablelist>
00200             <variable name="VMSTATUS">
00201                <para>This indicates the status of the execution of the VoiceMail application.</para>
00202                <value name="SUCCESS" />
00203                <value name="USEREXIT" />
00204                <value name="FAILED" />
00205             </variable>
00206          </variablelist>
00207       </description>
00208       <see-also>
00209          <ref type="application">VoiceMailMain</ref>
00210       </see-also>
00211    </application>
00212    <application name="VoiceMailMain" language="en_US">
00213       <synopsis>
00214          Check Voicemail messages.
00215       </synopsis>
00216       <syntax>
00217          <parameter name="mailbox" required="true" argsep="@">
00218             <argument name="mailbox" />
00219             <argument name="context" />
00220          </parameter>
00221          <parameter name="options">
00222             <optionlist>
00223                <option name="p">
00224                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00225                   the mailbox that is entered by the caller.</para>
00226                </option>
00227                <option name="g">
00228                   <argument name="#" required="true" />
00229                   <para>Use the specified amount of gain when recording a voicemail message.
00230                   The units are whole-number decibels (dB).</para>
00231                </option>
00232                <option name="s">
00233                   <para>Skip checking the passcode for the mailbox.</para>
00234                </option>
00235                <option name="a">
00236                   <argument name="folder" required="true" />
00237                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00238                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00239                   <enumlist>
00240                      <enum name="0"><para>INBOX</para></enum>
00241                      <enum name="1"><para>Old</para></enum>
00242                      <enum name="2"><para>Work</para></enum>
00243                      <enum name="3"><para>Family</para></enum>
00244                      <enum name="4"><para>Friends</para></enum>
00245                      <enum name="5"><para>Cust1</para></enum>
00246                      <enum name="6"><para>Cust2</para></enum>
00247                      <enum name="7"><para>Cust3</para></enum>
00248                      <enum name="8"><para>Cust4</para></enum>
00249                      <enum name="9"><para>Cust5</para></enum>
00250                   </enumlist>
00251                </option>
00252             </optionlist>
00253          </parameter>
00254       </syntax>
00255       <description>
00256          <para>This application allows the calling party to check voicemail messages. A specific
00257          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00258          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00259          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00260          <literal>default</literal> context will be used.</para>
00261          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00262          or Password, and the extension exists:</para>
00263          <enumlist>
00264             <enum name="*">
00265                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00266             </enum>
00267          </enumlist>
00268       </description>
00269       <see-also>
00270          <ref type="application">VoiceMail</ref>
00271       </see-also>
00272    </application>
00273    <application name="MailboxExists" language="en_US">
00274       <synopsis>
00275          Check to see if Voicemail mailbox exists.
00276       </synopsis>
00277       <syntax>
00278          <parameter name="mailbox" required="true" argsep="@">
00279             <argument name="mailbox" required="true" />
00280             <argument name="context" />
00281          </parameter>
00282          <parameter name="options">
00283             <para>None options.</para>
00284          </parameter>
00285       </syntax>
00286       <description>
00287          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00288          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00289          will be used.</para>
00290          <para>This application will set the following channel variable upon completion:</para>
00291          <variablelist>
00292             <variable name="VMBOXEXISTSSTATUS">
00293                <para>This will contain the status of the execution of the MailboxExists application.
00294                Possible values include:</para>
00295                <value name="SUCCESS" />
00296                <value name="FAILED" />
00297             </variable>
00298          </variablelist>
00299       </description>
00300    </application>
00301    <application name="VMAuthenticate" language="en_US">
00302       <synopsis>
00303          Authenticate with Voicemail passwords.
00304       </synopsis>
00305       <syntax>
00306          <parameter name="mailbox" required="true" argsep="@">
00307             <argument name="mailbox" />
00308             <argument name="context" />
00309          </parameter>
00310          <parameter name="options">
00311             <optionlist>
00312                <option name="s">
00313                   <para>Skip playing the initial prompts.</para>
00314                </option>
00315             </optionlist>
00316          </parameter>
00317       </syntax>
00318       <description>
00319          <para>This application behaves the same way as the Authenticate application, but the passwords
00320          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00321          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00322          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00323          mailbox.</para>
00324          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00325          or Password, and the extension exists:</para>
00326          <enumlist>
00327             <enum name="*">
00328                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00329             </enum>
00330          </enumlist>
00331       </description>
00332    </application>
00333    <application name="VMSayName" language="en_US">
00334       <synopsis>
00335          Play the name of a voicemail user
00336       </synopsis>
00337       <syntax>
00338          <parameter name="mailbox" required="true" argsep="@">
00339             <argument name="mailbox" />
00340             <argument name="context" />
00341          </parameter>
00342       </syntax>
00343       <description>
00344          <para>This application will say the recorded name of the voicemail user specified as the
00345          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00346       </description>
00347    </application>
00348    <function name="MAILBOX_EXISTS" language="en_US">
00349       <synopsis>
00350          Tell if a mailbox is configured.
00351       </synopsis>
00352       <syntax argsep="@">
00353          <parameter name="mailbox" required="true" />
00354          <parameter name="context" />
00355       </syntax>
00356       <description>
00357          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00358          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00359          context.</para>
00360       </description>
00361    </function>
00362    <manager name="VoicemailUsersList" language="en_US">
00363       <synopsis>
00364          List All Voicemail User Information.
00365       </synopsis>
00366       <syntax>
00367          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00368       </syntax>
00369       <description>
00370       </description>
00371    </manager>
00372  ***/
00373 
00374 #ifdef IMAP_STORAGE
00375 static char imapserver[48];
00376 static char imapport[8];
00377 static char imapflags[128];
00378 static char imapfolder[64];
00379 static char imapparentfolder[64] = "\0";
00380 static char greetingfolder[64];
00381 static char authuser[32];
00382 static char authpassword[42];
00383 static int imapversion = 1;
00384 
00385 static int expungeonhangup = 1;
00386 static int imapgreetings = 0;
00387 static char delimiter = '\0';
00388 
00389 struct vm_state;
00390 struct ast_vm_user;
00391 
00392 AST_THREADSTORAGE(ts_vmstate);
00393 
00394 /* Forward declarations for IMAP */
00395 static int init_mailstream(struct vm_state *vms, int box);
00396 static void write_file(char *filename, char *buffer, unsigned long len);
00397 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00398 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00399 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00400 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00401 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00402 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00403 static void vmstate_insert(struct vm_state *vms);
00404 static void vmstate_delete(struct vm_state *vms);
00405 static void set_update(MAILSTREAM * stream);
00406 static void init_vm_state(struct vm_state *vms);
00407 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00408 static void get_mailbox_delimiter(MAILSTREAM *stream);
00409 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00410 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00411 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00412 static void update_messages_by_imapuser(const char *user, unsigned long number);
00413 static int vm_delete(char *file);
00414 
00415 static int imap_remove_file (char *dir, int msgnum);
00416 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00417 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00418 static void check_quota(struct vm_state *vms, char *mailbox);
00419 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00420 struct vmstate {
00421    struct vm_state *vms;
00422    AST_LIST_ENTRY(vmstate) list;
00423 };
00424 
00425 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00426 
00427 #endif
00428 
00429 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00430 
00431 #define COMMAND_TIMEOUT 5000
00432 /* Don't modify these here; set your umask at runtime instead */
00433 #define  VOICEMAIL_DIR_MODE   0777
00434 #define  VOICEMAIL_FILE_MODE  0666
00435 #define  CHUNKSIZE   65536
00436 
00437 #define VOICEMAIL_CONFIG "voicemail.conf"
00438 #define ASTERISK_USERNAME "asterisk"
00439 
00440 /* Define fast-forward, pause, restart, and reverse keys
00441  * while listening to a voicemail message - these are
00442  * strings, not characters */
00443 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00444 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00445 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00446 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00447 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00448 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00449 
00450 /* Default mail command to mail voicemail. Change it with the
00451  * mailcmd= command in voicemail.conf */
00452 #define SENDMAIL "/usr/sbin/sendmail -t"
00453 
00454 #define INTRO "vm-intro"
00455 
00456 #define MAXMSG 100
00457 #define MAXMSGLIMIT 9999
00458 
00459 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00460 
00461 #define BASELINELEN 72
00462 #define BASEMAXINLINE 256
00463 #ifdef IMAP_STORAGE
00464 #define ENDL "\r\n"
00465 #else
00466 #define ENDL "\n"
00467 #endif
00468 
00469 #define MAX_DATETIME_FORMAT   512
00470 #define MAX_NUM_CID_CONTEXTS 10
00471 
00472 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00473 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00474 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00475 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00476 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00477 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00478 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00479 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00480 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00481 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00482 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00483 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00484 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00485 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00486 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00487 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00488 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00489 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00490 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00491 #define ERROR_LOCK_PATH  -100
00492 #define OPERATOR_EXIT     300
00493 
00494 
00495 enum vm_box {
00496    NEW_FOLDER,
00497    OLD_FOLDER,
00498    WORK_FOLDER,
00499    FAMILY_FOLDER,
00500    FRIENDS_FOLDER,
00501    GREETINGS_FOLDER
00502 };
00503 
00504 enum vm_option_flags {
00505    OPT_SILENT =           (1 << 0),
00506    OPT_BUSY_GREETING =    (1 << 1),
00507    OPT_UNAVAIL_GREETING = (1 << 2),
00508    OPT_RECORDGAIN =       (1 << 3),
00509    OPT_PREPEND_MAILBOX =  (1 << 4),
00510    OPT_AUTOPLAY =         (1 << 6),
00511    OPT_DTMFEXIT =         (1 << 7),
00512    OPT_MESSAGE_Urgent =   (1 << 8),
00513    OPT_MESSAGE_PRIORITY = (1 << 9)
00514 };
00515 
00516 enum vm_option_args {
00517    OPT_ARG_RECORDGAIN = 0,
00518    OPT_ARG_PLAYFOLDER = 1,
00519    OPT_ARG_DTMFEXIT   = 2,
00520    /* This *must* be the last value in this enum! */
00521    OPT_ARG_ARRAY_SIZE = 3,
00522 };
00523 
00524 enum vm_passwordlocation {
00525    OPT_PWLOC_VOICEMAILCONF = 0,
00526    OPT_PWLOC_SPOOLDIR      = 1,
00527    OPT_PWLOC_USERSCONF     = 2,
00528 };
00529 
00530 AST_APP_OPTIONS(vm_app_options, {
00531    AST_APP_OPTION('s', OPT_SILENT),
00532    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00533    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00534    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00535    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00536    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00537    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00538    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00539    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00540 });
00541 
00542 static int load_config(int reload);
00543 #ifdef TEST_FRAMEWORK
00544 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00545 #endif
00546 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00547 
00548 /*! \page vmlang Voicemail Language Syntaxes Supported
00549 
00550    \par Syntaxes supported, not really language codes.
00551    \arg \b en    - English
00552    \arg \b de    - German
00553    \arg \b es    - Spanish
00554    \arg \b fr    - French
00555    \arg \b it    - Italian
00556    \arg \b nl    - Dutch
00557    \arg \b pt    - Portuguese
00558    \arg \b pt_BR - Portuguese (Brazil)
00559    \arg \b gr    - Greek
00560    \arg \b no    - Norwegian
00561    \arg \b se    - Swedish
00562    \arg \b tw    - Chinese (Taiwan)
00563    \arg \b ua - Ukrainian
00564 
00565 German requires the following additional soundfile:
00566 \arg \b 1F  einE (feminine)
00567 
00568 Spanish requires the following additional soundfile:
00569 \arg \b 1M      un (masculine)
00570 
00571 Dutch, Portuguese & Spanish require the following additional soundfiles:
00572 \arg \b vm-INBOXs singular of 'new'
00573 \arg \b vm-Olds      singular of 'old/heard/read'
00574 
00575 NB these are plural:
00576 \arg \b vm-INBOX  nieuwe (nl)
00577 \arg \b vm-Old    oude (nl)
00578 
00579 Polish uses:
00580 \arg \b vm-new-a  'new', feminine singular accusative
00581 \arg \b vm-new-e  'new', feminine plural accusative
00582 \arg \b vm-new-ych   'new', feminine plural genitive
00583 \arg \b vm-old-a  'old', feminine singular accusative
00584 \arg \b vm-old-e  'old', feminine plural accusative
00585 \arg \b vm-old-ych   'old', feminine plural genitive
00586 \arg \b digits/1-a   'one', not always same as 'digits/1'
00587 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00588 
00589 Swedish uses:
00590 \arg \b vm-nytt      singular of 'new'
00591 \arg \b vm-nya    plural of 'new'
00592 \arg \b vm-gammalt   singular of 'old'
00593 \arg \b vm-gamla  plural of 'old'
00594 \arg \b digits/ett   'one', not always same as 'digits/1'
00595 
00596 Norwegian uses:
00597 \arg \b vm-ny     singular of 'new'
00598 \arg \b vm-nye    plural of 'new'
00599 \arg \b vm-gammel singular of 'old'
00600 \arg \b vm-gamle  plural of 'old'
00601 
00602 Dutch also uses:
00603 \arg \b nl-om     'at'?
00604 
00605 Spanish also uses:
00606 \arg \b vm-youhaveno
00607 
00608 Italian requires the following additional soundfile:
00609 
00610 For vm_intro_it:
00611 \arg \b vm-nuovo  new
00612 \arg \b vm-nuovi  new plural
00613 \arg \b vm-vecchio   old
00614 \arg \b vm-vecchi old plural
00615 
00616 Chinese (Taiwan) requires the following additional soundfile:
00617 \arg \b vm-tong      A class-word for call (tong1)
00618 \arg \b vm-ri     A class-word for day (ri4)
00619 \arg \b vm-you    You (ni3)
00620 \arg \b vm-haveno   Have no (mei2 you3)
00621 \arg \b vm-have     Have (you3)
00622 \arg \b vm-listen   To listen (yao4 ting1)
00623 
00624 
00625 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00626 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00627 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00628 
00629 */
00630 
00631 struct baseio {
00632    int iocp;
00633    int iolen;
00634    int linelength;
00635    int ateof;
00636    unsigned char iobuf[BASEMAXINLINE];
00637 };
00638 
00639 /*! Structure for linked list of users 
00640  * Use ast_vm_user_destroy() to free one of these structures. */
00641 struct ast_vm_user {
00642    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00643    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00644    char password[80];               /*!< Secret pin code, numbers only */
00645    char fullname[80];               /*!< Full name, for directory app */
00646    char email[80];                  /*!< E-mail address */
00647    char *emailsubject;              /*!< E-mail subject */
00648    char *emailbody;                 /*!< E-mail body */
00649    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00650    char serveremail[80];            /*!< From: Mail address */
00651    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00652    char zonetag[80];                /*!< Time zone */
00653    char locale[20];                 /*!< The locale (for presentation of date/time) */
00654    char callback[80];
00655    char dialout[80];
00656    char uniqueid[80];               /*!< Unique integer identifier */
00657    char exit[80];
00658    char attachfmt[20];              /*!< Attachment format */
00659    unsigned int flags;              /*!< VM_ flags */ 
00660    int saydurationm;
00661    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00662    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00663    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00664    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00665    int passwordlocation;            /*!< Storage location of the password */
00666 #ifdef IMAP_STORAGE
00667    char imapuser[80];               /*!< IMAP server login */
00668    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00669    char imapfolder[64];             /*!< IMAP voicemail folder */
00670    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00671    int imapversion;                 /*!< If configuration changes, use the new values */
00672 #endif
00673    double volgain;                  /*!< Volume gain for voicemails sent via email */
00674    AST_LIST_ENTRY(ast_vm_user) list;
00675 };
00676 
00677 /*! Voicemail time zones */
00678 struct vm_zone {
00679    AST_LIST_ENTRY(vm_zone) list;
00680    char name[80];
00681    char timezone[80];
00682    char msg_format[512];
00683 };
00684 
00685 #define VMSTATE_MAX_MSG_ARRAY 256
00686 
00687 /*! Voicemail mailbox state */
00688 struct vm_state {
00689    char curbox[80];
00690    char username[80];
00691    char context[80];
00692    char curdir[PATH_MAX];
00693    char vmbox[PATH_MAX];
00694    char fn[PATH_MAX];
00695    char intro[PATH_MAX];
00696    int *deleted;
00697    int *heard;
00698    int dh_arraysize; /* used for deleted / heard allocation */
00699    int curmsg;
00700    int lastmsg;
00701    int newmessages;
00702    int oldmessages;
00703    int urgentmessages;
00704    int starting;
00705    int repeats;
00706 #ifdef IMAP_STORAGE
00707    ast_mutex_t lock;
00708    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00709    long *msgArray;
00710    unsigned msg_array_max;
00711    MAILSTREAM *mailstream;
00712    int vmArrayIndex;
00713    char imapuser[80];                   /*!< IMAP server login */
00714    char imapfolder[64];                 /*!< IMAP voicemail folder */
00715    int imapversion;
00716    int interactive;
00717    char introfn[PATH_MAX];              /*!< Name of prepended file */
00718    unsigned int quota_limit;
00719    unsigned int quota_usage;
00720    struct vm_state *persist_vms;
00721 #endif
00722 };
00723 
00724 #ifdef ODBC_STORAGE
00725 static char odbc_database[80];
00726 static char odbc_table[80];
00727 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00728 #define DISPOSE(a,b) remove_file(a,b)
00729 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00730 #define EXISTS(a,b,c,d) (message_exists(a,b))
00731 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00732 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00733 #define DELETE(a,b,c,d) (delete_file(a,b))
00734 #else
00735 #ifdef IMAP_STORAGE
00736 #define DISPOSE(a,b) (imap_remove_file(a,b))
00737 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
00738 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00739 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00740 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00741 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00742 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00743 #else
00744 #define RETRIEVE(a,b,c,d)
00745 #define DISPOSE(a,b)
00746 #define STORE(a,b,c,d,e,f,g,h,i,j)
00747 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00748 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00749 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00750 #define DELETE(a,b,c,d) (vm_delete(c))
00751 #endif
00752 #endif
00753 
00754 static char VM_SPOOL_DIR[PATH_MAX];
00755 
00756 static char ext_pass_cmd[128];
00757 static char ext_pass_check_cmd[128];
00758 
00759 static int my_umask;
00760 
00761 #define PWDCHANGE_INTERNAL (1 << 1)
00762 #define PWDCHANGE_EXTERNAL (1 << 2)
00763 static int pwdchange = PWDCHANGE_INTERNAL;
00764 
00765 #ifdef ODBC_STORAGE
00766 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00767 #else
00768 # ifdef IMAP_STORAGE
00769 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00770 # else
00771 # define tdesc "Comedian Mail (Voicemail System)"
00772 # endif
00773 #endif
00774 
00775 static char userscontext[AST_MAX_EXTENSION] = "default";
00776 
00777 static char *addesc = "Comedian Mail";
00778 
00779 /* Leave a message */
00780 static char *app = "VoiceMail";
00781 
00782 /* Check mail, control, etc */
00783 static char *app2 = "VoiceMailMain";
00784 
00785 static char *app3 = "MailboxExists";
00786 static char *app4 = "VMAuthenticate";
00787 
00788 static char *sayname_app = "VMSayName";
00789 
00790 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00791 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00792 static char zonetag[80];
00793 static char locale[20];
00794 static int maxsilence;
00795 static int maxmsg;
00796 static int maxdeletedmsg;
00797 static int silencethreshold = 128;
00798 static char serveremail[80];
00799 static char mailcmd[160];  /* Configurable mail cmd */
00800 static char externnotify[160]; 
00801 static struct ast_smdi_interface *smdi_iface = NULL;
00802 static char vmfmts[80];
00803 static double volgain;
00804 static int vmminsecs;
00805 static int vmmaxsecs;
00806 static int maxgreet;
00807 static int skipms;
00808 static int maxlogins;
00809 static int minpassword;
00810 static int passwordlocation;
00811 
00812 /*! Poll mailboxes for changes since there is something external to
00813  *  app_voicemail that may change them. */
00814 static unsigned int poll_mailboxes;
00815 
00816 /*! Polling frequency */
00817 static unsigned int poll_freq;
00818 /*! By default, poll every 30 seconds */
00819 #define DEFAULT_POLL_FREQ 30
00820 
00821 AST_MUTEX_DEFINE_STATIC(poll_lock);
00822 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00823 static pthread_t poll_thread = AST_PTHREADT_NULL;
00824 static unsigned char poll_thread_run;
00825 
00826 /*! Subscription to ... MWI event subscriptions */
00827 static struct ast_event_sub *mwi_sub_sub;
00828 /*! Subscription to ... MWI event un-subscriptions */
00829 static struct ast_event_sub *mwi_unsub_sub;
00830 
00831 /*!
00832  * \brief An MWI subscription
00833  *
00834  * This is so we can keep track of which mailboxes are subscribed to.
00835  * This way, we know which mailboxes to poll when the pollmailboxes
00836  * option is being used.
00837  */
00838 struct mwi_sub {
00839    AST_RWLIST_ENTRY(mwi_sub) entry;
00840    int old_urgent;
00841    int old_new;
00842    int old_old;
00843    uint32_t uniqueid;
00844    char mailbox[1];
00845 };
00846 
00847 struct mwi_sub_task {
00848    const char *mailbox;
00849    const char *context;
00850    uint32_t uniqueid;
00851 };
00852 
00853 static struct ast_taskprocessor *mwi_subscription_tps;
00854 
00855 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00856 
00857 /* custom audio control prompts for voicemail playback */
00858 static char listen_control_forward_key[12];
00859 static char listen_control_reverse_key[12];
00860 static char listen_control_pause_key[12];
00861 static char listen_control_restart_key[12];
00862 static char listen_control_stop_key[12];
00863 
00864 /* custom password sounds */
00865 static char vm_password[80] = "vm-password";
00866 static char vm_newpassword[80] = "vm-newpassword";
00867 static char vm_passchanged[80] = "vm-passchanged";
00868 static char vm_reenterpassword[80] = "vm-reenterpassword";
00869 static char vm_mismatch[80] = "vm-mismatch";
00870 static char vm_invalid_password[80] = "vm-invalid-password";
00871 static char vm_pls_try_again[80] = "vm-pls-try-again";
00872 
00873 /*
00874  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00875  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00876  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00877  * app.c's __ast_play_and_record function
00878  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00879  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00880  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00881  * more effort than either of the other two.
00882  */
00883 static char vm_prepend_timeout[80] = "vm-then-pound";
00884 
00885 static struct ast_flags globalflags = {0};
00886 
00887 static int saydurationminfo;
00888 
00889 static char dialcontext[AST_MAX_CONTEXT] = "";
00890 static char callcontext[AST_MAX_CONTEXT] = "";
00891 static char exitcontext[AST_MAX_CONTEXT] = "";
00892 
00893 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00894 
00895 
00896 static char *emailbody = NULL;
00897 static char *emailsubject = NULL;
00898 static char *pagerbody = NULL;
00899 static char *pagersubject = NULL;
00900 static char fromstring[100];
00901 static char pagerfromstring[100];
00902 static char charset[32] = "ISO-8859-1";
00903 
00904 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00905 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00906 static int adsiver = 1;
00907 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00908 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00909 
00910 /* Forward declarations - generic */
00911 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00912 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
00913 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00914 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00915          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00916          signed char record_gain, struct vm_state *vms, char *flag);
00917 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00918 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00919 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
00920 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
00921 static void apply_options(struct ast_vm_user *vmu, const char *options);
00922 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
00923 static int is_valid_dtmf(const char *key);
00924 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00925 static int write_password_to_file(const char *secretfn, const char *password);
00926 static const char *substitute_escapes(const char *value);
00927 static void free_user(struct ast_vm_user *vmu);
00928 
00929 struct ao2_container *inprocess_container;
00930 
00931 struct inprocess {
00932    int count;
00933    char *context;
00934    char mailbox[0];
00935 };
00936 
00937 static int inprocess_hash_fn(const void *obj, const int flags)
00938 {
00939    const struct inprocess *i = obj;
00940    return atoi(i->mailbox);
00941 }
00942 
00943 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00944 {
00945    struct inprocess *i = obj, *j = arg;
00946    if (strcmp(i->mailbox, j->mailbox)) {
00947       return 0;
00948    }
00949    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00950 }
00951 
00952 static int inprocess_count(const char *context, const char *mailbox, int delta)
00953 {
00954    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00955    arg->context = arg->mailbox + strlen(mailbox) + 1;
00956    strcpy(arg->mailbox, mailbox); /* SAFE */
00957    strcpy(arg->context, context); /* SAFE */
00958    ao2_lock(inprocess_container);
00959    if ((i = ao2_find(inprocess_container, arg, 0))) {
00960       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00961       ao2_unlock(inprocess_container);
00962       ao2_ref(i, -1);
00963       return ret;
00964    }
00965    if (delta < 0) {
00966       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00967    }
00968    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00969       ao2_unlock(inprocess_container);
00970       return 0;
00971    }
00972    i->context = i->mailbox + strlen(mailbox) + 1;
00973    strcpy(i->mailbox, mailbox); /* SAFE */
00974    strcpy(i->context, context); /* SAFE */
00975    i->count = delta;
00976    ao2_link(inprocess_container, i);
00977    ao2_unlock(inprocess_container);
00978    ao2_ref(i, -1);
00979    return 0;
00980 }
00981 
00982 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00983 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00984 #endif
00985 
00986 /*!
00987  * \brief Strips control and non 7-bit clean characters from input string.
00988  *
00989  * \note To map control and none 7-bit characters to a 7-bit clean characters
00990  *  please use ast_str_encode_mine().
00991  */
00992 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00993 {
00994    char *bufptr = buf;
00995    for (; *input; input++) {
00996       if (*input < 32) {
00997          continue;
00998       }
00999       *bufptr++ = *input;
01000       if (bufptr == buf + buflen - 1) {
01001          break;
01002       }
01003    }
01004    *bufptr = '\0';
01005    return buf;
01006 }
01007 
01008 
01009 /*!
01010  * \brief Sets default voicemail system options to a voicemail user.
01011  *
01012  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01013  * - all the globalflags
01014  * - the saydurationminfo
01015  * - the callcontext
01016  * - the dialcontext
01017  * - the exitcontext
01018  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01019  * - volume gain
01020  * - emailsubject, emailbody set to NULL
01021  */
01022 static void populate_defaults(struct ast_vm_user *vmu)
01023 {
01024    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01025    vmu->passwordlocation = passwordlocation;
01026    if (saydurationminfo) {
01027       vmu->saydurationm = saydurationminfo;
01028    }
01029    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01030    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01031    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01032    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01033    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01034    if (vmminsecs) {
01035       vmu->minsecs = vmminsecs;
01036    }
01037    if (vmmaxsecs) {
01038       vmu->maxsecs = vmmaxsecs;
01039    }
01040    if (maxmsg) {
01041       vmu->maxmsg = maxmsg;
01042    }
01043    if (maxdeletedmsg) {
01044       vmu->maxdeletedmsg = maxdeletedmsg;
01045    }
01046    vmu->volgain = volgain;
01047    ast_free(vmu->emailsubject);
01048    vmu->emailsubject = NULL;
01049    ast_free(vmu->emailbody);
01050    vmu->emailbody = NULL;
01051 #ifdef IMAP_STORAGE
01052    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01053 #endif
01054 }
01055 
01056 /*!
01057  * \brief Sets a a specific property value.
01058  * \param vmu The voicemail user object to work with.
01059  * \param var The name of the property to be set.
01060  * \param value The value to be set to the property.
01061  * 
01062  * The property name must be one of the understood properties. See the source for details.
01063  */
01064 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01065 {
01066    int x;
01067    if (!strcasecmp(var, "attach")) {
01068       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01069    } else if (!strcasecmp(var, "attachfmt")) {
01070       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01071    } else if (!strcasecmp(var, "serveremail")) {
01072       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01073    } else if (!strcasecmp(var, "emailbody")) {
01074       vmu->emailbody = ast_strdup(substitute_escapes(value));
01075    } else if (!strcasecmp(var, "emailsubject")) {
01076       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01077    } else if (!strcasecmp(var, "language")) {
01078       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01079    } else if (!strcasecmp(var, "tz")) {
01080       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01081    } else if (!strcasecmp(var, "locale")) {
01082       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01083 #ifdef IMAP_STORAGE
01084    } else if (!strcasecmp(var, "imapuser")) {
01085       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01086       vmu->imapversion = imapversion;
01087    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01088       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01089       vmu->imapversion = imapversion;
01090    } else if (!strcasecmp(var, "imapfolder")) {
01091       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01092    } else if (!strcasecmp(var, "imapvmshareid")) {
01093       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01094       vmu->imapversion = imapversion;
01095 #endif
01096    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01097       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01098    } else if (!strcasecmp(var, "saycid")){
01099       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01100    } else if (!strcasecmp(var, "sendvoicemail")){
01101       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01102    } else if (!strcasecmp(var, "review")){
01103       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01104    } else if (!strcasecmp(var, "tempgreetwarn")){
01105       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01106    } else if (!strcasecmp(var, "messagewrap")){
01107       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01108    } else if (!strcasecmp(var, "operator")) {
01109       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01110    } else if (!strcasecmp(var, "envelope")){
01111       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01112    } else if (!strcasecmp(var, "moveheard")){
01113       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01114    } else if (!strcasecmp(var, "sayduration")){
01115       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01116    } else if (!strcasecmp(var, "saydurationm")){
01117       if (sscanf(value, "%30d", &x) == 1) {
01118          vmu->saydurationm = x;
01119       } else {
01120          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01121       }
01122    } else if (!strcasecmp(var, "forcename")){
01123       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01124    } else if (!strcasecmp(var, "forcegreetings")){
01125       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01126    } else if (!strcasecmp(var, "callback")) {
01127       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01128    } else if (!strcasecmp(var, "dialout")) {
01129       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01130    } else if (!strcasecmp(var, "exitcontext")) {
01131       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01132    } else if (!strcasecmp(var, "minsecs")) {
01133       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01134          vmu->minsecs = x;
01135       } else {
01136          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01137          vmu->minsecs = vmminsecs;
01138       }
01139    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01140       vmu->maxsecs = atoi(value);
01141       if (vmu->maxsecs <= 0) {
01142          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01143          vmu->maxsecs = vmmaxsecs;
01144       } else {
01145          vmu->maxsecs = atoi(value);
01146       }
01147       if (!strcasecmp(var, "maxmessage"))
01148          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01149    } else if (!strcasecmp(var, "maxmsg")) {
01150       vmu->maxmsg = atoi(value);
01151       /* Accept maxmsg=0 (Greetings only voicemail) */
01152       if (vmu->maxmsg < 0) {
01153          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01154          vmu->maxmsg = MAXMSG;
01155       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01156          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01157          vmu->maxmsg = MAXMSGLIMIT;
01158       }
01159    } else if (!strcasecmp(var, "nextaftercmd")) {
01160       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01161    } else if (!strcasecmp(var, "backupdeleted")) {
01162       if (sscanf(value, "%30d", &x) == 1)
01163          vmu->maxdeletedmsg = x;
01164       else if (ast_true(value))
01165          vmu->maxdeletedmsg = MAXMSG;
01166       else
01167          vmu->maxdeletedmsg = 0;
01168 
01169       if (vmu->maxdeletedmsg < 0) {
01170          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01171          vmu->maxdeletedmsg = MAXMSG;
01172       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01173          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01174          vmu->maxdeletedmsg = MAXMSGLIMIT;
01175       }
01176    } else if (!strcasecmp(var, "volgain")) {
01177       sscanf(value, "%30lf", &vmu->volgain);
01178    } else if (!strcasecmp(var, "passwordlocation")) {
01179       if (!strcasecmp(value, "spooldir")) {
01180          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01181       } else {
01182          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01183       }
01184    } else if (!strcasecmp(var, "options")) {
01185       apply_options(vmu, value);
01186    }
01187 }
01188 
01189 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01190 {
01191    int fds[2], pid = 0;
01192 
01193    memset(buf, 0, len);
01194 
01195    if (pipe(fds)) {
01196       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01197    } else {
01198       /* good to go*/
01199       pid = ast_safe_fork(0);
01200 
01201       if (pid < 0) {
01202          /* ok maybe not */
01203          close(fds[0]);
01204          close(fds[1]);
01205          snprintf(buf, len, "FAILURE: Fork failed");
01206       } else if (pid) {
01207          /* parent */
01208          close(fds[1]);
01209          if (read(fds[0], buf, len) < 0) {
01210             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01211          }
01212          close(fds[0]);
01213       } else {
01214          /*  child */
01215          AST_DECLARE_APP_ARGS(arg,
01216             AST_APP_ARG(v)[20];
01217          );
01218          char *mycmd = ast_strdupa(command);
01219 
01220          close(fds[0]);
01221          dup2(fds[1], STDOUT_FILENO);
01222          close(fds[1]);
01223          ast_close_fds_above_n(STDOUT_FILENO);
01224 
01225          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01226 
01227          execv(arg.v[0], arg.v); 
01228          printf("FAILURE: %s", strerror(errno));
01229          _exit(0);
01230       }
01231    }
01232    return buf;
01233 }
01234 
01235 /*!
01236  * \brief Check that password meets minimum required length
01237  * \param vmu The voicemail user to change the password for.
01238  * \param password The password string to check
01239  *
01240  * \return zero on ok, 1 on not ok.
01241  */
01242 static int check_password(struct ast_vm_user *vmu, char *password)
01243 {
01244    /* check minimum length */
01245    if (strlen(password) < minpassword)
01246       return 1;
01247    /* check that password does not contain '*' character */
01248    if (!ast_strlen_zero(password) && password[0] == '*')
01249       return 1;
01250    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01251       char cmd[255], buf[255];
01252 
01253       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01254 
01255       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01256       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01257          ast_debug(5, "Result: %s\n", buf);
01258          if (!strncasecmp(buf, "VALID", 5)) {
01259             ast_debug(3, "Passed password check: '%s'\n", buf);
01260             return 0;
01261          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01262             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01263             return 0;
01264          } else {
01265             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01266             return 1;
01267          }
01268       }
01269    }
01270    return 0;
01271 }
01272 
01273 /*! 
01274  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01275  * \param vmu The voicemail user to change the password for.
01276  * \param password The new value to be set to the password for this user.
01277  * 
01278  * This only works if there is a realtime engine configured.
01279  * This is called from the (top level) vm_change_password.
01280  *
01281  * \return zero on success, -1 on error.
01282  */
01283 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01284 {
01285    int res = -1;
01286    if (!strcmp(vmu->password, password)) {
01287       /* No change (but an update would return 0 rows updated, so we opt out here) */
01288       return 0;
01289    }
01290 
01291    if (strlen(password) > 10) {
01292       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01293    }
01294    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01295       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01296       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01297       res = 0;
01298    }
01299    return res;
01300 }
01301 
01302 /*!
01303  * \brief Destructively Parse options and apply.
01304  */
01305 static void apply_options(struct ast_vm_user *vmu, const char *options)
01306 {  
01307    char *stringp;
01308    char *s;
01309    char *var, *value;
01310    stringp = ast_strdupa(options);
01311    while ((s = strsep(&stringp, "|"))) {
01312       value = s;
01313       if ((var = strsep(&value, "=")) && value) {
01314          apply_option(vmu, var, value);
01315       }
01316    }  
01317 }
01318 
01319 /*!
01320  * \brief Loads the options specific to a voicemail user.
01321  * 
01322  * This is called when a vm_user structure is being set up, such as from load_options.
01323  */
01324 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01325 {
01326    for (; var; var = var->next) {
01327       if (!strcasecmp(var->name, "vmsecret")) {
01328          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01329       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01330          if (ast_strlen_zero(retval->password)) {
01331             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01332                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01333                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01334             } else {
01335                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01336             }
01337          }
01338       } else if (!strcasecmp(var->name, "uniqueid")) {
01339          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01340       } else if (!strcasecmp(var->name, "pager")) {
01341          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01342       } else if (!strcasecmp(var->name, "email")) {
01343          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01344       } else if (!strcasecmp(var->name, "fullname")) {
01345          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01346       } else if (!strcasecmp(var->name, "context")) {
01347          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01348       } else if (!strcasecmp(var->name, "emailsubject")) {
01349          ast_free(retval->emailsubject);
01350          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01351       } else if (!strcasecmp(var->name, "emailbody")) {
01352          ast_free(retval->emailbody);
01353          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01354 #ifdef IMAP_STORAGE
01355       } else if (!strcasecmp(var->name, "imapuser")) {
01356          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01357          retval->imapversion = imapversion;
01358       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01359          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01360          retval->imapversion = imapversion;
01361       } else if (!strcasecmp(var->name, "imapfolder")) {
01362          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01363       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01364          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01365          retval->imapversion = imapversion;
01366 #endif
01367       } else
01368          apply_option(retval, var->name, var->value);
01369    }
01370 }
01371 
01372 /*!
01373  * \brief Determines if a DTMF key entered is valid.
01374  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
01375  *
01376  * Tests the character entered against the set of valid DTMF characters. 
01377  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01378  */
01379 static int is_valid_dtmf(const char *key)
01380 {
01381    int i;
01382    char *local_key = ast_strdupa(key);
01383 
01384    for (i = 0; i < strlen(key); ++i) {
01385       if (!strchr(VALID_DTMF, *local_key)) {
01386          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01387          return 0;
01388       }
01389       local_key++;
01390    }
01391    return 1;
01392 }
01393 
01394 /*!
01395  * \brief Finds a voicemail user from the realtime engine.
01396  * \param ivm
01397  * \param context
01398  * \param mailbox
01399  *
01400  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
01401  *
01402  * \return The ast_vm_user structure for the user that was found.
01403  */
01404 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01405 {
01406    struct ast_variable *var;
01407    struct ast_vm_user *retval;
01408 
01409    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01410       if (ivm) {
01411          memset(retval, 0, sizeof(*retval));
01412       }
01413       populate_defaults(retval);
01414       if (!ivm) {
01415          ast_set_flag(retval, VM_ALLOCED);
01416       }
01417       if (mailbox) {
01418          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01419       }
01420       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01421          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01422       } else {
01423          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01424       }
01425       if (var) {
01426          apply_options_full(retval, var);
01427          ast_variables_destroy(var);
01428       } else { 
01429          if (!ivm) 
01430             free_user(retval);
01431          retval = NULL;
01432       }  
01433    } 
01434    return retval;
01435 }
01436 
01437 /*!
01438  * \brief Finds a voicemail user from the users file or the realtime engine.
01439  * \param ivm
01440  * \param context
01441  * \param mailbox
01442  * 
01443  * \return The ast_vm_user structure for the user that was found.
01444  */
01445 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01446 {
01447    /* This function could be made to generate one from a database, too */
01448    struct ast_vm_user *vmu = NULL, *cur;
01449    AST_LIST_LOCK(&users);
01450 
01451    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01452       context = "default";
01453 
01454    AST_LIST_TRAVERSE(&users, cur, list) {
01455 #ifdef IMAP_STORAGE
01456       if (cur->imapversion != imapversion) {
01457          continue;
01458       }
01459 #endif
01460       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01461          break;
01462       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01463          break;
01464    }
01465    if (cur) {
01466       /* Make a copy, so that on a reload, we have no race */
01467       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01468          *vmu = *cur;
01469          if (!ivm) {
01470             vmu->emailbody = ast_strdup(cur->emailbody);
01471             vmu->emailsubject = ast_strdup(cur->emailsubject);
01472          }
01473          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01474          AST_LIST_NEXT(vmu, list) = NULL;
01475       }
01476    } else
01477       vmu = find_user_realtime(ivm, context, mailbox);
01478    AST_LIST_UNLOCK(&users);
01479    return vmu;
01480 }
01481 
01482 /*!
01483  * \brief Resets a user password to a specified password.
01484  * \param context
01485  * \param mailbox
01486  * \param newpass
01487  *
01488  * This does the actual change password work, called by the vm_change_password() function.
01489  *
01490  * \return zero on success, -1 on error.
01491  */
01492 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01493 {
01494    /* This function could be made to generate one from a database, too */
01495    struct ast_vm_user *cur;
01496    int res = -1;
01497    AST_LIST_LOCK(&users);
01498    AST_LIST_TRAVERSE(&users, cur, list) {
01499       if ((!context || !strcasecmp(context, cur->context)) &&
01500          (!strcasecmp(mailbox, cur->mailbox)))
01501             break;
01502    }
01503    if (cur) {
01504       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01505       res = 0;
01506    }
01507    AST_LIST_UNLOCK(&users);
01508    return res;
01509 }
01510 
01511 /*!
01512  * \brief Check if configuration file is valid
01513  */
01514 static inline int valid_config(const struct ast_config *cfg)
01515 {
01516    return cfg && cfg != CONFIG_STATUS_FILEINVALID;
01517 }
01518 
01519 /*! 
01520  * \brief The handler for the change password option.
01521  * \param vmu The voicemail user to work with.
01522  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01523  * 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.
01524  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01525  */
01526 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01527 {
01528    struct ast_config   *cfg = NULL;
01529    struct ast_variable *var = NULL;
01530    struct ast_category *cat = NULL;
01531    char *category = NULL, *value = NULL, *new = NULL;
01532    const char *tmp = NULL;
01533    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01534    char secretfn[PATH_MAX] = "";
01535    int found = 0;
01536 
01537    if (!change_password_realtime(vmu, newpassword))
01538       return;
01539 
01540    /* check if we should store the secret in the spool directory next to the messages */
01541    switch (vmu->passwordlocation) {
01542    case OPT_PWLOC_SPOOLDIR:
01543       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01544       if (write_password_to_file(secretfn, newpassword) == 0) {
01545          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01546          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01547          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01548          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01549          break;
01550       } else {
01551          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01552       }
01553       /* Fall-through */
01554    case OPT_PWLOC_VOICEMAILCONF:
01555       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
01556          while ((category = ast_category_browse(cfg, category))) {
01557             if (!strcasecmp(category, vmu->context)) {
01558                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01559                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01560                   break;
01561                }
01562                value = strstr(tmp, ",");
01563                if (!value) {
01564                   new = ast_alloca(strlen(newpassword)+1);
01565                   sprintf(new, "%s", newpassword);
01566                } else {
01567                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01568                   sprintf(new, "%s%s", newpassword, value);
01569                }
01570                if (!(cat = ast_category_get(cfg, category))) {
01571                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01572                   break;
01573                }
01574                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01575                found = 1;
01576             }
01577          }
01578          /* save the results */
01579          if (found) {
01580             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01581             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01582             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01583             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01584             ast_config_destroy(cfg);
01585             break;
01586          }
01587 
01588          ast_config_destroy(cfg);
01589       }
01590       /* Fall-through */
01591    case OPT_PWLOC_USERSCONF:
01592       /* check users.conf and update the password stored for the mailbox */
01593       /* if no vmsecret entry exists create one. */
01594       if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
01595          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01596          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01597             ast_debug(4, "users.conf: %s\n", category);
01598             if (!strcasecmp(category, vmu->mailbox)) {
01599                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01600                   ast_debug(3, "looks like we need to make vmsecret!\n");
01601                   var = ast_variable_new("vmsecret", newpassword, "");
01602                } else {
01603                   var = NULL;
01604                }
01605                new = ast_alloca(strlen(newpassword) + 1);
01606                sprintf(new, "%s", newpassword);
01607                if (!(cat = ast_category_get(cfg, category))) {
01608                   ast_debug(4, "failed to get category!\n");
01609                   ast_free(var);
01610                   break;
01611                }
01612                if (!var) {
01613                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01614                } else {
01615                   ast_variable_append(cat, var);
01616                }
01617                found = 1;
01618                break;
01619             }
01620          }
01621          /* save the results and clean things up */
01622          if (found) {
01623             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01624             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01625             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01626             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01627          }
01628 
01629          ast_config_destroy(cfg);
01630       }
01631    }
01632 }
01633 
01634 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01635 {
01636    char buf[255];
01637    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01638    ast_debug(1, "External password: %s\n",buf);
01639    if (!ast_safe_system(buf)) {
01640       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01641       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01642       /* Reset the password in memory, too */
01643       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01644    }
01645 }
01646 
01647 /*! 
01648  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01649  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01650  * \param len The length of the path string that was written out.
01651  * \param context
01652  * \param ext 
01653  * \param folder 
01654  * 
01655  * The path is constructed as 
01656  *    VM_SPOOL_DIRcontext/ext/folder
01657  *
01658  * \return zero on success, -1 on error.
01659  */
01660 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01661 {
01662    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01663 }
01664 
01665 /*! 
01666  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01667  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01668  * \param len The length of the path string that was written out.
01669  * \param dir 
01670  * \param num 
01671  * 
01672  * The path is constructed as 
01673  *    VM_SPOOL_DIRcontext/ext/folder
01674  *
01675  * \return zero on success, -1 on error.
01676  */
01677 static int make_file(char *dest, const int len, const char *dir, const int num)
01678 {
01679    return snprintf(dest, len, "%s/msg%04d", dir, num);
01680 }
01681 
01682 /* same as mkstemp, but return a FILE * */
01683 static FILE *vm_mkftemp(char *template)
01684 {
01685    FILE *p = NULL;
01686    int pfd = mkstemp(template);
01687    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01688    if (pfd > -1) {
01689       p = fdopen(pfd, "w+");
01690       if (!p) {
01691          close(pfd);
01692          pfd = -1;
01693       }
01694    }
01695    return p;
01696 }
01697 
01698 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01699  * \param dest    String. base directory.
01700  * \param len     Length of dest.
01701  * \param context String. Ignored if is null or empty string.
01702  * \param ext     String. Ignored if is null or empty string.
01703  * \param folder  String. Ignored if is null or empty string. 
01704  * \return -1 on failure, 0 on success.
01705  */
01706 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01707 {
01708    mode_t   mode = VOICEMAIL_DIR_MODE;
01709    int res;
01710 
01711    make_dir(dest, len, context, ext, folder);
01712    if ((res = ast_mkdir(dest, mode))) {
01713       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01714       return -1;
01715    }
01716    return 0;
01717 }
01718 
01719 static const char * const mailbox_folders[] = {
01720 #ifdef IMAP_STORAGE
01721    imapfolder,
01722 #else
01723    "INBOX",
01724 #endif
01725    "Old",
01726    "Work",
01727    "Family",
01728    "Friends",
01729    "Cust1",
01730    "Cust2",
01731    "Cust3",
01732    "Cust4",
01733    "Cust5",
01734    "Deleted",
01735    "Urgent",
01736 };
01737 
01738 static const char *mbox(struct ast_vm_user *vmu, int id)
01739 {
01740 #ifdef IMAP_STORAGE
01741    if (vmu && id == 0) {
01742       return vmu->imapfolder;
01743    }
01744 #endif
01745    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01746 }
01747 
01748 static int get_folder_by_name(const char *name)
01749 {
01750    size_t i;
01751 
01752    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01753       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01754          return i;
01755       }
01756    }
01757 
01758    return -1;
01759 }
01760 
01761 static void free_user(struct ast_vm_user *vmu)
01762 {
01763    if (ast_test_flag(vmu, VM_ALLOCED)) {
01764 
01765       ast_free(vmu->emailbody);
01766       vmu->emailbody = NULL;
01767 
01768       ast_free(vmu->emailsubject);
01769       vmu->emailsubject = NULL;
01770 
01771       ast_free(vmu);
01772    }
01773 }
01774 
01775 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01776 
01777    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01778 
01779    /* remove old allocation */
01780    if (vms->deleted) {
01781       ast_free(vms->deleted);
01782       vms->deleted = NULL;
01783    }
01784    if (vms->heard) {
01785       ast_free(vms->heard);
01786       vms->heard = NULL;
01787    }
01788    vms->dh_arraysize = 0;
01789 
01790    if (arraysize > 0) {
01791       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01792          return -1;
01793       }
01794       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01795          ast_free(vms->deleted);
01796          vms->deleted = NULL;
01797          return -1;
01798       }
01799       vms->dh_arraysize = arraysize;
01800    }
01801 
01802    return 0;
01803 }
01804 
01805 /* All IMAP-specific functions should go in this block. This
01806  * keeps them from being spread out all over the code */
01807 #ifdef IMAP_STORAGE
01808 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01809 {
01810    char arg[10];
01811    struct vm_state *vms;
01812    unsigned long messageNum;
01813 
01814    /* If greetings aren't stored in IMAP, just delete the file */
01815    if (msgnum < 0 && !imapgreetings) {
01816       ast_filedelete(file, NULL);
01817       return;
01818    }
01819 
01820    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01821       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);
01822       return;
01823    }
01824 
01825    if (msgnum < 0) {
01826       imap_delete_old_greeting(file, vms);
01827       return;
01828    }
01829 
01830    /* find real message number based on msgnum */
01831    /* this may be an index into vms->msgArray based on the msgnum. */
01832    messageNum = vms->msgArray[msgnum];
01833    if (messageNum == 0) {
01834       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01835       return;
01836    }
01837    if (option_debug > 2)
01838       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01839    /* delete message */
01840    snprintf (arg, sizeof(arg), "%lu", messageNum);
01841    ast_mutex_lock(&vms->lock);
01842    mail_setflag (vms->mailstream, arg, "\\DELETED");
01843    mail_expunge(vms->mailstream);
01844    ast_mutex_unlock(&vms->lock);
01845 }
01846 
01847 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01848 {
01849    struct vm_state *vms_p;
01850    char *file, *filename;
01851    char *attachment;
01852    int i;
01853    BODY *body;
01854 
01855    /* This function is only used for retrieval of IMAP greetings
01856     * regular messages are not retrieved this way, nor are greetings
01857     * if they are stored locally*/
01858    if (msgnum > -1 || !imapgreetings) {
01859       return 0;
01860    } else {
01861       file = strrchr(ast_strdupa(dir), '/');
01862       if (file)
01863          *file++ = '\0';
01864       else {
01865          ast_debug (1, "Failed to procure file name from directory passed.\n");
01866          return -1;
01867       }
01868    }
01869 
01870    /* check if someone is accessing this box right now... */
01871    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01872       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01873       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01874       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01875       * that's all we need to do.
01876       */
01877       if (!(vms_p = create_vm_state_from_user(vmu))) {
01878          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01879          return -1;
01880       }
01881    }
01882 
01883    /* Greetings will never have a prepended message */
01884    *vms_p->introfn = '\0';
01885 
01886    ast_mutex_lock(&vms_p->lock);
01887    init_mailstream(vms_p, GREETINGS_FOLDER);
01888    if (!vms_p->mailstream) {
01889       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01890       ast_mutex_unlock(&vms_p->lock);
01891       return -1;
01892    }
01893 
01894    /*XXX Yuck, this could probably be done a lot better */
01895    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01896       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01897       /* We have the body, now we extract the file name of the first attachment. */
01898       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01899          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01900       } else {
01901          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01902          ast_mutex_unlock(&vms_p->lock);
01903          return -1;
01904       }
01905       filename = strsep(&attachment, ".");
01906       if (!strcmp(filename, file)) {
01907          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01908          vms_p->msgArray[vms_p->curmsg] = i + 1;
01909          save_body(body, vms_p, "2", attachment, 0);
01910          ast_mutex_unlock(&vms_p->lock);
01911          return 0;
01912       }
01913    }
01914    ast_mutex_unlock(&vms_p->lock);
01915 
01916    return -1;
01917 }
01918 
01919 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01920 {
01921    BODY *body;
01922    char *header_content;
01923    char *attachedfilefmt;
01924    char buf[80];
01925    struct vm_state *vms;
01926    char text_file[PATH_MAX];
01927    FILE *text_file_ptr;
01928    int res = 0;
01929    struct ast_vm_user *vmu;
01930 
01931    if (!(vmu = find_user(NULL, context, mailbox))) {
01932       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01933       return -1;
01934    }
01935    
01936    if (msgnum < 0) {
01937       if (imapgreetings) {
01938          res = imap_retrieve_greeting(dir, msgnum, vmu);
01939          goto exit;
01940       } else {
01941          res = 0;
01942          goto exit;
01943       }
01944    }
01945 
01946    /* Before anything can happen, we need a vm_state so that we can
01947     * actually access the imap server through the vms->mailstream
01948     */
01949    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01950       /* This should not happen. If it does, then I guess we'd
01951        * need to create the vm_state, extract which mailbox to
01952        * open, and then set up the msgArray so that the correct
01953        * IMAP message could be accessed. If I have seen correctly
01954        * though, the vms should be obtainable from the vmstates list
01955        * and should have its msgArray properly set up.
01956        */
01957       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01958       res = -1;
01959       goto exit;
01960    }
01961    
01962    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01963    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01964 
01965    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01966    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01967       res = 0;
01968       goto exit;
01969    }
01970 
01971    if (option_debug > 2)
01972       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01973    if (vms->msgArray[msgnum] == 0) {
01974       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01975       res = -1;
01976       goto exit;
01977    }
01978 
01979    /* This will only work for new messages... */
01980    ast_mutex_lock(&vms->lock);
01981    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01982    ast_mutex_unlock(&vms->lock);
01983    /* empty string means no valid header */
01984    if (ast_strlen_zero(header_content)) {
01985       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01986       res = -1;
01987       goto exit;
01988    }
01989 
01990    ast_mutex_lock(&vms->lock);
01991    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01992    ast_mutex_unlock(&vms->lock);
01993 
01994    /* We have the body, now we extract the file name of the first attachment. */
01995    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01996       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01997    } else {
01998       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01999       res = -1;
02000       goto exit;
02001    }
02002    
02003    /* Find the format of the attached file */
02004 
02005    strsep(&attachedfilefmt, ".");
02006    if (!attachedfilefmt) {
02007       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
02008       res = -1;
02009       goto exit;
02010    }
02011    
02012    save_body(body, vms, "2", attachedfilefmt, 0);
02013    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02014       *vms->introfn = '\0';
02015    }
02016 
02017    /* Get info from headers!! */
02018    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02019 
02020    if (!(text_file_ptr = fopen(text_file, "w"))) {
02021       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02022    }
02023 
02024    fprintf(text_file_ptr, "%s\n", "[message]");
02025 
02026    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02027    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02028    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02029    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02030    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02031    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02032    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02033    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02034    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02035    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02036    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02037    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02038    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02039    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02040    fclose(text_file_ptr);
02041 
02042 exit:
02043    free_user(vmu);
02044    return res;
02045 }
02046 
02047 static int folder_int(const char *folder)
02048 {
02049    /*assume a NULL folder means INBOX*/
02050    if (!folder) {
02051       return 0;
02052    }
02053    if (!strcasecmp(folder, imapfolder)) {
02054       return 0;
02055    } else if (!strcasecmp(folder, "Old")) {
02056       return 1;
02057    } else if (!strcasecmp(folder, "Work")) {
02058       return 2;
02059    } else if (!strcasecmp(folder, "Family")) {
02060       return 3;
02061    } else if (!strcasecmp(folder, "Friends")) {
02062       return 4;
02063    } else if (!strcasecmp(folder, "Cust1")) {
02064       return 5;
02065    } else if (!strcasecmp(folder, "Cust2")) {
02066       return 6;
02067    } else if (!strcasecmp(folder, "Cust3")) {
02068       return 7;
02069    } else if (!strcasecmp(folder, "Cust4")) {
02070       return 8;
02071    } else if (!strcasecmp(folder, "Cust5")) {
02072       return 9;
02073    } else if (!strcasecmp(folder, "Urgent")) {
02074       return 11;
02075    } else { /*assume they meant INBOX if folder is not found otherwise*/
02076       return 0;
02077    }
02078 }
02079 
02080 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02081 {
02082    SEARCHPGM *pgm;
02083    SEARCHHEADER *hdr;
02084 
02085    struct ast_vm_user *vmu, vmus;
02086    struct vm_state *vms_p;
02087    int ret = 0;
02088    int fold = folder_int(folder);
02089    int urgent = 0;
02090    
02091    /* If URGENT, then look at INBOX */
02092    if (fold == 11) {
02093       fold = NEW_FOLDER;
02094       urgent = 1;
02095    }
02096 
02097    if (ast_strlen_zero(mailbox))
02098       return 0;
02099 
02100    /* We have to get the user before we can open the stream! */
02101    vmu = find_user(&vmus, context, mailbox);
02102    if (!vmu) {
02103       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02104       return -1;
02105    } else {
02106       /* No IMAP account available */
02107       if (vmu->imapuser[0] == '\0') {
02108          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02109          return -1;
02110       }
02111    }
02112    
02113    /* No IMAP account available */
02114    if (vmu->imapuser[0] == '\0') {
02115       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02116       free_user(vmu);
02117       return -1;
02118    }
02119    ast_assert(msgnum < vms->msg_array_max);
02120 
02121    /* check if someone is accessing this box right now... */
02122    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02123    if (!vms_p) {
02124       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02125    }
02126    if (vms_p) {
02127       ast_debug(3, "Returning before search - user is logged in\n");
02128       if (fold == 0) { /* INBOX */
02129          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02130       }
02131       if (fold == 1) { /* Old messages */
02132          return vms_p->oldmessages;
02133       }
02134    }
02135 
02136    /* add one if not there... */
02137    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02138    if (!vms_p) {
02139       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02140    }
02141 
02142    if (!vms_p) {
02143       vms_p = create_vm_state_from_user(vmu);
02144    }
02145    ret = init_mailstream(vms_p, fold);
02146    if (!vms_p->mailstream) {
02147       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02148       return -1;
02149    }
02150    if (ret == 0) {
02151       ast_mutex_lock(&vms_p->lock);
02152       pgm = mail_newsearchpgm ();
02153       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02154       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02155       pgm->header = hdr;
02156       if (fold != OLD_FOLDER) {
02157          pgm->unseen = 1;
02158          pgm->seen = 0;
02159       }
02160       /* In the special case where fold is 1 (old messages) we have to do things a bit
02161        * differently. Old messages are stored in the INBOX but are marked as "seen"
02162        */
02163       else {
02164          pgm->unseen = 0;
02165          pgm->seen = 1;
02166       }
02167       /* look for urgent messages */
02168       if (fold == NEW_FOLDER) {
02169          if (urgent) {
02170             pgm->flagged = 1;
02171             pgm->unflagged = 0;
02172          } else {
02173             pgm->flagged = 0;
02174             pgm->unflagged = 1;
02175          }
02176       }
02177       pgm->undeleted = 1;
02178       pgm->deleted = 0;
02179 
02180       vms_p->vmArrayIndex = 0;
02181       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02182       if (fold == 0 && urgent == 0)
02183          vms_p->newmessages = vms_p->vmArrayIndex;
02184       if (fold == 1)
02185          vms_p->oldmessages = vms_p->vmArrayIndex;
02186       if (fold == 0 && urgent == 1)
02187          vms_p->urgentmessages = vms_p->vmArrayIndex;
02188       /*Freeing the searchpgm also frees the searchhdr*/
02189       mail_free_searchpgm(&pgm);
02190       ast_mutex_unlock(&vms_p->lock);
02191       vms_p->updated = 0;
02192       return vms_p->vmArrayIndex;
02193    } else {
02194       ast_mutex_lock(&vms_p->lock);
02195       mail_ping(vms_p->mailstream);
02196       ast_mutex_unlock(&vms_p->lock);
02197    }
02198    return 0;
02199 }
02200 
02201 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02202 {
02203    /* Check if mailbox is full */
02204    check_quota(vms, vmu->imapfolder);
02205    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02206       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02207       ast_play_and_wait(chan, "vm-mailboxfull");
02208       return -1;
02209    }
02210    
02211    /* Check if we have exceeded maxmsg */
02212    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));
02213    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02214       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02215       ast_play_and_wait(chan, "vm-mailboxfull");
02216       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02217       return -1;
02218    }
02219 
02220    return 0;
02221 }
02222 
02223 /*!
02224  * \brief Gets the number of messages that exist in a mailbox folder.
02225  * \param context
02226  * \param mailbox
02227  * \param folder
02228  * 
02229  * This method is used when IMAP backend is used.
02230  * \return The number of messages in this mailbox folder (zero or more).
02231  */
02232 static int messagecount(const char *context, const char *mailbox, const char *folder)
02233 {
02234    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02235       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02236    } else {
02237       return __messagecount(context, mailbox, folder);
02238    }
02239 }
02240 
02241 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)
02242 {
02243    char *myserveremail = serveremail;
02244    char fn[PATH_MAX];
02245    char introfn[PATH_MAX];
02246    char mailbox[256];
02247    char *stringp;
02248    FILE *p = NULL;
02249    char tmp[80] = "/tmp/astmail-XXXXXX";
02250    long len;
02251    void *buf;
02252    int tempcopy = 0;
02253    STRING str;
02254    int ret; /* for better error checking */
02255    char *imap_flags = NIL;
02256    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02257    int box = NEW_FOLDER;
02258 
02259    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02260    if (msgnum < 0) {
02261       if(!imapgreetings) {
02262          return 0;
02263       } else {
02264          box = GREETINGS_FOLDER;
02265       }
02266    }
02267    
02268    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02269       return -1;
02270    }
02271 
02272    /* Set urgent flag for IMAP message */
02273    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02274       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02275       imap_flags = "\\FLAGGED";
02276    }
02277    
02278    /* Attach only the first format */
02279    fmt = ast_strdupa(fmt);
02280    stringp = fmt;
02281    strsep(&stringp, "|");
02282 
02283    if (!ast_strlen_zero(vmu->serveremail))
02284       myserveremail = vmu->serveremail;
02285 
02286    if (msgnum > -1)
02287       make_file(fn, sizeof(fn), dir, msgnum);
02288    else
02289       ast_copy_string (fn, dir, sizeof(fn));
02290 
02291    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02292    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02293       *introfn = '\0';
02294    }
02295    
02296    if (ast_strlen_zero(vmu->email)) {
02297       /* We need the vmu->email to be set when we call make_email_file, but
02298        * if we keep it set, a duplicate e-mail will be created. So at the end
02299        * of this function, we will revert back to an empty string if tempcopy
02300        * is 1.
02301        */
02302       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02303       tempcopy = 1;
02304    }
02305 
02306    if (!strcmp(fmt, "wav49"))
02307       fmt = "WAV";
02308    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02309 
02310    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02311       command hangs. */
02312    if (!(p = vm_mkftemp(tmp))) {
02313       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02314       if (tempcopy)
02315          *(vmu->email) = '\0';
02316       return -1;
02317    }
02318 
02319    if (msgnum < 0 && imapgreetings) {
02320       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02321          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02322          return -1;
02323       }
02324       imap_delete_old_greeting(fn, vms);
02325    }
02326 
02327    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02328       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02329       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02330       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02331    /* read mail file to memory */
02332    len = ftell(p);
02333    rewind(p);
02334    if (!(buf = ast_malloc(len + 1))) {
02335       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02336       fclose(p);
02337       if (tempcopy)
02338          *(vmu->email) = '\0';
02339       return -1;
02340    }
02341    if (fread(buf, len, 1, p) < len) {
02342       if (ferror(p)) {
02343          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02344          return -1;
02345       }
02346    }
02347    ((char *) buf)[len] = '\0';
02348    INIT(&str, mail_string, buf, len);
02349    ret = init_mailstream(vms, box);
02350    if (ret == 0) {
02351       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02352       ast_mutex_lock(&vms->lock);
02353       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02354          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02355       ast_mutex_unlock(&vms->lock);
02356       fclose(p);
02357       unlink(tmp);
02358       ast_free(buf);
02359    } else {
02360       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02361       fclose(p);
02362       unlink(tmp);
02363       ast_free(buf);
02364       return -1;
02365    }
02366    ast_debug(3, "%s stored\n", fn);
02367    
02368    if (tempcopy)
02369       *(vmu->email) = '\0';
02370    inprocess_count(vmu->mailbox, vmu->context, -1);
02371    return 0;
02372 
02373 }
02374 
02375 /*!
02376  * \brief Gets the number of messages that exist in the inbox folder.
02377  * \param mailbox_context
02378  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02379  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02380  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02381  * 
02382  * This method is used when IMAP backend is used.
02383  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02384  *
02385  * \return zero on success, -1 on error.
02386  */
02387 
02388 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02389 {
02390    char tmp[PATH_MAX] = "";
02391    char *mailboxnc;
02392    char *context;
02393    char *mb;
02394    char *cur;
02395    if (newmsgs)
02396       *newmsgs = 0;
02397    if (oldmsgs)
02398       *oldmsgs = 0;
02399    if (urgentmsgs)
02400       *urgentmsgs = 0;
02401 
02402    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02403    /* If no mailbox, return immediately */
02404    if (ast_strlen_zero(mailbox_context))
02405       return 0;
02406    
02407    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02408    context = strchr(tmp, '@');
02409    if (strchr(mailbox_context, ',')) {
02410       int tmpnew, tmpold, tmpurgent;
02411       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02412       mb = tmp;
02413       while ((cur = strsep(&mb, ", "))) {
02414          if (!ast_strlen_zero(cur)) {
02415             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02416                return -1;
02417             else {
02418                if (newmsgs)
02419                   *newmsgs += tmpnew; 
02420                if (oldmsgs)
02421                   *oldmsgs += tmpold;
02422                if (urgentmsgs)
02423                   *urgentmsgs += tmpurgent;
02424             }
02425          }
02426       }
02427       return 0;
02428    }
02429    if (context) {
02430       *context = '\0';
02431       mailboxnc = tmp;
02432       context++;
02433    } else {
02434       context = "default";
02435       mailboxnc = (char *) mailbox_context;
02436    }
02437 
02438    if (newmsgs) {
02439       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02440       if (!vmu) {
02441          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02442          return -1;
02443       }
02444       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02445          free_user(vmu);
02446          return -1;
02447       }
02448       free_user(vmu);
02449    }
02450    if (oldmsgs) {
02451       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02452          return -1;
02453       }
02454    }
02455    if (urgentmsgs) {
02456       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02457          return -1;
02458       }
02459    }
02460    return 0;
02461 }
02462 
02463 /** 
02464  * \brief Determines if the given folder has messages.
02465  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02466  * \param folder the folder to look in
02467  *
02468  * This function is used when the mailbox is stored in an IMAP back end.
02469  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02470  * \return 1 if the folder has one or more messages. zero otherwise.
02471  */
02472 
02473 static int has_voicemail(const char *mailbox, const char *folder)
02474 {
02475    char tmp[256], *tmp2, *box, *context;
02476    ast_copy_string(tmp, mailbox, sizeof(tmp));
02477    tmp2 = tmp;
02478    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02479       while ((box = strsep(&tmp2, ",&"))) {
02480          if (!ast_strlen_zero(box)) {
02481             if (has_voicemail(box, folder)) {
02482                return 1;
02483             }
02484          }
02485       }
02486    }
02487    if ((context = strchr(tmp, '@'))) {
02488       *context++ = '\0';
02489    } else {
02490       context = "default";
02491    }
02492    return __messagecount(context, tmp, folder) ? 1 : 0;
02493 }
02494 
02495 /*!
02496  * \brief Copies a message from one mailbox to another.
02497  * \param chan
02498  * \param vmu
02499  * \param imbox
02500  * \param msgnum
02501  * \param duration
02502  * \param recip
02503  * \param fmt
02504  * \param dir
02505  *
02506  * This works with IMAP storage based mailboxes.
02507  *
02508  * \return zero on success, -1 on error.
02509  */
02510 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)
02511 {
02512    struct vm_state *sendvms = NULL, *destvms = NULL;
02513    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02514    if (msgnum >= recip->maxmsg) {
02515       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02516       return -1;
02517    }
02518    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02519       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02520       return -1;
02521    }
02522    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02523       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02524       return -1;
02525    }
02526    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02527    ast_mutex_lock(&sendvms->lock);
02528    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02529       ast_mutex_unlock(&sendvms->lock);
02530       return 0;
02531    }
02532    ast_mutex_unlock(&sendvms->lock);
02533    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02534    return -1;
02535 }
02536 
02537 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02538 {
02539    char tmp[256], *t = tmp;
02540    size_t left = sizeof(tmp);
02541    
02542    if (box == OLD_FOLDER) {
02543       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02544    } else {
02545       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02546    }
02547 
02548    if (box == NEW_FOLDER) {
02549       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02550    } else {
02551       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02552    }
02553 
02554    /* Build up server information */
02555    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02556 
02557    /* Add authentication user if present */
02558    if (!ast_strlen_zero(authuser))
02559       ast_build_string(&t, &left, "/authuser=%s", authuser);
02560 
02561    /* Add flags if present */
02562    if (!ast_strlen_zero(imapflags))
02563       ast_build_string(&t, &left, "/%s", imapflags);
02564 
02565    /* End with username */
02566 #if 1
02567    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02568 #else
02569    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02570 #endif
02571    if (box == NEW_FOLDER || box == OLD_FOLDER)
02572       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02573    else if (box == GREETINGS_FOLDER)
02574       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02575    else {   /* Other folders such as Friends, Family, etc... */
02576       if (!ast_strlen_zero(imapparentfolder)) {
02577          /* imapparentfolder would typically be set to INBOX */
02578          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02579       } else {
02580          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02581       }
02582    }
02583 }
02584 
02585 static int init_mailstream(struct vm_state *vms, int box)
02586 {
02587    MAILSTREAM *stream = NIL;
02588    long debug;
02589    char tmp[256];
02590    
02591    if (!vms) {
02592       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02593       return -1;
02594    }
02595    if (option_debug > 2)
02596       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02597    if (vms->mailstream == NIL || !vms->mailstream) {
02598       if (option_debug)
02599          ast_log(LOG_DEBUG, "mailstream not set.\n");
02600    } else {
02601       stream = vms->mailstream;
02602    }
02603    /* debug = T;  user wants protocol telemetry? */
02604    debug = NIL;  /* NO protocol telemetry? */
02605 
02606    if (delimiter == '\0') {      /* did not probe the server yet */
02607       char *cp;
02608 #ifdef USE_SYSTEM_IMAP
02609 #include <imap/linkage.c>
02610 #elif defined(USE_SYSTEM_CCLIENT)
02611 #include <c-client/linkage.c>
02612 #else
02613 #include "linkage.c"
02614 #endif
02615       /* Connect to INBOX first to get folders delimiter */
02616       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02617       ast_mutex_lock(&vms->lock);
02618       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02619       ast_mutex_unlock(&vms->lock);
02620       if (stream == NIL) {
02621          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02622          return -1;
02623       }
02624       get_mailbox_delimiter(stream);
02625       /* update delimiter in imapfolder */
02626       for (cp = vms->imapfolder; *cp; cp++)
02627          if (*cp == '/')
02628             *cp = delimiter;
02629    }
02630    /* Now connect to the target folder */
02631    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02632    if (option_debug > 2)
02633       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02634    ast_mutex_lock(&vms->lock);
02635    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02636    ast_mutex_unlock(&vms->lock);
02637    if (vms->mailstream == NIL) {
02638       return -1;
02639    } else {
02640       return 0;
02641    }
02642 }
02643 
02644 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02645 {
02646    SEARCHPGM *pgm;
02647    SEARCHHEADER *hdr;
02648    int ret, urgent = 0;
02649 
02650    /* If Urgent, then look at INBOX */
02651    if (box == 11) {
02652       box = NEW_FOLDER;
02653       urgent = 1;
02654    }
02655 
02656    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02657    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02658    vms->imapversion = vmu->imapversion;
02659    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02660 
02661    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02662       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02663       return -1;
02664    }
02665    
02666    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02667    
02668    /* Check Quota */
02669    if  (box == 0)  {
02670       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02671       check_quota(vms, (char *) mbox(vmu, box));
02672    }
02673 
02674    ast_mutex_lock(&vms->lock);
02675    pgm = mail_newsearchpgm();
02676 
02677    /* Check IMAP folder for Asterisk messages only... */
02678    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02679    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02680    pgm->header = hdr;
02681    pgm->deleted = 0;
02682    pgm->undeleted = 1;
02683 
02684    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02685    if (box == NEW_FOLDER && urgent == 1) {
02686       pgm->unseen = 1;
02687       pgm->seen = 0;
02688       pgm->flagged = 1;
02689       pgm->unflagged = 0;
02690    } else if (box == NEW_FOLDER && urgent == 0) {
02691       pgm->unseen = 1;
02692       pgm->seen = 0;
02693       pgm->flagged = 0;
02694       pgm->unflagged = 1;
02695    } else if (box == OLD_FOLDER) {
02696       pgm->seen = 1;
02697       pgm->unseen = 0;
02698    }
02699 
02700    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02701 
02702    vms->vmArrayIndex = 0;
02703    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02704    vms->lastmsg = vms->vmArrayIndex - 1;
02705    mail_free_searchpgm(&pgm);
02706    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02707     * ensure to allocate enough space to account for all of them. Warn if old messages
02708     * have not been checked first as that is required.
02709     */
02710    if (box == 0 && !vms->dh_arraysize) {
02711       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02712    }
02713    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02714       ast_mutex_unlock(&vms->lock);
02715       return -1;
02716    }
02717 
02718    ast_mutex_unlock(&vms->lock);
02719    return 0;
02720 }
02721 
02722 static void write_file(char *filename, char *buffer, unsigned long len)
02723 {
02724    FILE *output;
02725 
02726    output = fopen (filename, "w");
02727    if (fwrite(buffer, len, 1, output) != 1) {
02728       if (ferror(output)) {
02729          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02730       }
02731    }
02732    fclose (output);
02733 }
02734 
02735 static void update_messages_by_imapuser(const char *user, unsigned long number)
02736 {
02737    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02738 
02739    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02740       return;
02741    }
02742 
02743    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02744 
02745    /* Ensure we have room for the next message. */
02746    if (vms->vmArrayIndex >= vms->msg_array_max) {
02747       long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
02748       if (!new_mem) {
02749          return;
02750       }
02751       vms->msgArray = new_mem;
02752       vms->msg_array_max *= 2;
02753    }
02754 
02755    vms->msgArray[vms->vmArrayIndex++] = number;
02756 }
02757 
02758 void mm_searched(MAILSTREAM *stream, unsigned long number)
02759 {
02760    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02761 
02762    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02763       return;
02764 
02765    update_messages_by_imapuser(user, number);
02766 }
02767 
02768 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02769 {
02770    struct ast_variable *var;
02771    struct ast_vm_user *vmu;
02772 
02773    vmu = ast_calloc(1, sizeof *vmu);
02774    if (!vmu)
02775       return NULL;
02776 
02777    populate_defaults(vmu);
02778    ast_set_flag(vmu, VM_ALLOCED);
02779 
02780    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02781    if (var) {
02782       apply_options_full(vmu, var);
02783       ast_variables_destroy(var);
02784       return vmu;
02785    } else {
02786       ast_free(vmu);
02787       return NULL;
02788    }
02789 }
02790 
02791 /* Interfaces to C-client */
02792 
02793 void mm_exists(MAILSTREAM * stream, unsigned long number)
02794 {
02795    /* mail_ping will callback here if new mail! */
02796    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02797    if (number == 0) return;
02798    set_update(stream);
02799 }
02800 
02801 
02802 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02803 {
02804    /* mail_ping will callback here if expunged mail! */
02805    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02806    if (number == 0) return;
02807    set_update(stream);
02808 }
02809 
02810 
02811 void mm_flags(MAILSTREAM * stream, unsigned long number)
02812 {
02813    /* mail_ping will callback here if read mail! */
02814    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02815    if (number == 0) return;
02816    set_update(stream);
02817 }
02818 
02819 
02820 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02821 {
02822    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02823    mm_log (string, errflg);
02824 }
02825 
02826 
02827 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02828 {
02829    if (delimiter == '\0') {
02830       delimiter = delim;
02831    }
02832 
02833    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02834    if (attributes & LATT_NOINFERIORS)
02835       ast_debug(5, "no inferiors\n");
02836    if (attributes & LATT_NOSELECT)
02837       ast_debug(5, "no select\n");
02838    if (attributes & LATT_MARKED)
02839       ast_debug(5, "marked\n");
02840    if (attributes & LATT_UNMARKED)
02841       ast_debug(5, "unmarked\n");
02842 }
02843 
02844 
02845 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02846 {
02847    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02848    if (attributes & LATT_NOINFERIORS)
02849       ast_debug(5, "no inferiors\n");
02850    if (attributes & LATT_NOSELECT)
02851       ast_debug(5, "no select\n");
02852    if (attributes & LATT_MARKED)
02853       ast_debug(5, "marked\n");
02854    if (attributes & LATT_UNMARKED)
02855       ast_debug(5, "unmarked\n");
02856 }
02857 
02858 
02859 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02860 {
02861    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02862    if (status->flags & SA_MESSAGES)
02863       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02864    if (status->flags & SA_RECENT)
02865       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02866    if (status->flags & SA_UNSEEN)
02867       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02868    if (status->flags & SA_UIDVALIDITY)
02869       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02870    if (status->flags & SA_UIDNEXT)
02871       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02872    ast_log(AST_LOG_NOTICE, "\n");
02873 }
02874 
02875 
02876 void mm_log(char *string, long errflg)
02877 {
02878    switch ((short) errflg) {
02879       case NIL:
02880          ast_debug(1, "IMAP Info: %s\n", string);
02881          break;
02882       case PARSE:
02883       case WARN:
02884          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02885          break;
02886       case ERROR:
02887          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02888          break;
02889    }
02890 }
02891 
02892 
02893 void mm_dlog(char *string)
02894 {
02895    ast_log(AST_LOG_NOTICE, "%s\n", string);
02896 }
02897 
02898 
02899 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02900 {
02901    struct ast_vm_user *vmu;
02902 
02903    ast_debug(4, "Entering callback mm_login\n");
02904 
02905    ast_copy_string(user, mb->user, MAILTMPLEN);
02906 
02907    /* We should only do this when necessary */
02908    if (!ast_strlen_zero(authpassword)) {
02909       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02910    } else {
02911       AST_LIST_TRAVERSE(&users, vmu, list) {
02912          if (!strcasecmp(mb->user, vmu->imapuser)) {
02913             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02914             break;
02915          }
02916       }
02917       if (!vmu) {
02918          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02919             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02920             free_user(vmu);
02921          }
02922       }
02923    }
02924 }
02925 
02926 
02927 void mm_critical(MAILSTREAM * stream)
02928 {
02929 }
02930 
02931 
02932 void mm_nocritical(MAILSTREAM * stream)
02933 {
02934 }
02935 
02936 
02937 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02938 {
02939    kill (getpid (), SIGSTOP);
02940    return NIL;
02941 }
02942 
02943 
02944 void mm_fatal(char *string)
02945 {
02946    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02947 }
02948 
02949 /* C-client callback to handle quota */
02950 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02951 {
02952    struct vm_state *vms;
02953    char *mailbox = stream->mailbox, *user;
02954    char buf[1024] = "";
02955    unsigned long usage = 0, limit = 0;
02956    
02957    while (pquota) {
02958       usage = pquota->usage;
02959       limit = pquota->limit;
02960       pquota = pquota->next;
02961    }
02962    
02963    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)))) {
02964       ast_log(AST_LOG_ERROR, "No state found.\n");
02965       return;
02966    }
02967 
02968    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02969 
02970    vms->quota_usage = usage;
02971    vms->quota_limit = limit;
02972 }
02973 
02974 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02975 {
02976    char *start, *eol_pnt;
02977    int taglen;
02978 
02979    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02980       return NULL;
02981 
02982    taglen = strlen(tag) + 1;
02983    if (taglen < 1)
02984       return NULL;
02985 
02986    if (!(start = strstr(header, tag)))
02987       return NULL;
02988 
02989    /* Since we can be called multiple times we should clear our buffer */
02990    memset(buf, 0, len);
02991 
02992    ast_copy_string(buf, start+taglen, len);
02993    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02994       *eol_pnt = '\0';
02995    return buf;
02996 }
02997 
02998 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02999 {
03000    char *start, *quote, *eol_pnt;
03001 
03002    if (ast_strlen_zero(mailbox))
03003       return NULL;
03004 
03005    if (!(start = strstr(mailbox, "/user=")))
03006       return NULL;
03007 
03008    ast_copy_string(buf, start+6, len);
03009 
03010    if (!(quote = strchr(buf, '\"'))) {
03011       if (!(eol_pnt = strchr(buf, '/')))
03012          eol_pnt = strchr(buf,'}');
03013       *eol_pnt = '\0';
03014       return buf;
03015    } else {
03016       eol_pnt = strchr(buf+1,'\"');
03017       *eol_pnt = '\0';
03018       return buf+1;
03019    }
03020 }
03021 
03022 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
03023 {
03024    struct vm_state *vms_p;
03025 
03026    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03027    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03028       return vms_p;
03029    }
03030    if (option_debug > 4)
03031       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
03032    /* XXX: Is this correctly freed always? */
03033    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03034       return NULL;
03035    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03036    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03037    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03038    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03039    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03040    vms_p->imapversion = vmu->imapversion;
03041    if (option_debug > 4)
03042       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03043    vms_p->updated = 1;
03044    /* set mailbox to INBOX! */
03045    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03046    init_vm_state(vms_p);
03047    vmstate_insert(vms_p);
03048    return vms_p;
03049 }
03050 
03051 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03052 {
03053    struct vmstate *vlist = NULL;
03054 
03055    if (interactive) {
03056       struct vm_state *vms;
03057       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03058       vms = pthread_getspecific(ts_vmstate.key);
03059       return vms;
03060    }
03061 
03062    AST_LIST_LOCK(&vmstates);
03063    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03064       if (!vlist->vms) {
03065          ast_debug(3, "error: vms is NULL for %s\n", user);
03066          continue;
03067       }
03068       if (vlist->vms->imapversion != imapversion) {
03069          continue;
03070       }
03071       if (!vlist->vms->imapuser) {
03072          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03073          continue;
03074       }
03075 
03076       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03077          AST_LIST_UNLOCK(&vmstates);
03078          return vlist->vms;
03079       }
03080    }
03081    AST_LIST_UNLOCK(&vmstates);
03082 
03083    ast_debug(3, "%s not found in vmstates\n", user);
03084 
03085    return NULL;
03086 }
03087 
03088 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03089 {
03090 
03091    struct vmstate *vlist = NULL;
03092    const char *local_context = S_OR(context, "default");
03093 
03094    if (interactive) {
03095       struct vm_state *vms;
03096       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03097       vms = pthread_getspecific(ts_vmstate.key);
03098       return vms;
03099    }
03100 
03101    AST_LIST_LOCK(&vmstates);
03102    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03103       if (!vlist->vms) {
03104          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03105          continue;
03106       }
03107       if (vlist->vms->imapversion != imapversion) {
03108          continue;
03109       }
03110       if (!vlist->vms->username || !vlist->vms->context) {
03111          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03112          continue;
03113       }
03114 
03115       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);
03116       
03117       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03118          ast_debug(3, "Found it!\n");
03119          AST_LIST_UNLOCK(&vmstates);
03120          return vlist->vms;
03121       }
03122    }
03123    AST_LIST_UNLOCK(&vmstates);
03124 
03125    ast_debug(3, "%s not found in vmstates\n", mailbox);
03126 
03127    return NULL;
03128 }
03129 
03130 static void vmstate_insert(struct vm_state *vms) 
03131 {
03132    struct vmstate *v;
03133    struct vm_state *altvms;
03134 
03135    /* If interactive, it probably already exists, and we should
03136       use the one we already have since it is more up to date.
03137       We can compare the username to find the duplicate */
03138    if (vms->interactive == 1) {
03139       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03140       if (altvms) {  
03141          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03142          vms->newmessages = altvms->newmessages;
03143          vms->oldmessages = altvms->oldmessages;
03144          vms->vmArrayIndex = altvms->vmArrayIndex;
03145          /* XXX: no msgArray copying? */
03146          vms->lastmsg = altvms->lastmsg;
03147          vms->curmsg = altvms->curmsg;
03148          /* get a pointer to the persistent store */
03149          vms->persist_vms = altvms;
03150          /* Reuse the mailstream? */
03151 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03152          vms->mailstream = altvms->mailstream;
03153 #else
03154          vms->mailstream = NIL;
03155 #endif
03156       }
03157       return;
03158    }
03159 
03160    if (!(v = ast_calloc(1, sizeof(*v))))
03161       return;
03162    
03163    v->vms = vms;
03164 
03165    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03166 
03167    AST_LIST_LOCK(&vmstates);
03168    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03169    AST_LIST_UNLOCK(&vmstates);
03170 }
03171 
03172 static void vmstate_delete(struct vm_state *vms) 
03173 {
03174    struct vmstate *vc = NULL;
03175    struct vm_state *altvms = NULL;
03176 
03177    /* If interactive, we should copy pertinent info
03178       back to the persistent state (to make update immediate) */
03179    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03180       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03181       altvms->newmessages = vms->newmessages;
03182       altvms->oldmessages = vms->oldmessages;
03183       altvms->updated = 1;
03184       vms->mailstream = mail_close(vms->mailstream);
03185 
03186       /* Interactive states are not stored within the persistent list */
03187       return;
03188    }
03189    
03190    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03191    
03192    AST_LIST_LOCK(&vmstates);
03193    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03194       if (vc->vms == vms) {
03195          AST_LIST_REMOVE_CURRENT(list);
03196          break;
03197       }
03198    }
03199    AST_LIST_TRAVERSE_SAFE_END
03200    AST_LIST_UNLOCK(&vmstates);
03201    
03202    if (vc) {
03203       ast_mutex_destroy(&vc->vms->lock);
03204       ast_free(vc->vms->msgArray);
03205       vc->vms->msgArray = NULL;
03206       vc->vms->msg_array_max = 0;
03207       /* XXX: is no one supposed to free vms itself? */
03208       ast_free(vc);
03209    } else {
03210       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03211    }
03212 }
03213 
03214 static void set_update(MAILSTREAM * stream) 
03215 {
03216    struct vm_state *vms;
03217    char *mailbox = stream->mailbox, *user;
03218    char buf[1024] = "";
03219 
03220    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03221       if (user && option_debug > 2)
03222          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03223       return;
03224    }
03225 
03226    ast_debug(3, "User %s mailbox set for update.\n", user);
03227 
03228    vms->updated = 1; /* Set updated flag since mailbox changed */
03229 }
03230 
03231 static void init_vm_state(struct vm_state *vms) 
03232 {
03233    vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
03234    vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
03235    if (!vms->msgArray) {
03236       /* Out of mem? This can't be good. */
03237       vms->msg_array_max = 0;
03238    }
03239    vms->vmArrayIndex = 0;
03240    ast_mutex_init(&vms->lock);
03241 }
03242 
03243 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03244 {
03245    char *body_content;
03246    char *body_decoded;
03247    char *fn = is_intro ? vms->introfn : vms->fn;
03248    unsigned long len;
03249    unsigned long newlen;
03250    char filename[256];
03251    
03252    if (!body || body == NIL)
03253       return -1;
03254 
03255    ast_mutex_lock(&vms->lock);
03256    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03257    ast_mutex_unlock(&vms->lock);
03258    if (body_content != NIL) {
03259       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03260       /* ast_debug(1,body_content); */
03261       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03262       /* If the body of the file is empty, return an error */
03263       if (!newlen) {
03264          return -1;
03265       }
03266       write_file(filename, (char *) body_decoded, newlen);
03267    } else {
03268       ast_debug(5, "Body of message is NULL.\n");
03269       return -1;
03270    }
03271    return 0;
03272 }
03273 
03274 /*! 
03275  * \brief Get delimiter via mm_list callback 
03276  * \param stream
03277  *
03278  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03279  */
03280 /* MUTEX should already be held */
03281 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03282    char tmp[50];
03283    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03284    mail_list(stream, tmp, "*");
03285 }
03286 
03287 /*! 
03288  * \brief Check Quota for user 
03289  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03290  * \param mailbox the mailbox to check the quota for.
03291  *
03292  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03293  */
03294 static void check_quota(struct vm_state *vms, char *mailbox) {
03295    ast_mutex_lock(&vms->lock);
03296    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03297    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03298    if (vms && vms->mailstream != NULL) {
03299       imap_getquotaroot(vms->mailstream, mailbox);
03300    } else {
03301       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03302    }
03303    ast_mutex_unlock(&vms->lock);
03304 }
03305 
03306 #endif /* IMAP_STORAGE */
03307 
03308 /*! \brief Lock file path
03309  * only return failure if ast_lock_path returns 'timeout',
03310  * not if the path does not exist or any other reason
03311  */
03312 static int vm_lock_path(const char *path)
03313 {
03314    switch (ast_lock_path(path)) {
03315    case AST_LOCK_TIMEOUT:
03316       return -1;
03317    default:
03318       return 0;
03319    }
03320 }
03321 
03322 
03323 #ifdef ODBC_STORAGE
03324 struct generic_prepare_struct {
03325    char *sql;
03326    int argc;
03327    char **argv;
03328 };
03329 
03330 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03331 {
03332    struct generic_prepare_struct *gps = data;
03333    int res, i;
03334    SQLHSTMT stmt;
03335 
03336    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03337    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03338       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03339       return NULL;
03340    }
03341    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03342    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03343       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03344       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03345       return NULL;
03346    }
03347    for (i = 0; i < gps->argc; i++)
03348       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03349 
03350    return stmt;
03351 }
03352 
03353 /*!
03354  * \brief Retrieves a file from an ODBC data store.
03355  * \param dir the path to the file to be retreived.
03356  * \param msgnum the message number, such as within a mailbox folder.
03357  * 
03358  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03359  * 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.
03360  *
03361  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03362  * The output is the message information file with the name msgnum and the extension .txt
03363  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03364  * 
03365  * \return 0 on success, -1 on error.
03366  */
03367 static int retrieve_file(char *dir, int msgnum)
03368 {
03369    int x = 0;
03370    int res;
03371    int fd = -1;
03372    size_t fdlen = 0;
03373    void *fdm = MAP_FAILED;
03374    SQLSMALLINT colcount = 0;
03375    SQLHSTMT stmt;
03376    char sql[PATH_MAX];
03377    char fmt[80]="";
03378    char *c;
03379    char coltitle[256];
03380    SQLSMALLINT collen;
03381    SQLSMALLINT datatype;
03382    SQLSMALLINT decimaldigits;
03383    SQLSMALLINT nullable;
03384    SQLULEN colsize;
03385    SQLLEN colsize2;
03386    FILE *f = NULL;
03387    char rowdata[80];
03388    char fn[PATH_MAX];
03389    char full_fn[PATH_MAX];
03390    char msgnums[80];
03391    char *argv[] = { dir, msgnums };
03392    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03393 
03394    struct odbc_obj *obj;
03395    obj = ast_odbc_request_obj(odbc_database, 0);
03396    if (obj) {
03397       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03398       c = strchr(fmt, '|');
03399       if (c)
03400          *c = '\0';
03401       if (!strcasecmp(fmt, "wav49"))
03402          strcpy(fmt, "WAV");
03403       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03404       if (msgnum > -1)
03405          make_file(fn, sizeof(fn), dir, msgnum);
03406       else
03407          ast_copy_string(fn, dir, sizeof(fn));
03408 
03409       /* Create the information file */
03410       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03411       
03412       if (!(f = fopen(full_fn, "w+"))) {
03413          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03414          goto yuck;
03415       }
03416       
03417       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03418       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03419       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03420       if (!stmt) {
03421          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03422          ast_odbc_release_obj(obj);
03423          goto yuck;
03424       }
03425       res = SQLFetch(stmt);
03426       if (res == SQL_NO_DATA) {
03427          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03428          ast_odbc_release_obj(obj);
03429          goto yuck;
03430       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03431          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03432          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03433          ast_odbc_release_obj(obj);
03434          goto yuck;
03435       }
03436       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03437       if (fd < 0) {
03438          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03439          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03440          ast_odbc_release_obj(obj);
03441          goto yuck;
03442       }
03443       res = SQLNumResultCols(stmt, &colcount);
03444       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03445          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03446          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03447          ast_odbc_release_obj(obj);
03448          goto yuck;
03449       }
03450       if (f) 
03451          fprintf(f, "[message]\n");
03452       for (x = 0; x < colcount; x++) {
03453          rowdata[0] = '\0';
03454          colsize = 0;
03455          collen = sizeof(coltitle);
03456          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03457                   &datatype, &colsize, &decimaldigits, &nullable);
03458          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03459             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03460             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03461             ast_odbc_release_obj(obj);
03462             goto yuck;
03463          }
03464          if (!strcasecmp(coltitle, "recording")) {
03465             off_t offset;
03466             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03467             fdlen = colsize2;
03468             if (fd > -1) {
03469                char tmp[1]="";
03470                lseek(fd, fdlen - 1, SEEK_SET);
03471                if (write(fd, tmp, 1) != 1) {
03472                   close(fd);
03473                   fd = -1;
03474                   continue;
03475                }
03476                /* Read out in small chunks */
03477                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03478                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03479                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03480                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03481                      ast_odbc_release_obj(obj);
03482                      goto yuck;
03483                   } else {
03484                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03485                      munmap(fdm, CHUNKSIZE);
03486                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03487                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03488                         unlink(full_fn);
03489                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03490                         ast_odbc_release_obj(obj);
03491                         goto yuck;
03492                      }
03493                   }
03494                }
03495                if (truncate(full_fn, fdlen) < 0) {
03496                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03497                }
03498             }
03499          } else {
03500             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03501             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03502                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03503                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03504                ast_odbc_release_obj(obj);
03505                goto yuck;
03506             }
03507             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03508                fprintf(f, "%s=%s\n", coltitle, rowdata);
03509          }
03510       }
03511       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03512       ast_odbc_release_obj(obj);
03513    } else
03514       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03515 yuck:
03516    if (f)
03517       fclose(f);
03518    if (fd > -1)
03519       close(fd);
03520    return x - 1;
03521 }
03522 
03523 /*!
03524  * \brief Determines the highest message number in use for a given user and mailbox folder.
03525  * \param vmu 
03526  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03527  *
03528  * This method is used when mailboxes are stored in an ODBC back end.
03529  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03530  *
03531  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03532 
03533  */
03534 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03535 {
03536    int x = 0;
03537    int res;
03538    SQLHSTMT stmt;
03539    char sql[PATH_MAX];
03540    char rowdata[20];
03541    char *argv[] = { dir };
03542    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03543 
03544    struct odbc_obj *obj;
03545    obj = ast_odbc_request_obj(odbc_database, 0);
03546    if (obj) {
03547       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03548 
03549       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03550       if (!stmt) {
03551          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03552          ast_odbc_release_obj(obj);
03553          goto yuck;
03554       }
03555       res = SQLFetch(stmt);
03556       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03557          if (res == SQL_NO_DATA) {
03558             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03559          } else {
03560             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03561          }
03562 
03563          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03564          ast_odbc_release_obj(obj);
03565          goto yuck;
03566       }
03567       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03568       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03569          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03570          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03571          ast_odbc_release_obj(obj);
03572          goto yuck;
03573       }
03574       if (sscanf(rowdata, "%30d", &x) != 1)
03575          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03576       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03577       ast_odbc_release_obj(obj);
03578       return x;
03579    } else
03580       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03581 yuck:
03582    return x - 1;
03583 }
03584 
03585 /*!
03586  * \brief Determines if the specified message exists.
03587  * \param dir the folder the mailbox folder to look for messages. 
03588  * \param msgnum the message index to query for.
03589  *
03590  * This method is used when mailboxes are stored in an ODBC back end.
03591  *
03592  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03593  */
03594 static int message_exists(char *dir, int msgnum)
03595 {
03596    int x = 0;
03597    int res;
03598    SQLHSTMT stmt;
03599    char sql[PATH_MAX];
03600    char rowdata[20];
03601    char msgnums[20];
03602    char *argv[] = { dir, msgnums };
03603    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03604 
03605    struct odbc_obj *obj;
03606    obj = ast_odbc_request_obj(odbc_database, 0);
03607    if (obj) {
03608       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03609       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03610       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03611       if (!stmt) {
03612          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03613          ast_odbc_release_obj(obj);
03614          goto yuck;
03615       }
03616       res = SQLFetch(stmt);
03617       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03618          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03619          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03620          ast_odbc_release_obj(obj);
03621          goto yuck;
03622       }
03623       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03624       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03625          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03626          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03627          ast_odbc_release_obj(obj);
03628          goto yuck;
03629       }
03630       if (sscanf(rowdata, "%30d", &x) != 1)
03631          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03632       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03633       ast_odbc_release_obj(obj);
03634    } else
03635       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03636 yuck:
03637    return x;
03638 }
03639 
03640 /*!
03641  * \brief returns the number of messages found.
03642  * \param vmu
03643  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03644  *
03645  * This method is used when mailboxes are stored in an ODBC back end.
03646  *
03647  * \return The count of messages being zero or more, less than zero on error.
03648  */
03649 static int count_messages(struct ast_vm_user *vmu, char *dir)
03650 {
03651    int x = 0;
03652    int res;
03653    SQLHSTMT stmt;
03654    char sql[PATH_MAX];
03655    char rowdata[20];
03656    char *argv[] = { dir };
03657    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03658 
03659    struct odbc_obj *obj;
03660    obj = ast_odbc_request_obj(odbc_database, 0);
03661    if (obj) {
03662       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03663       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03664       if (!stmt) {
03665          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03666          ast_odbc_release_obj(obj);
03667          goto yuck;
03668       }
03669       res = SQLFetch(stmt);
03670       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03671          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03672          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03673          ast_odbc_release_obj(obj);
03674          goto yuck;
03675       }
03676       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03677       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03678          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03679          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03680          ast_odbc_release_obj(obj);
03681          goto yuck;
03682       }
03683       if (sscanf(rowdata, "%30d", &x) != 1)
03684          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03685       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03686       ast_odbc_release_obj(obj);
03687       return x;
03688    } else
03689       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03690 yuck:
03691    return x - 1;
03692 
03693 }
03694 
03695 /*!
03696  * \brief Deletes a message from the mailbox folder.
03697  * \param sdir The mailbox folder to work in.
03698  * \param smsg The message index to be deleted.
03699  *
03700  * This method is used when mailboxes are stored in an ODBC back end.
03701  * The specified message is directly deleted from the database 'voicemessages' table.
03702  * 
03703  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03704  */
03705 static void delete_file(const char *sdir, int smsg)
03706 {
03707    SQLHSTMT stmt;
03708    char sql[PATH_MAX];
03709    char msgnums[20];
03710    char *argv[] = { NULL, msgnums };
03711    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03712    struct odbc_obj *obj;
03713 
03714    argv[0] = ast_strdupa(sdir);
03715 
03716    obj = ast_odbc_request_obj(odbc_database, 0);
03717    if (obj) {
03718       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03719       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03720       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03721       if (!stmt)
03722          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03723       else
03724          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03725       ast_odbc_release_obj(obj);
03726    } else
03727       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03728    return;  
03729 }
03730 
03731 /*!
03732  * \brief Copies a voicemail from one mailbox to another.
03733  * \param sdir the folder for which to look for the message to be copied.
03734  * \param smsg the index of the message to be copied.
03735  * \param ddir the destination folder to copy the message into.
03736  * \param dmsg the index to be used for the copied message.
03737  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03738  * \param dmailboxcontext The context for the destination user.
03739  *
03740  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03741  */
03742 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03743 {
03744    SQLHSTMT stmt;
03745    char sql[512];
03746    char msgnums[20];
03747    char msgnumd[20];
03748    struct odbc_obj *obj;
03749    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03750    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03751 
03752    delete_file(ddir, dmsg);
03753    obj = ast_odbc_request_obj(odbc_database, 0);
03754    if (obj) {
03755       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03756       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03757       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);
03758       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03759       if (!stmt)
03760          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03761       else
03762          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03763       ast_odbc_release_obj(obj);
03764    } else
03765       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03766    return;  
03767 }
03768 
03769 struct insert_data {
03770    char *sql;
03771    const char *dir;
03772    const char *msgnums;
03773    void *data;
03774    SQLLEN datalen;
03775    SQLLEN indlen;
03776    const char *context;
03777    const char *macrocontext;
03778    const char *callerid;
03779    const char *origtime;
03780    const char *duration;
03781    const char *mailboxuser;
03782    const char *mailboxcontext;
03783    const char *category;
03784    const char *flag;
03785 };
03786 
03787 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03788 {
03789    struct insert_data *data = vdata;
03790    int res;
03791    SQLHSTMT stmt;
03792 
03793    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03794    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03795       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03796       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03797       return NULL;
03798    }
03799 
03800    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03801    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03802    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03803    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03804    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03805    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03806    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03807    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03808    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03809    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03810    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03811    if (!ast_strlen_zero(data->category)) {
03812       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03813    }
03814    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03815    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03816       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03817       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03818       return NULL;
03819    }
03820 
03821    return stmt;
03822 }
03823 
03824 /*!
03825  * \brief Stores a voicemail into the database.
03826  * \param dir the folder the mailbox folder to store the message.
03827  * \param mailboxuser the user owning the mailbox folder.
03828  * \param mailboxcontext
03829  * \param msgnum the message index for the message to be stored.
03830  *
03831  * This method is used when mailboxes are stored in an ODBC back end.
03832  * The message sound file and information file is looked up on the file system. 
03833  * A SQL query is invoked to store the message into the (MySQL) database.
03834  *
03835  * \return the zero on success -1 on error.
03836  */
03837 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03838 {
03839    int res = 0;
03840    int fd = -1;
03841    void *fdm = MAP_FAILED;
03842    off_t fdlen = -1;
03843    SQLHSTMT stmt;
03844    char sql[PATH_MAX];
03845    char msgnums[20];
03846    char fn[PATH_MAX];
03847    char full_fn[PATH_MAX];
03848    char fmt[80]="";
03849    char *c;
03850    struct ast_config *cfg = NULL;
03851    struct odbc_obj *obj;
03852    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03853       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03854    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03855 
03856    delete_file(dir, msgnum);
03857    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03858       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03859       return -1;
03860    }
03861 
03862    do {
03863       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03864       c = strchr(fmt, '|');
03865       if (c)
03866          *c = '\0';
03867       if (!strcasecmp(fmt, "wav49"))
03868          strcpy(fmt, "WAV");
03869       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03870       if (msgnum > -1)
03871          make_file(fn, sizeof(fn), dir, msgnum);
03872       else
03873          ast_copy_string(fn, dir, sizeof(fn));
03874       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03875       cfg = ast_config_load(full_fn, config_flags);
03876       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03877       fd = open(full_fn, O_RDWR);
03878       if (fd < 0) {
03879          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03880          res = -1;
03881          break;
03882       }
03883       if (valid_config(cfg)) {
03884          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03885             idata.context = "";
03886          }
03887          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03888             idata.macrocontext = "";
03889          }
03890          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03891             idata.callerid = "";
03892          }
03893          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03894             idata.origtime = "";
03895          }
03896          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03897             idata.duration = "";
03898          }
03899          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03900             idata.category = "";
03901          }
03902          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03903             idata.flag = "";
03904          }
03905       }
03906       fdlen = lseek(fd, 0, SEEK_END);
03907       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03908          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03909          res = -1;
03910          break;
03911       }
03912       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03913       if (fdm == MAP_FAILED) {
03914          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03915          res = -1;
03916          break;
03917       } 
03918       idata.data = fdm;
03919       idata.datalen = idata.indlen = fdlen;
03920 
03921       if (!ast_strlen_zero(idata.category)) 
03922          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03923       else
03924          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03925 
03926       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03927          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03928       } else {
03929          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03930          res = -1;
03931       }
03932    } while (0);
03933    if (obj) {
03934       ast_odbc_release_obj(obj);
03935    }
03936    if (valid_config(cfg))
03937       ast_config_destroy(cfg);
03938    if (fdm != MAP_FAILED)
03939       munmap(fdm, fdlen);
03940    if (fd > -1)
03941       close(fd);
03942    return res;
03943 }
03944 
03945 /*!
03946  * \brief Renames a message in a mailbox folder.
03947  * \param sdir The folder of the message to be renamed.
03948  * \param smsg The index of the message to be renamed.
03949  * \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.
03950  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03951  * \param ddir The destination folder for the message to be renamed into
03952  * \param dmsg The destination message for the message to be renamed.
03953  *
03954  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03955  * 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.
03956  * 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.
03957  */
03958 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03959 {
03960    SQLHSTMT stmt;
03961    char sql[PATH_MAX];
03962    char msgnums[20];
03963    char msgnumd[20];
03964    struct odbc_obj *obj;
03965    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03966    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03967 
03968    delete_file(ddir, dmsg);
03969    obj = ast_odbc_request_obj(odbc_database, 0);
03970    if (obj) {
03971       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03972       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03973       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03974       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03975       if (!stmt)
03976          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03977       else
03978          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03979       ast_odbc_release_obj(obj);
03980    } else
03981       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03982    return;  
03983 }
03984 
03985 /*!
03986  * \brief Removes a voicemail message file.
03987  * \param dir the path to the message file.
03988  * \param msgnum the unique number for the message within the mailbox.
03989  *
03990  * Removes the message content file and the information file.
03991  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03992  * Typical use is to clean up after a RETRIEVE operation. 
03993  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03994  * \return zero on success, -1 on error.
03995  */
03996 static int remove_file(char *dir, int msgnum)
03997 {
03998    char fn[PATH_MAX];
03999    char full_fn[PATH_MAX];
04000    char msgnums[80];
04001    
04002    if (msgnum > -1) {
04003       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
04004       make_file(fn, sizeof(fn), dir, msgnum);
04005    } else
04006       ast_copy_string(fn, dir, sizeof(fn));
04007    ast_filedelete(fn, NULL);  
04008    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
04009    unlink(full_fn);
04010    return 0;
04011 }
04012 #else
04013 #ifndef IMAP_STORAGE
04014 /*!
04015  * \brief Find all .txt files - even if they are not in sequence from 0000.
04016  * \param vmu
04017  * \param dir
04018  *
04019  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04020  *
04021  * \return the count of messages, zero or more.
04022  */
04023 static int count_messages(struct ast_vm_user *vmu, char *dir)
04024 {
04025 
04026    int vmcount = 0;
04027    DIR *vmdir = NULL;
04028    struct dirent *vment = NULL;
04029 
04030    if (vm_lock_path(dir))
04031       return ERROR_LOCK_PATH;
04032 
04033    if ((vmdir = opendir(dir))) {
04034       while ((vment = readdir(vmdir))) {
04035          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04036             vmcount++;
04037          }
04038       }
04039       closedir(vmdir);
04040    }
04041    ast_unlock_path(dir);
04042    
04043    return vmcount;
04044 }
04045 
04046 /*!
04047  * \brief Renames a message in a mailbox folder.
04048  * \param sfn The path to the mailbox information and data file to be renamed.
04049  * \param dfn The path for where the message data and information files will be renamed to.
04050  *
04051  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04052  */
04053 static void rename_file(char *sfn, char *dfn)
04054 {
04055    char stxt[PATH_MAX];
04056    char dtxt[PATH_MAX];
04057    ast_filerename(sfn, dfn, NULL);
04058    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04059    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04060    if (ast_check_realtime("voicemail_data")) {
04061       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04062    }
04063    rename(stxt, dtxt);
04064 }
04065 
04066 /*! 
04067  * \brief Determines the highest message number in use for a given user and mailbox folder.
04068  * \param vmu 
04069  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04070  *
04071  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04072  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04073  *
04074  * \note Should always be called with a lock already set on dir.
04075  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04076  */
04077 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04078 {
04079    int x;
04080    unsigned char map[MAXMSGLIMIT] = "";
04081    DIR *msgdir;
04082    struct dirent *msgdirent;
04083    int msgdirint;
04084    char extension[4];
04085    int stopcount = 0;
04086 
04087    /* Reading the entire directory into a file map scales better than
04088     * doing a stat repeatedly on a predicted sequence.  I suspect this
04089     * is partially due to stat(2) internally doing a readdir(2) itself to
04090     * find each file. */
04091    if (!(msgdir = opendir(dir))) {
04092       return -1;
04093    }
04094 
04095    while ((msgdirent = readdir(msgdir))) {
04096       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04097          map[msgdirint] = 1;
04098          stopcount++;
04099          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04100       }
04101    }
04102    closedir(msgdir);
04103 
04104    for (x = 0; x < vmu->maxmsg; x++) {
04105       if (map[x] == 1) {
04106          stopcount--;
04107       } else if (map[x] == 0 && !stopcount) {
04108          break;
04109       }
04110    }
04111 
04112    return x - 1;
04113 }
04114 
04115 #endif /* #ifndef IMAP_STORAGE */
04116 #endif /* #else of #ifdef ODBC_STORAGE */
04117 #ifndef IMAP_STORAGE
04118 /*!
04119  * \brief Utility function to copy a file.
04120  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04121  * \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.
04122  *
04123  * 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.
04124  * The copy operation copies up to 4096 bytes at once.
04125  *
04126  * \return zero on success, -1 on error.
04127  */
04128 static int copy(char *infile, char *outfile)
04129 {
04130    int ifd;
04131    int ofd;
04132    int res;
04133    int len;
04134    char buf[4096];
04135 
04136 #ifdef HARDLINK_WHEN_POSSIBLE
04137    /* Hard link if possible; saves disk space & is faster */
04138    if (link(infile, outfile)) {
04139 #endif
04140       if ((ifd = open(infile, O_RDONLY)) < 0) {
04141          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04142          return -1;
04143       }
04144       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04145          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04146          close(ifd);
04147          return -1;
04148       }
04149       do {
04150          len = read(ifd, buf, sizeof(buf));
04151          if (len < 0) {
04152             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04153             close(ifd);
04154             close(ofd);
04155             unlink(outfile);
04156          } else if (len) {
04157             res = write(ofd, buf, len);
04158             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04159                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04160                close(ifd);
04161                close(ofd);
04162                unlink(outfile);
04163             }
04164          }
04165       } while (len);
04166       close(ifd);
04167       close(ofd);
04168       return 0;
04169 #ifdef HARDLINK_WHEN_POSSIBLE
04170    } else {
04171       /* Hard link succeeded */
04172       return 0;
04173    }
04174 #endif
04175 }
04176 
04177 /*!
04178  * \brief Copies a voicemail information (envelope) file.
04179  * \param frompath
04180  * \param topath 
04181  *
04182  * Every voicemail has the data (.wav) file, and the information file.
04183  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04184  * This is used by the COPY macro when not using IMAP storage.
04185  */
04186 static void copy_plain_file(char *frompath, char *topath)
04187 {
04188    char frompath2[PATH_MAX], topath2[PATH_MAX];
04189    struct ast_variable *tmp,*var = NULL;
04190    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04191    ast_filecopy(frompath, topath, NULL);
04192    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04193    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04194    if (ast_check_realtime("voicemail_data")) {
04195       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04196       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04197       for (tmp = var; tmp; tmp = tmp->next) {
04198          if (!strcasecmp(tmp->name, "origmailbox")) {
04199             origmailbox = tmp->value;
04200          } else if (!strcasecmp(tmp->name, "context")) {
04201             context = tmp->value;
04202          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04203             macrocontext = tmp->value;
04204          } else if (!strcasecmp(tmp->name, "exten")) {
04205             exten = tmp->value;
04206          } else if (!strcasecmp(tmp->name, "priority")) {
04207             priority = tmp->value;
04208          } else if (!strcasecmp(tmp->name, "callerchan")) {
04209             callerchan = tmp->value;
04210          } else if (!strcasecmp(tmp->name, "callerid")) {
04211             callerid = tmp->value;
04212          } else if (!strcasecmp(tmp->name, "origdate")) {
04213             origdate = tmp->value;
04214          } else if (!strcasecmp(tmp->name, "origtime")) {
04215             origtime = tmp->value;
04216          } else if (!strcasecmp(tmp->name, "category")) {
04217             category = tmp->value;
04218          } else if (!strcasecmp(tmp->name, "duration")) {
04219             duration = tmp->value;
04220          }
04221       }
04222       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);
04223    }
04224    copy(frompath2, topath2);
04225    ast_variables_destroy(var);
04226 }
04227 #endif
04228 
04229 /*! 
04230  * \brief Removes the voicemail sound and information file.
04231  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04232  *
04233  * This is used by the DELETE macro when voicemails are stored on the file system.
04234  *
04235  * \return zero on success, -1 on error.
04236  */
04237 static int vm_delete(char *file)
04238 {
04239    char *txt;
04240    int txtsize = 0;
04241 
04242    txtsize = (strlen(file) + 5)*sizeof(char);
04243    txt = ast_alloca(txtsize);
04244    /* Sprintf here would safe because we alloca'd exactly the right length,
04245     * but trying to eliminate all sprintf's anyhow
04246     */
04247    if (ast_check_realtime("voicemail_data")) {
04248       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04249    }
04250    snprintf(txt, txtsize, "%s.txt", file);
04251    unlink(txt);
04252    return ast_filedelete(file, NULL);
04253 }
04254 
04255 /*!
04256  * \brief utility used by inchar(), for base_encode()
04257  */
04258 static int inbuf(struct baseio *bio, FILE *fi)
04259 {
04260    int l;
04261 
04262    if (bio->ateof)
04263       return 0;
04264 
04265    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04266       if (ferror(fi))
04267          return -1;
04268 
04269       bio->ateof = 1;
04270       return 0;
04271    }
04272 
04273    bio->iolen = l;
04274    bio->iocp = 0;
04275 
04276    return 1;
04277 }
04278 
04279 /*!
04280  * \brief utility used by base_encode()
04281  */
04282 static int inchar(struct baseio *bio, FILE *fi)
04283 {
04284    if (bio->iocp>=bio->iolen) {
04285       if (!inbuf(bio, fi))
04286          return EOF;
04287    }
04288 
04289    return bio->iobuf[bio->iocp++];
04290 }
04291 
04292 /*!
04293  * \brief utility used by base_encode()
04294  */
04295 static int ochar(struct baseio *bio, int c, FILE *so)
04296 {
04297    if (bio->linelength >= BASELINELEN) {
04298       if (fputs(ENDL, so) == EOF) {
04299          return -1;
04300       }
04301 
04302       bio->linelength = 0;
04303    }
04304 
04305    if (putc(((unsigned char) c), so) == EOF) {
04306       return -1;
04307    }
04308 
04309    bio->linelength++;
04310 
04311    return 1;
04312 }
04313 
04314 /*!
04315  * \brief Performs a base 64 encode algorithm on the contents of a File
04316  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04317  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04318  *
04319  * 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 ?
04320  *
04321  * \return zero on success, -1 on error.
04322  */
04323 static int base_encode(char *filename, FILE *so)
04324 {
04325    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04326       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04327       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04328       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04329    int i, hiteof = 0;
04330    FILE *fi;
04331    struct baseio bio;
04332 
04333    memset(&bio, 0, sizeof(bio));
04334    bio.iocp = BASEMAXINLINE;
04335 
04336    if (!(fi = fopen(filename, "rb"))) {
04337       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04338       return -1;
04339    }
04340 
04341    while (!hiteof){
04342       unsigned char igroup[3], ogroup[4];
04343       int c, n;
04344 
04345       memset(igroup, 0, sizeof(igroup));
04346 
04347       for (n = 0; n < 3; n++) {
04348          if ((c = inchar(&bio, fi)) == EOF) {
04349             hiteof = 1;
04350             break;
04351          }
04352 
04353          igroup[n] = (unsigned char) c;
04354       }
04355 
04356       if (n > 0) {
04357          ogroup[0]= dtable[igroup[0] >> 2];
04358          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04359          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04360          ogroup[3]= dtable[igroup[2] & 0x3F];
04361 
04362          if (n < 3) {
04363             ogroup[3] = '=';
04364 
04365             if (n < 2)
04366                ogroup[2] = '=';
04367          }
04368 
04369          for (i = 0; i < 4; i++)
04370             ochar(&bio, ogroup[i], so);
04371       }
04372    }
04373 
04374    fclose(fi);
04375    
04376    if (fputs(ENDL, so) == EOF) {
04377       return 0;
04378    }
04379 
04380    return 1;
04381 }
04382 
04383 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)
04384 {
04385    char callerid[256];
04386    char num[12];
04387    char fromdir[256], fromfile[256];
04388    struct ast_config *msg_cfg;
04389    const char *origcallerid, *origtime;
04390    char origcidname[80], origcidnum[80], origdate[80];
04391    int inttime;
04392    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04393 
04394    /* Prepare variables for substitution in email body and subject */
04395    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04396    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04397    snprintf(num, sizeof(num), "%d", msgnum);
04398    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04399    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04400    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04401    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04402       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04403    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04404    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04405    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04406    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04407    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04408 
04409    /* Retrieve info from VM attribute file */
04410    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04411    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04412    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04413       strcat(fromfile, ".txt");
04414    }
04415    if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
04416       if (option_debug > 0) {
04417          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04418       }
04419       return;
04420    }
04421 
04422    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04423       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04424       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04425       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04426       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04427    }
04428 
04429    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04430       struct timeval tv = { inttime, };
04431       struct ast_tm tm;
04432       ast_localtime(&tv, &tm, NULL);
04433       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04434       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04435    }
04436    ast_config_destroy(msg_cfg);
04437 }
04438 
04439 /*!
04440  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04441  * \param from The string to work with.
04442  * \param buf The buffer into which to write the modified quoted string.
04443  * \param maxlen Always zero, but see \see ast_str
04444  * 
04445  * \return The destination string with quotes wrapped on it (the to field).
04446  */
04447 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04448 {
04449    const char *ptr;
04450 
04451    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04452    ast_str_set(buf, maxlen, "\"");
04453    for (ptr = from; *ptr; ptr++) {
04454       if (*ptr == '"' || *ptr == '\\') {
04455          ast_str_append(buf, maxlen, "\\%c", *ptr);
04456       } else {
04457          ast_str_append(buf, maxlen, "%c", *ptr);
04458       }
04459    }
04460    ast_str_append(buf, maxlen, "\"");
04461 
04462    return ast_str_buffer(*buf);
04463 }
04464 
04465 /*! \brief
04466  * fill in *tm for current time according to the proper timezone, if any.
04467  * \return tm so it can be used as a function argument.
04468  */
04469 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04470 {
04471    const struct vm_zone *z = NULL;
04472    struct timeval t = ast_tvnow();
04473 
04474    /* Does this user have a timezone specified? */
04475    if (!ast_strlen_zero(vmu->zonetag)) {
04476       /* Find the zone in the list */
04477       AST_LIST_LOCK(&zones);
04478       AST_LIST_TRAVERSE(&zones, z, list) {
04479          if (!strcmp(z->name, vmu->zonetag))
04480             break;
04481       }
04482       AST_LIST_UNLOCK(&zones);
04483    }
04484    ast_localtime(&t, tm, z ? z->timezone : NULL);
04485    return tm;
04486 }
04487 
04488 /*!\brief Check if the string would need encoding within the MIME standard, to
04489  * avoid confusing certain mail software that expects messages to be 7-bit
04490  * clean.
04491  */
04492 static int check_mime(const char *str)
04493 {
04494    for (; *str; str++) {
04495       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04496          return 1;
04497       }
04498    }
04499    return 0;
04500 }
04501 
04502 /*!\brief Encode a string according to the MIME rules for encoding strings
04503  * that are not 7-bit clean or contain control characters.
04504  *
04505  * Additionally, if the encoded string would exceed the MIME limit of 76
04506  * characters per line, then the encoding will be broken up into multiple
04507  * sections, separated by a space character, in order to facilitate
04508  * breaking up the associated header across multiple lines.
04509  *
04510  * \param end An expandable buffer for holding the result
04511  * \param maxlen Always zero, but see \see ast_str
04512  * \param start A string to be encoded
04513  * \param preamble The length of the first line already used for this string,
04514  * to ensure that each line maintains a maximum length of 76 chars.
04515  * \param postamble the length of any additional characters appended to the
04516  * line, used to ensure proper field wrapping.
04517  * \retval The encoded string.
04518  */
04519 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04520 {
04521    struct ast_str *tmp = ast_str_alloca(80);
04522    int first_section = 1;
04523 
04524    ast_str_reset(*end);
04525    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04526    for (; *start; start++) {
04527       int need_encoding = 0;
04528       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04529          need_encoding = 1;
04530       }
04531       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04532          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04533          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04534          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04535          /* Start new line */
04536          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04537          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04538          first_section = 0;
04539       }
04540       if (need_encoding && *start == ' ') {
04541          ast_str_append(&tmp, -1, "_");
04542       } else if (need_encoding) {
04543          ast_str_append(&tmp, -1, "=%hhX", *start);
04544       } else {
04545          ast_str_append(&tmp, -1, "%c", *start);
04546       }
04547    }
04548    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04549    return ast_str_buffer(*end);
04550 }
04551 
04552 /*!
04553  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04554  * \param p The output file to generate the email contents into.
04555  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04556  * \param vmu The voicemail user who is sending the voicemail.
04557  * \param msgnum The message index in the mailbox folder.
04558  * \param context 
04559  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04560  * \param fromfolder
04561  * \param cidnum The caller ID number.
04562  * \param cidname The caller ID name.
04563  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04564  * \param attach2 
04565  * \param format The message sound file format. i.e. .wav
04566  * \param duration The time of the message content, in seconds.
04567  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04568  * \param chan
04569  * \param category
04570  * \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.
04571  * \param flag
04572  *
04573  * 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.
04574  */
04575 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)
04576 {
04577    char date[256];
04578    char host[MAXHOSTNAMELEN] = "";
04579    char who[256];
04580    char bound[256];
04581    char dur[256];
04582    struct ast_tm tm;
04583    char enc_cidnum[256] = "", enc_cidname[256] = "";
04584    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04585    char *greeting_attachment; 
04586    char filename[256];
04587 
04588    if (!str1 || !str2) {
04589       ast_free(str1);
04590       ast_free(str2);
04591       return;
04592    }
04593 
04594    if (cidnum) {
04595       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04596    }
04597    if (cidname) {
04598       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04599    }
04600    gethostname(host, sizeof(host) - 1);
04601 
04602    if (strchr(srcemail, '@')) {
04603       ast_copy_string(who, srcemail, sizeof(who));
04604    } else {
04605       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04606    }
04607 
04608    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04609    if (greeting_attachment) {
04610       *greeting_attachment++ = '\0';
04611    }
04612 
04613    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04614    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04615    fprintf(p, "Date: %s" ENDL, date);
04616 
04617    /* Set date format for voicemail mail */
04618    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04619 
04620    if (!ast_strlen_zero(fromstring)) {
04621       struct ast_channel *ast;
04622       if ((ast = ast_dummy_channel_alloc())) {
04623          char *ptr;
04624          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04625          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04626 
04627          if (check_mime(ast_str_buffer(str1))) {
04628             int first_line = 1;
04629             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04630             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04631                *ptr = '\0';
04632                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04633                first_line = 0;
04634                /* Substring is smaller, so this will never grow */
04635                ast_str_set(&str2, 0, "%s", ptr + 1);
04636             }
04637             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04638          } else {
04639             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04640          }
04641          ast = ast_channel_unref(ast);
04642       } else {
04643          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04644       }
04645    } else {
04646       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04647    }
04648 
04649    if (check_mime(vmu->fullname)) {
04650       int first_line = 1;
04651       char *ptr;
04652       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04653       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04654          *ptr = '\0';
04655          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04656          first_line = 0;
04657          /* Substring is smaller, so this will never grow */
04658          ast_str_set(&str2, 0, "%s", ptr + 1);
04659       }
04660       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04661    } else {
04662       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04663    }
04664 
04665    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04666       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04667       struct ast_channel *ast;
04668       if ((ast = ast_dummy_channel_alloc())) {
04669          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04670          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04671          if (check_mime(ast_str_buffer(str1))) {
04672             int first_line = 1;
04673             char *ptr;
04674             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04675             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04676                *ptr = '\0';
04677                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04678                first_line = 0;
04679                /* Substring is smaller, so this will never grow */
04680                ast_str_set(&str2, 0, "%s", ptr + 1);
04681             }
04682             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04683          } else {
04684             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04685          }
04686          ast = ast_channel_unref(ast);
04687       } else {
04688          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04689       }
04690    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04691       if (ast_strlen_zero(flag)) {
04692          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04693       } else {
04694          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04695       }
04696    } else {
04697       if (ast_strlen_zero(flag)) {
04698          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04699       } else {
04700          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04701       }
04702    }
04703 
04704    fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
04705       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04706    if (imap) {
04707       /* additional information needed for IMAP searching */
04708       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04709       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04710       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04711       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04712 #ifdef IMAP_STORAGE
04713       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04714 #else
04715       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04716 #endif
04717       /* flag added for Urgent */
04718       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04719       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04720       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04721       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04722       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04723       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04724       if (!ast_strlen_zero(category)) {
04725          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04726       } else {
04727          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04728       }
04729       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04730       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04731       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04732    }
04733    if (!ast_strlen_zero(cidnum)) {
04734       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04735    }
04736    if (!ast_strlen_zero(cidname)) {
04737       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04738    }
04739    fprintf(p, "MIME-Version: 1.0" ENDL);
04740    if (attach_user_voicemail) {
04741       /* Something unique. */
04742       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
04743          (int) getpid(), (unsigned int) ast_random());
04744 
04745       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04746       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04747       fprintf(p, "--%s" ENDL, bound);
04748    }
04749    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04750    if (emailbody || vmu->emailbody) {
04751       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04752       struct ast_channel *ast;
04753       if ((ast = ast_dummy_channel_alloc())) {
04754          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04755          ast_str_substitute_variables(&str1, 0, ast, e_body);
04756 #ifdef IMAP_STORAGE
04757             {
04758                /* Convert body to native line terminators for IMAP backend */
04759                char *line = ast_str_buffer(str1), *next;
04760                do {
04761                   /* Terminate line before outputting it to the file */
04762                   if ((next = strchr(line, '\n'))) {
04763                      *next++ = '\0';
04764                   }
04765                   fprintf(p, "%s" ENDL, line);
04766                   line = next;
04767                } while (!ast_strlen_zero(line));
04768             }
04769 #else
04770          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04771 #endif
04772          ast = ast_channel_unref(ast);
04773       } else {
04774          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04775       }
04776    } else if (msgnum > -1) {
04777       if (strcmp(vmu->mailbox, mailbox)) {
04778          /* Forwarded type */
04779          struct ast_config *msg_cfg;
04780          const char *v;
04781          int inttime;
04782          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04783          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04784          /* Retrieve info from VM attribute file */
04785          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04786          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04787          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04788             strcat(fromfile, ".txt");
04789          }
04790          if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
04791             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04792                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04793             }
04794 
04795             /* You might be tempted to do origdate, except that a) it's in the wrong
04796              * format, and b) it's missing for IMAP recordings. */
04797             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04798                struct timeval tv = { inttime, };
04799                struct ast_tm tm;
04800                ast_localtime(&tv, &tm, NULL);
04801                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04802             }
04803             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04804                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04805                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04806                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04807                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04808                date, origcallerid, origdate);
04809             ast_config_destroy(msg_cfg);
04810          } else {
04811             goto plain_message;
04812          }
04813       } else {
04814 plain_message:
04815          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04816             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04817             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04818             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04819             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04820       }
04821    } else {
04822       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04823             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04824    }
04825 
04826    if (imap || attach_user_voicemail) {
04827       if (!ast_strlen_zero(attach2)) {
04828          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04829          ast_debug(5, "creating second attachment filename %s\n", filename);
04830          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04831          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04832          ast_debug(5, "creating attachment filename %s\n", filename);
04833          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04834       } else {
04835          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04836          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04837          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04838       }
04839    }
04840    ast_free(str1);
04841    ast_free(str2);
04842 }
04843 
04844 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)
04845 {
04846    char tmpdir[256], newtmp[256];
04847    char fname[256];
04848    char tmpcmd[256];
04849    int tmpfd = -1;
04850    int soxstatus = 0;
04851 
04852    /* Eww. We want formats to tell us their own MIME type */
04853    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04854 
04855    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04856       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04857       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04858       tmpfd = mkstemp(newtmp);
04859       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04860       ast_debug(3, "newtmp: %s\n", newtmp);
04861       if (tmpfd > -1) {
04862          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04863          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04864             attach = newtmp;
04865             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04866          } else {
04867             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04868                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04869             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04870          }
04871       }
04872    }
04873    fprintf(p, "--%s" ENDL, bound);
04874    if (msgnum > -1)
04875       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04876    else
04877       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04878    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04879    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04880    if (msgnum > -1)
04881       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04882    else
04883       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04884    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04885    base_encode(fname, p);
04886    if (last)
04887       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04888    if (tmpfd > -1) {
04889       if (soxstatus == 0) {
04890          unlink(fname);
04891       }
04892       close(tmpfd);
04893       unlink(newtmp);
04894    }
04895    return 0;
04896 }
04897 
04898 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)
04899 {
04900    FILE *p = NULL;
04901    char tmp[80] = "/tmp/astmail-XXXXXX";
04902    char tmp2[256];
04903    char *stringp;
04904 
04905    if (vmu && ast_strlen_zero(vmu->email)) {
04906       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04907       return(0);
04908    }
04909 
04910    /* Mail only the first format */
04911    format = ast_strdupa(format);
04912    stringp = format;
04913    strsep(&stringp, "|");
04914 
04915    if (!strcmp(format, "wav49"))
04916       format = "WAV";
04917    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));
04918    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04919       command hangs */
04920    if ((p = vm_mkftemp(tmp)) == NULL) {
04921       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04922       return -1;
04923    } else {
04924       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04925       fclose(p);
04926       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04927       ast_safe_system(tmp2);
04928       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04929    }
04930    return 0;
04931 }
04932 
04933 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)
04934 {
04935    char enc_cidnum[256], enc_cidname[256];
04936    char date[256];
04937    char host[MAXHOSTNAMELEN] = "";
04938    char who[256];
04939    char dur[PATH_MAX];
04940    char tmp[80] = "/tmp/astmail-XXXXXX";
04941    char tmp2[PATH_MAX];
04942    struct ast_tm tm;
04943    FILE *p;
04944    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04945 
04946    if (!str1 || !str2) {
04947       ast_free(str1);
04948       ast_free(str2);
04949       return -1;
04950    }
04951 
04952    if (cidnum) {
04953       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04954    }
04955    if (cidname) {
04956       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04957    }
04958 
04959    if ((p = vm_mkftemp(tmp)) == NULL) {
04960       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04961       ast_free(str1);
04962       ast_free(str2);
04963       return -1;
04964    }
04965    gethostname(host, sizeof(host)-1);
04966    if (strchr(srcemail, '@')) {
04967       ast_copy_string(who, srcemail, sizeof(who));
04968    } else {
04969       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04970    }
04971    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04972    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04973    fprintf(p, "Date: %s\n", date);
04974 
04975    /* Reformat for custom pager format */
04976    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04977 
04978    if (!ast_strlen_zero(pagerfromstring)) {
04979       struct ast_channel *ast;
04980       if ((ast = ast_dummy_channel_alloc())) {
04981          char *ptr;
04982          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04983          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04984 
04985          if (check_mime(ast_str_buffer(str1))) {
04986             int first_line = 1;
04987             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04988             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04989                *ptr = '\0';
04990                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04991                first_line = 0;
04992                /* Substring is smaller, so this will never grow */
04993                ast_str_set(&str2, 0, "%s", ptr + 1);
04994             }
04995             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04996          } else {
04997             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04998          }
04999          ast = ast_channel_unref(ast);
05000       } else {
05001          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05002       }
05003    } else {
05004       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
05005    }
05006 
05007    if (check_mime(vmu->fullname)) {
05008       int first_line = 1;
05009       char *ptr;
05010       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
05011       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05012          *ptr = '\0';
05013          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
05014          first_line = 0;
05015          /* Substring is smaller, so this will never grow */
05016          ast_str_set(&str2, 0, "%s", ptr + 1);
05017       }
05018       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
05019    } else {
05020       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
05021    }
05022 
05023    if (!ast_strlen_zero(pagersubject)) {
05024       struct ast_channel *ast;
05025       if ((ast = ast_dummy_channel_alloc())) {
05026          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05027          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05028          if (check_mime(ast_str_buffer(str1))) {
05029             int first_line = 1;
05030             char *ptr;
05031             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05032             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05033                *ptr = '\0';
05034                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05035                first_line = 0;
05036                /* Substring is smaller, so this will never grow */
05037                ast_str_set(&str2, 0, "%s", ptr + 1);
05038             }
05039             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05040          } else {
05041             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05042          }
05043          ast = ast_channel_unref(ast);
05044       } else {
05045          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05046       }
05047    } else {
05048       if (ast_strlen_zero(flag)) {
05049          fprintf(p, "Subject: New VM\n\n");
05050       } else {
05051          fprintf(p, "Subject: New %s VM\n\n", flag);
05052       }
05053    }
05054 
05055    if (pagerbody) {
05056       struct ast_channel *ast;
05057       if ((ast = ast_dummy_channel_alloc())) {
05058          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05059          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05060          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05061          ast = ast_channel_unref(ast);
05062       } else {
05063          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05064       }
05065    } else {
05066       fprintf(p, "New %s long %s msg in box %s\n"
05067             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05068    }
05069 
05070    fclose(p);
05071    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05072    ast_safe_system(tmp2);
05073    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05074    ast_free(str1);
05075    ast_free(str2);
05076    return 0;
05077 }
05078 
05079 /*!
05080  * \brief Gets the current date and time, as formatted string.
05081  * \param s The buffer to hold the output formatted date.
05082  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05083  * 
05084  * The date format string used is "%a %b %e %r UTC %Y".
05085  * 
05086  * \return zero on success, -1 on error.
05087  */
05088 static int get_date(char *s, int len)
05089 {
05090    struct ast_tm tm;
05091    struct timeval t = ast_tvnow();
05092    
05093    ast_localtime(&t, &tm, "UTC");
05094 
05095    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05096 }
05097 
05098 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05099 {
05100    int res;
05101    char fn[PATH_MAX];
05102    char dest[PATH_MAX];
05103 
05104    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05105 
05106    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05107       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05108       return -1;
05109    }
05110 
05111    RETRIEVE(fn, -1, ext, context);
05112    if (ast_fileexists(fn, NULL, NULL) > 0) {
05113       res = ast_stream_and_wait(chan, fn, ecodes);
05114       if (res) {
05115          DISPOSE(fn, -1);
05116          return res;
05117       }
05118    } else {
05119       /* Dispose just in case */
05120       DISPOSE(fn, -1);
05121       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05122       if (res)
05123          return res;
05124       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05125       if (res)
05126          return res;
05127    }
05128    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05129    return res;
05130 }
05131 
05132 static void free_zone(struct vm_zone *z)
05133 {
05134    ast_free(z);
05135 }
05136 
05137 #ifdef ODBC_STORAGE
05138 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05139 {
05140    int x = -1;
05141    int res;
05142    SQLHSTMT stmt = NULL;
05143    char sql[PATH_MAX];
05144    char rowdata[20];
05145    char tmp[PATH_MAX] = "";
05146    struct odbc_obj *obj = NULL;
05147    char *context;
05148    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05149 
05150    if (newmsgs)
05151       *newmsgs = 0;
05152    if (oldmsgs)
05153       *oldmsgs = 0;
05154    if (urgentmsgs)
05155       *urgentmsgs = 0;
05156 
05157    /* If no mailbox, return immediately */
05158    if (ast_strlen_zero(mailbox))
05159       return 0;
05160 
05161    ast_copy_string(tmp, mailbox, sizeof(tmp));
05162 
05163    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05164       int u, n, o;
05165       char *next, *remaining = tmp;
05166       while ((next = strsep(&remaining, " ,"))) {
05167          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05168             return -1;
05169          }
05170          if (urgentmsgs) {
05171             *urgentmsgs += u;
05172          }
05173          if (newmsgs) {
05174             *newmsgs += n;
05175          }
05176          if (oldmsgs) {
05177             *oldmsgs += o;
05178          }
05179       }
05180       return 0;
05181    }
05182 
05183    context = strchr(tmp, '@');
05184    if (context) {
05185       *context = '\0';
05186       context++;
05187    } else
05188       context = "default";
05189 
05190    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05191       do {
05192          if (newmsgs) {
05193             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05194             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05195                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05196                break;
05197             }
05198             res = SQLFetch(stmt);
05199             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05200                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05201                break;
05202             }
05203             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05204             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05205                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05206                break;
05207             }
05208             *newmsgs = atoi(rowdata);
05209             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05210          }
05211 
05212          if (oldmsgs) {
05213             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05214             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05215                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05216                break;
05217             }
05218             res = SQLFetch(stmt);
05219             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05220                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05221                break;
05222             }
05223             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05224             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05225                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05226                break;
05227             }
05228             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05229             *oldmsgs = atoi(rowdata);
05230          }
05231 
05232          if (urgentmsgs) {
05233             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05234             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05235                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05236                break;
05237             }
05238             res = SQLFetch(stmt);
05239             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05240                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05241                break;
05242             }
05243             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05244             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05245                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05246                break;
05247             }
05248             *urgentmsgs = atoi(rowdata);
05249          }
05250 
05251          x = 0;
05252       } while (0);
05253    } else {
05254       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05255    }
05256 
05257    if (stmt) {
05258       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05259    }
05260    if (obj) {
05261       ast_odbc_release_obj(obj);
05262    }
05263    return x;
05264 }
05265 
05266 /*!
05267  * \brief Gets the number of messages that exist in a mailbox folder.
05268  * \param context
05269  * \param mailbox
05270  * \param folder
05271  * 
05272  * This method is used when ODBC backend is used.
05273  * \return The number of messages in this mailbox folder (zero or more).
05274  */
05275 static int messagecount(const char *context, const char *mailbox, const char *folder)
05276 {
05277    struct odbc_obj *obj = NULL;
05278    int nummsgs = 0;
05279    int res;
05280    SQLHSTMT stmt = NULL;
05281    char sql[PATH_MAX];
05282    char rowdata[20];
05283    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05284    if (!folder)
05285       folder = "INBOX";
05286    /* If no mailbox, return immediately */
05287    if (ast_strlen_zero(mailbox))
05288       return 0;
05289 
05290    obj = ast_odbc_request_obj(odbc_database, 0);
05291    if (obj) {
05292       if (!strcmp(folder, "INBOX")) {
05293          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);
05294       } else {
05295          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05296       }
05297       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05298       if (!stmt) {
05299          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05300          goto yuck;
05301       }
05302       res = SQLFetch(stmt);
05303       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05304          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05305          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05306          goto yuck;
05307       }
05308       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05309       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05310          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05311          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05312          goto yuck;
05313       }
05314       nummsgs = atoi(rowdata);
05315       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05316    } else
05317       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05318 
05319 yuck:
05320    if (obj)
05321       ast_odbc_release_obj(obj);
05322    return nummsgs;
05323 }
05324 
05325 /** 
05326  * \brief Determines if the given folder has messages.
05327  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05328  * 
05329  * This function is used when the mailbox is stored in an ODBC back end.
05330  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05331  * \return 1 if the folder has one or more messages. zero otherwise.
05332  */
05333 static int has_voicemail(const char *mailbox, const char *folder)
05334 {
05335    char tmp[256], *tmp2 = tmp, *box, *context;
05336    ast_copy_string(tmp, mailbox, sizeof(tmp));
05337    while ((context = box = strsep(&tmp2, ",&"))) {
05338       strsep(&context, "@");
05339       if (ast_strlen_zero(context))
05340          context = "default";
05341       if (messagecount(context, box, folder))
05342          return 1;
05343    }
05344    return 0;
05345 }
05346 #endif
05347 #ifndef IMAP_STORAGE
05348 /*! 
05349  * \brief Copies a message from one mailbox to another.
05350  * \param chan
05351  * \param vmu
05352  * \param imbox
05353  * \param msgnum
05354  * \param duration
05355  * \param recip
05356  * \param fmt
05357  * \param dir
05358  * \param flag
05359  *
05360  * This is only used by file storage based mailboxes.
05361  *
05362  * \return zero on success, -1 on error.
05363  */
05364 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)
05365 {
05366    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05367    const char *frombox = mbox(vmu, imbox);
05368    const char *userfolder;
05369    int recipmsgnum;
05370    int res = 0;
05371 
05372    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05373 
05374    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05375       userfolder = "Urgent";
05376    } else {
05377       userfolder = "INBOX";
05378    }
05379 
05380    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05381 
05382    if (!dir)
05383       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05384    else
05385       ast_copy_string(fromdir, dir, sizeof(fromdir));
05386 
05387    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05388    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05389 
05390    if (vm_lock_path(todir))
05391       return ERROR_LOCK_PATH;
05392 
05393    recipmsgnum = last_message_index(recip, todir) + 1;
05394    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05395       make_file(topath, sizeof(topath), todir, recipmsgnum);
05396 #ifndef ODBC_STORAGE
05397       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05398          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05399       } else {
05400 #endif
05401          /* If we are prepending a message for ODBC, then the message already
05402           * exists in the database, but we want to force copying from the
05403           * filesystem (since only the FS contains the prepend). */
05404          copy_plain_file(frompath, topath);
05405          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05406          vm_delete(topath);
05407 #ifndef ODBC_STORAGE
05408       }
05409 #endif
05410    } else {
05411       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05412       res = -1;
05413    }
05414    ast_unlock_path(todir);
05415    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05416       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05417       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05418       flag);
05419    
05420    return res;
05421 }
05422 #endif
05423 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05424 
05425 static int messagecount(const char *context, const char *mailbox, const char *folder)
05426 {
05427    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05428 }
05429 
05430 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05431 {
05432    DIR *dir;
05433    struct dirent *de;
05434    char fn[256];
05435    int ret = 0;
05436 
05437    /* If no mailbox, return immediately */
05438    if (ast_strlen_zero(mailbox))
05439       return 0;
05440 
05441    if (ast_strlen_zero(folder))
05442       folder = "INBOX";
05443    if (ast_strlen_zero(context))
05444       context = "default";
05445 
05446    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05447 
05448    if (!(dir = opendir(fn)))
05449       return 0;
05450 
05451    while ((de = readdir(dir))) {
05452       if (!strncasecmp(de->d_name, "msg", 3)) {
05453          if (shortcircuit) {
05454             ret = 1;
05455             break;
05456          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05457             ret++;
05458          }
05459       }
05460    }
05461 
05462    closedir(dir);
05463 
05464    return ret;
05465 }
05466 
05467 /** 
05468  * \brief Determines if the given folder has messages.
05469  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05470  * \param folder the folder to look in
05471  *
05472  * This function is used when the mailbox is stored in a filesystem back end.
05473  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05474  * \return 1 if the folder has one or more messages. zero otherwise.
05475  */
05476 static int has_voicemail(const char *mailbox, const char *folder)
05477 {
05478    char tmp[256], *tmp2 = tmp, *box, *context;
05479    ast_copy_string(tmp, mailbox, sizeof(tmp));
05480    if (ast_strlen_zero(folder)) {
05481       folder = "INBOX";
05482    }
05483    while ((box = strsep(&tmp2, ",&"))) {
05484       if ((context = strchr(box, '@')))
05485          *context++ = '\0';
05486       else
05487          context = "default";
05488       if (__has_voicemail(context, box, folder, 1))
05489          return 1;
05490       /* If we are checking INBOX, we should check Urgent as well */
05491       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05492          return 1;
05493       }
05494    }
05495    return 0;
05496 }
05497 
05498 
05499 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05500 {
05501    char tmp[256];
05502    char *context;
05503 
05504    /* If no mailbox, return immediately */
05505    if (ast_strlen_zero(mailbox))
05506       return 0;
05507 
05508    if (newmsgs)
05509       *newmsgs = 0;
05510    if (oldmsgs)
05511       *oldmsgs = 0;
05512    if (urgentmsgs)
05513       *urgentmsgs = 0;
05514 
05515    if (strchr(mailbox, ',')) {
05516       int tmpnew, tmpold, tmpurgent;
05517       char *mb, *cur;
05518 
05519       ast_copy_string(tmp, mailbox, sizeof(tmp));
05520       mb = tmp;
05521       while ((cur = strsep(&mb, ", "))) {
05522          if (!ast_strlen_zero(cur)) {
05523             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05524                return -1;
05525             else {
05526                if (newmsgs)
05527                   *newmsgs += tmpnew; 
05528                if (oldmsgs)
05529                   *oldmsgs += tmpold;
05530                if (urgentmsgs)
05531                   *urgentmsgs += tmpurgent;
05532             }
05533          }
05534       }
05535       return 0;
05536    }
05537 
05538    ast_copy_string(tmp, mailbox, sizeof(tmp));
05539    
05540    if ((context = strchr(tmp, '@')))
05541       *context++ = '\0';
05542    else
05543       context = "default";
05544 
05545    if (newmsgs)
05546       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05547    if (oldmsgs)
05548       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05549    if (urgentmsgs)
05550       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05551 
05552    return 0;
05553 }
05554 
05555 #endif
05556 
05557 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05558 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05559 {
05560    int urgentmsgs = 0;
05561    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05562    if (newmsgs) {
05563       *newmsgs += urgentmsgs;
05564    }
05565    return res;
05566 }
05567 
05568 static void run_externnotify(char *context, char *extension, const char *flag)
05569 {
05570    char arguments[255];
05571    char ext_context[256] = "";
05572    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05573    struct ast_smdi_mwi_message *mwi_msg;
05574 
05575    if (!ast_strlen_zero(context))
05576       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05577    else
05578       ast_copy_string(ext_context, extension, sizeof(ext_context));
05579 
05580    if (smdi_iface) {
05581       if (ast_app_has_voicemail(ext_context, NULL)) 
05582          ast_smdi_mwi_set(smdi_iface, extension);
05583       else
05584          ast_smdi_mwi_unset(smdi_iface, extension);
05585 
05586       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05587          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05588          if (!strncmp(mwi_msg->cause, "INV", 3))
05589             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05590          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05591             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05592          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05593          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05594       } else {
05595          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05596       }
05597    }
05598 
05599    if (!ast_strlen_zero(externnotify)) {
05600       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05601          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05602       } else {
05603          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
05604             externnotify, S_OR(context, "\"\""),
05605             extension, newvoicemails,
05606             oldvoicemails, urgentvoicemails);
05607          ast_debug(1, "Executing %s\n", arguments);
05608          ast_safe_system(arguments);
05609       }
05610    }
05611 }
05612 
05613 /*!
05614  * \brief Variables used for saving a voicemail.
05615  *
05616  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05617  */
05618 struct leave_vm_options {
05619    unsigned int flags;
05620    signed char record_gain;
05621    char *exitcontext;
05622 };
05623 
05624 /*!
05625  * \brief Prompts the user and records a voicemail to a mailbox.
05626  * \param chan
05627  * \param ext
05628  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05629  * 
05630  * 
05631  * 
05632  * \return zero on success, -1 on error.
05633  */
05634 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05635 {
05636 #ifdef IMAP_STORAGE
05637    int newmsgs, oldmsgs;
05638 #else
05639    char urgdir[PATH_MAX];
05640 #endif
05641    char txtfile[PATH_MAX];
05642    char tmptxtfile[PATH_MAX];
05643    struct vm_state *vms = NULL;
05644    char callerid[256];
05645    FILE *txt;
05646    char date[256];
05647    int txtdes;
05648    int res = 0;
05649    int msgnum;
05650    int duration = 0;
05651    int sound_duration = 0;
05652    int ausemacro = 0;
05653    int ousemacro = 0;
05654    int ouseexten = 0;
05655    char tmpdur[16];
05656    char priority[16];
05657    char origtime[16];
05658    char dir[PATH_MAX];
05659    char tmpdir[PATH_MAX];
05660    char fn[PATH_MAX];
05661    char prefile[PATH_MAX] = "";
05662    char tempfile[PATH_MAX] = "";
05663    char ext_context[256] = "";
05664    char fmt[80];
05665    char *context;
05666    char ecodes[17] = "#";
05667    struct ast_str *tmp = ast_str_create(16);
05668    char *tmpptr;
05669    struct ast_vm_user *vmu;
05670    struct ast_vm_user svm;
05671    const char *category = NULL;
05672    const char *code;
05673    const char *alldtmf = "0123456789ABCD*#";
05674    char flag[80];
05675 
05676    if (!tmp) {
05677       return -1;
05678    }
05679 
05680    ast_str_set(&tmp, 0, "%s", ext);
05681    ext = ast_str_buffer(tmp);
05682    if ((context = strchr(ext, '@'))) {
05683       *context++ = '\0';
05684       tmpptr = strchr(context, '&');
05685    } else {
05686       tmpptr = strchr(ext, '&');
05687    }
05688 
05689    if (tmpptr)
05690       *tmpptr++ = '\0';
05691 
05692    ast_channel_lock(chan);
05693    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05694       category = ast_strdupa(category);
05695    }
05696    ast_channel_unlock(chan);
05697 
05698    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05699       ast_copy_string(flag, "Urgent", sizeof(flag));
05700    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05701       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05702    } else {
05703       flag[0] = '\0';
05704    }
05705 
05706    ast_debug(3, "Before find_user\n");
05707    if (!(vmu = find_user(&svm, context, ext))) {
05708       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05709       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05710       ast_free(tmp);
05711       return res;
05712    }
05713    /* Setup pre-file if appropriate */
05714    if (strcmp(vmu->context, "default"))
05715       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05716    else
05717       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05718 
05719    /* Set the path to the prefile. Will be one of 
05720       VM_SPOOL_DIRcontext/ext/busy
05721       VM_SPOOL_DIRcontext/ext/unavail
05722       Depending on the flag set in options.
05723    */
05724    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05725       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05726    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05727       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05728    }
05729    /* Set the path to the tmpfile as
05730       VM_SPOOL_DIR/context/ext/temp
05731       and attempt to create the folder structure.
05732    */
05733    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05734    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05735       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05736       ast_free(tmp);
05737       return -1;
05738    }
05739    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05740    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05741       ast_copy_string(prefile, tempfile, sizeof(prefile));
05742 
05743    DISPOSE(tempfile, -1);
05744    /* It's easier just to try to make it than to check for its existence */
05745 #ifndef IMAP_STORAGE
05746    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05747 #else
05748    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05749    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05750       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05751    }
05752 #endif
05753 
05754    /* Check current or macro-calling context for special extensions */
05755    if (ast_test_flag(vmu, VM_OPERATOR)) {
05756       if (!ast_strlen_zero(vmu->exit)) {
05757          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05758             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05759             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05760             ouseexten = 1;
05761          }
05762       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05763          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05764          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05765          ouseexten = 1;
05766       } else if (!ast_strlen_zero(chan->macrocontext)
05767          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05768             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05769          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05770          ousemacro = 1;
05771       }
05772    }
05773 
05774    if (!ast_strlen_zero(vmu->exit)) {
05775       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05776          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05777          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05778       }
05779    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05780       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05781       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05782    } else if (!ast_strlen_zero(chan->macrocontext)
05783       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05784          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05785       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05786       ausemacro = 1;
05787    }
05788 
05789    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05790       for (code = alldtmf; *code; code++) {
05791          char e[2] = "";
05792          e[0] = *code;
05793          if (strchr(ecodes, e[0]) == NULL
05794             && ast_canmatch_extension(chan,
05795                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05796                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05797             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05798          }
05799       }
05800    }
05801 
05802    /* Play the beginning intro if desired */
05803    if (!ast_strlen_zero(prefile)) {
05804 #ifdef ODBC_STORAGE
05805       int success = 
05806 #endif
05807          RETRIEVE(prefile, -1, ext, context);
05808       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05809          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05810             res = ast_waitstream(chan, ecodes);
05811 #ifdef ODBC_STORAGE
05812          if (success == -1) {
05813             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05814             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05815             store_file(prefile, vmu->mailbox, vmu->context, -1);
05816          }
05817 #endif
05818       } else {
05819          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05820          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05821       }
05822       DISPOSE(prefile, -1);
05823       if (res < 0) {
05824          ast_debug(1, "Hang up during prefile playback\n");
05825          free_user(vmu);
05826          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05827          ast_free(tmp);
05828          return -1;
05829       }
05830    }
05831    if (res == '#') {
05832       /* On a '#' we skip the instructions */
05833       ast_set_flag(options, OPT_SILENT);
05834       res = 0;
05835    }
05836    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05837    if (vmu->maxmsg == 0) {
05838       if (option_debug > 2)
05839          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05840       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05841       goto leave_vm_out;
05842    }
05843    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05844       res = ast_stream_and_wait(chan, INTRO, ecodes);
05845       if (res == '#') {
05846          ast_set_flag(options, OPT_SILENT);
05847          res = 0;
05848       }
05849    }
05850    if (res > 0)
05851       ast_stopstream(chan);
05852    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05853     other than the operator -- an automated attendant or mailbox login for example */
05854    if (res == '*') {
05855       chan->exten[0] = 'a';
05856       chan->exten[1] = '\0';
05857       if (!ast_strlen_zero(vmu->exit)) {
05858          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05859       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05860          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05861       }
05862       chan->priority = 0;
05863       free_user(vmu);
05864       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05865       ast_free(tmp);
05866       return 0;
05867    }
05868 
05869    /* Check for a '0' here */
05870    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05871    transfer:
05872       if (ouseexten || ousemacro) {
05873          chan->exten[0] = 'o';
05874          chan->exten[1] = '\0';
05875          if (!ast_strlen_zero(vmu->exit)) {
05876             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05877          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05878             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05879          }
05880          ast_play_and_wait(chan, "transfer");
05881          chan->priority = 0;
05882          free_user(vmu);
05883          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05884       }
05885       ast_free(tmp);
05886       return OPERATOR_EXIT;
05887    }
05888 
05889    /* Allow all other digits to exit Voicemail and return to the dialplan */
05890    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05891       if (!ast_strlen_zero(options->exitcontext)) {
05892          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05893       }
05894       free_user(vmu);
05895       ast_free(tmp);
05896       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05897       return res;
05898    }
05899 
05900    if (res < 0) {
05901       free_user(vmu);
05902       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05903       ast_free(tmp);
05904       return -1;
05905    }
05906    /* The meat of recording the message...  All the announcements and beeps have been played*/
05907    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05908    if (!ast_strlen_zero(fmt)) {
05909       msgnum = 0;
05910 
05911 #ifdef IMAP_STORAGE
05912       /* Is ext a mailbox? */
05913       /* must open stream for this user to get info! */
05914       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05915       if (res < 0) {
05916          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05917          ast_free(tmp);
05918          return -1;
05919       }
05920       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05921       /* It is possible under certain circumstances that inboxcount did not
05922        * create a vm_state when it was needed. This is a catchall which will
05923        * rarely be used.
05924        */
05925          if (!(vms = create_vm_state_from_user(vmu))) {
05926             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05927             ast_free(tmp);
05928             return -1;
05929          }
05930       }
05931       vms->newmessages++;
05932       
05933       /* here is a big difference! We add one to it later */
05934       msgnum = newmsgs + oldmsgs;
05935       ast_debug(3, "Messagecount set to %d\n", msgnum);
05936       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05937       /* set variable for compatibility */
05938       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05939 
05940       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05941          goto leave_vm_out;
05942       }
05943 #else
05944       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05945          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05946          if (!res)
05947             res = ast_waitstream(chan, "");
05948          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05949          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05950          inprocess_count(vmu->mailbox, vmu->context, -1);
05951          goto leave_vm_out;
05952       }
05953 
05954 #endif
05955       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05956       txtdes = mkstemp(tmptxtfile);
05957       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05958       if (txtdes < 0) {
05959          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05960          if (!res)
05961             res = ast_waitstream(chan, "");
05962          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05963          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05964          inprocess_count(vmu->mailbox, vmu->context, -1);
05965          goto leave_vm_out;
05966       }
05967 
05968       /* Now play the beep once we have the message number for our next message. */
05969       if (res >= 0) {
05970          /* Unless we're *really* silent, try to send the beep */
05971          res = ast_stream_and_wait(chan, "beep", "");
05972       }
05973             
05974       /* Store information in real-time storage */
05975       if (ast_check_realtime("voicemail_data")) {
05976          snprintf(priority, sizeof(priority), "%d", chan->priority);
05977          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05978          get_date(date, sizeof(date));
05979          ast_callerid_merge(callerid, sizeof(callerid),
05980             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05981             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05982             "Unknown");
05983          ast_store_realtime("voicemail_data",
05984             "origmailbox", ext,
05985             "context", chan->context,
05986             "macrocontext", chan->macrocontext,
05987             "exten", chan->exten,
05988             "priority", priority,
05989             "callerchan", chan->name,
05990             "callerid", callerid,
05991             "origdate", date,
05992             "origtime", origtime,
05993             "category", S_OR(category, ""),
05994             "filename", tmptxtfile,
05995             SENTINEL);
05996       }
05997 
05998       /* Store information */
05999       txt = fdopen(txtdes, "w+");
06000       if (txt) {
06001          get_date(date, sizeof(date));
06002          ast_callerid_merge(callerid, sizeof(callerid),
06003             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06004             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06005             "Unknown");
06006          fprintf(txt, 
06007             ";\n"
06008             "; Message Information file\n"
06009             ";\n"
06010             "[message]\n"
06011             "origmailbox=%s\n"
06012             "context=%s\n"
06013             "macrocontext=%s\n"
06014             "exten=%s\n"
06015             "rdnis=%s\n"
06016             "priority=%d\n"
06017             "callerchan=%s\n"
06018             "callerid=%s\n"
06019             "origdate=%s\n"
06020             "origtime=%ld\n"
06021             "category=%s\n",
06022             ext,
06023             chan->context,
06024             chan->macrocontext, 
06025             chan->exten,
06026             S_COR(chan->redirecting.from.number.valid,
06027                chan->redirecting.from.number.str, "unknown"),
06028             chan->priority,
06029             chan->name,
06030             callerid,
06031             date, (long) time(NULL),
06032             category ? category : "");
06033       } else {
06034          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06035          inprocess_count(vmu->mailbox, vmu->context, -1);
06036          if (ast_check_realtime("voicemail_data")) {
06037             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06038          }
06039          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
06040          goto leave_vm_out;
06041       }
06042       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06043 
06044       if (txt) {
06045          fprintf(txt, "flag=%s\n", flag);
06046          if (sound_duration < vmu->minsecs) {
06047             fclose(txt);
06048             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06049             ast_filedelete(tmptxtfile, NULL);
06050             unlink(tmptxtfile);
06051             if (ast_check_realtime("voicemail_data")) {
06052                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06053             }
06054             inprocess_count(vmu->mailbox, vmu->context, -1);
06055          } else {
06056             fprintf(txt, "duration=%d\n", duration);
06057             fclose(txt);
06058             if (vm_lock_path(dir)) {
06059                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06060                /* Delete files */
06061                ast_filedelete(tmptxtfile, NULL);
06062                unlink(tmptxtfile);
06063                inprocess_count(vmu->mailbox, vmu->context, -1);
06064             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06065                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06066                unlink(tmptxtfile);
06067                ast_unlock_path(dir);
06068                inprocess_count(vmu->mailbox, vmu->context, -1);
06069                if (ast_check_realtime("voicemail_data")) {
06070                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06071                }
06072             } else {
06073 #ifndef IMAP_STORAGE
06074                msgnum = last_message_index(vmu, dir) + 1;
06075 #endif
06076                make_file(fn, sizeof(fn), dir, msgnum);
06077 
06078                /* assign a variable with the name of the voicemail file */ 
06079 #ifndef IMAP_STORAGE
06080                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06081 #else
06082                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06083 #endif
06084 
06085                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06086                ast_filerename(tmptxtfile, fn, NULL);
06087                rename(tmptxtfile, txtfile);
06088                inprocess_count(vmu->mailbox, vmu->context, -1);
06089 
06090                /* Properly set permissions on voicemail text descriptor file.
06091                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06092                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06093                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06094 
06095                ast_unlock_path(dir);
06096                if (ast_check_realtime("voicemail_data")) {
06097                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06098                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06099                }
06100                /* We must store the file first, before copying the message, because
06101                 * ODBC storage does the entire copy with SQL.
06102                 */
06103                if (ast_fileexists(fn, NULL, NULL) > 0) {
06104                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06105                }
06106 
06107                /* Are there to be more recipients of this message? */
06108                while (tmpptr) {
06109                   struct ast_vm_user recipu, *recip;
06110                   char *exten, *cntx;
06111 
06112                   exten = strsep(&tmpptr, "&");
06113                   cntx = strchr(exten, '@');
06114                   if (cntx) {
06115                      *cntx = '\0';
06116                      cntx++;
06117                   }
06118                   if ((recip = find_user(&recipu, cntx, exten))) {
06119                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06120                      free_user(recip);
06121                   }
06122                }
06123 #ifndef IMAP_STORAGE
06124                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06125                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06126                   char sfn[PATH_MAX];
06127                   char dfn[PATH_MAX];
06128                   int x;
06129                   /* It's easier just to try to make it than to check for its existence */
06130                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06131                   x = last_message_index(vmu, urgdir) + 1;
06132                   make_file(sfn, sizeof(sfn), dir, msgnum);
06133                   make_file(dfn, sizeof(dfn), urgdir, x);
06134                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06135                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06136                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06137                   ast_copy_string(fn, dfn, sizeof(fn));
06138                   msgnum = x;
06139                }
06140 #endif
06141                /* Notification needs to happen after the copy, though. */
06142                if (ast_fileexists(fn, NULL, NULL)) {
06143 #ifdef IMAP_STORAGE
06144                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06145                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06146                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06147                      flag);
06148 #else
06149                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06150                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06151                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06152                      flag);
06153 #endif
06154                }
06155 
06156                /* Disposal needs to happen after the optional move and copy */
06157                if (ast_fileexists(fn, NULL, NULL)) {
06158                   DISPOSE(dir, msgnum);
06159                }
06160             }
06161          }
06162       } else {
06163          inprocess_count(vmu->mailbox, vmu->context, -1);
06164       }
06165       if (res == '0') {
06166          goto transfer;
06167       } else if (res > 0 && res != 't')
06168          res = 0;
06169 
06170       if (sound_duration < vmu->minsecs)
06171          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06172          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06173       else
06174          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06175    } else
06176       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06177 leave_vm_out:
06178    free_user(vmu);
06179 
06180 #ifdef IMAP_STORAGE
06181    /* expunge message - use UID Expunge if supported on IMAP server*/
06182    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06183    if (expungeonhangup == 1) {
06184       ast_mutex_lock(&vms->lock);
06185 #ifdef HAVE_IMAP_TK2006
06186       if (LEVELUIDPLUS (vms->mailstream)) {
06187          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06188       } else 
06189 #endif
06190          mail_expunge(vms->mailstream);
06191       ast_mutex_unlock(&vms->lock);
06192    }
06193 #endif
06194 
06195    ast_free(tmp);
06196    return res;
06197 }
06198 
06199 #if !defined(IMAP_STORAGE)
06200 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06201 {
06202    /* we know the actual number of messages, so stop process when number is hit */
06203 
06204    int x, dest;
06205    char sfn[PATH_MAX];
06206    char dfn[PATH_MAX];
06207 
06208    if (vm_lock_path(dir)) {
06209       return ERROR_LOCK_PATH;
06210    }
06211 
06212    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06213       make_file(sfn, sizeof(sfn), dir, x);
06214       if (EXISTS(dir, x, sfn, NULL)) {
06215 
06216          if (x != dest) {
06217             make_file(dfn, sizeof(dfn), dir, dest);
06218             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06219          }
06220 
06221          dest++;
06222       }
06223    }
06224    ast_unlock_path(dir);
06225 
06226    return dest;
06227 }
06228 #endif
06229 
06230 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06231 {
06232    int d;
06233    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06234    return d;
06235 }
06236 
06237 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06238 {
06239 #ifdef IMAP_STORAGE
06240    /* we must use mbox(x) folder names, and copy the message there */
06241    /* simple. huh? */
06242    char sequence[10];
06243    char mailbox[256];
06244    int res;
06245 
06246    /* get the real IMAP message number for this message */
06247    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06248    
06249    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06250    ast_mutex_lock(&vms->lock);
06251    /* if save to Old folder, put in INBOX as read */
06252    if (box == OLD_FOLDER) {
06253       mail_setflag(vms->mailstream, sequence, "\\Seen");
06254       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06255    } else if (box == NEW_FOLDER) {
06256       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06257       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06258    }
06259    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06260       ast_mutex_unlock(&vms->lock);
06261       return 0;
06262    }
06263    /* Create the folder if it don't exist */
06264    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06265    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06266    if (mail_create(vms->mailstream, mailbox) == NIL) 
06267       ast_debug(5, "Folder exists.\n");
06268    else
06269       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06270    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06271    ast_mutex_unlock(&vms->lock);
06272    return res;
06273 #else
06274    char *dir = vms->curdir;
06275    char *username = vms->username;
06276    char *context = vmu->context;
06277    char sfn[PATH_MAX];
06278    char dfn[PATH_MAX];
06279    char ddir[PATH_MAX];
06280    const char *dbox = mbox(vmu, box);
06281    int x, i;
06282    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06283 
06284    if (vm_lock_path(ddir))
06285       return ERROR_LOCK_PATH;
06286 
06287    x = last_message_index(vmu, ddir) + 1;
06288 
06289    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06290       x--;
06291       for (i = 1; i <= x; i++) {
06292          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06293          make_file(sfn, sizeof(sfn), ddir, i);
06294          make_file(dfn, sizeof(dfn), ddir, i - 1);
06295          if (EXISTS(ddir, i, sfn, NULL)) {
06296             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06297          } else
06298             break;
06299       }
06300    } else {
06301       if (x >= vmu->maxmsg) {
06302          ast_unlock_path(ddir);
06303          return -1;
06304       }
06305    }
06306    make_file(sfn, sizeof(sfn), dir, msg);
06307    make_file(dfn, sizeof(dfn), ddir, x);
06308    if (strcmp(sfn, dfn)) {
06309       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06310    }
06311    ast_unlock_path(ddir);
06312 #endif
06313    return 0;
06314 }
06315 
06316 static int adsi_logo(unsigned char *buf)
06317 {
06318    int bytes = 0;
06319    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06320    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06321    return bytes;
06322 }
06323 
06324 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06325 {
06326    unsigned char buf[256];
06327    int bytes = 0;
06328    int x;
06329    char num[5];
06330 
06331    *useadsi = 0;
06332    bytes += ast_adsi_data_mode(buf + bytes);
06333    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06334 
06335    bytes = 0;
06336    bytes += adsi_logo(buf);
06337    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06338 #ifdef DISPLAY
06339    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06340 #endif
06341    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06342    bytes += ast_adsi_data_mode(buf + bytes);
06343    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06344 
06345    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06346       bytes = 0;
06347       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06348       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06349       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06350       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06351       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06352       return 0;
06353    }
06354 
06355 #ifdef DISPLAY
06356    /* Add a dot */
06357    bytes = 0;
06358    bytes += ast_adsi_logo(buf);
06359    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06360    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06361    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06362    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06363 #endif
06364    bytes = 0;
06365    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06366    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06367    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06368    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06369    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06370    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06371    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06372 
06373 #ifdef DISPLAY
06374    /* Add another dot */
06375    bytes = 0;
06376    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06377    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06378 
06379    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06380    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06381 #endif
06382 
06383    bytes = 0;
06384    /* These buttons we load but don't use yet */
06385    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06386    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06387    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06388    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06389    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06390    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06391    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06392 
06393 #ifdef DISPLAY
06394    /* Add another dot */
06395    bytes = 0;
06396    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06397    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06398    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06399 #endif
06400 
06401    bytes = 0;
06402    for (x = 0; x < 5; x++) {
06403       snprintf(num, sizeof(num), "%d", x);
06404       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06405    }
06406    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06407    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06408 
06409 #ifdef DISPLAY
06410    /* Add another dot */
06411    bytes = 0;
06412    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06413    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06414    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06415 #endif
06416 
06417    if (ast_adsi_end_download(chan)) {
06418       bytes = 0;
06419       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06420       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06421       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06422       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06423       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06424       return 0;
06425    }
06426    bytes = 0;
06427    bytes += ast_adsi_download_disconnect(buf + bytes);
06428    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06429    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06430 
06431    ast_debug(1, "Done downloading scripts...\n");
06432 
06433 #ifdef DISPLAY
06434    /* Add last dot */
06435    bytes = 0;
06436    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06437    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06438 #endif
06439    ast_debug(1, "Restarting session...\n");
06440 
06441    bytes = 0;
06442    /* Load the session now */
06443    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06444       *useadsi = 1;
06445       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06446    } else
06447       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06448 
06449    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06450    return 0;
06451 }
06452 
06453 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06454 {
06455    int x;
06456    if (!ast_adsi_available(chan))
06457       return;
06458    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06459    if (x < 0)
06460       return;
06461    if (!x) {
06462       if (adsi_load_vmail(chan, useadsi)) {
06463          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06464          return;
06465       }
06466    } else
06467       *useadsi = 1;
06468 }
06469 
06470 static void adsi_login(struct ast_channel *chan)
06471 {
06472    unsigned char buf[256];
06473    int bytes = 0;
06474    unsigned char keys[8];
06475    int x;
06476    if (!ast_adsi_available(chan))
06477       return;
06478 
06479    for (x = 0; x < 8; x++)
06480       keys[x] = 0;
06481    /* Set one key for next */
06482    keys[3] = ADSI_KEY_APPS + 3;
06483 
06484    bytes += adsi_logo(buf + bytes);
06485    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06486    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06487    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06488    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06489    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06490    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06491    bytes += ast_adsi_set_keys(buf + bytes, keys);
06492    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06493    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06494 }
06495 
06496 static void adsi_password(struct ast_channel *chan)
06497 {
06498    unsigned char buf[256];
06499    int bytes = 0;
06500    unsigned char keys[8];
06501    int x;
06502    if (!ast_adsi_available(chan))
06503       return;
06504 
06505    for (x = 0; x < 8; x++)
06506       keys[x] = 0;
06507    /* Set one key for next */
06508    keys[3] = ADSI_KEY_APPS + 3;
06509 
06510    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06511    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06512    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06513    bytes += ast_adsi_set_keys(buf + bytes, keys);
06514    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06515    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06516 }
06517 
06518 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06519 {
06520    unsigned char buf[256];
06521    int bytes = 0;
06522    unsigned char keys[8];
06523    int x, y;
06524 
06525    if (!ast_adsi_available(chan))
06526       return;
06527 
06528    for (x = 0; x < 5; x++) {
06529       y = ADSI_KEY_APPS + 12 + start + x;
06530       if (y > ADSI_KEY_APPS + 12 + 4)
06531          y = 0;
06532       keys[x] = ADSI_KEY_SKT | y;
06533    }
06534    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06535    keys[6] = 0;
06536    keys[7] = 0;
06537 
06538    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06539    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06540    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06541    bytes += ast_adsi_set_keys(buf + bytes, keys);
06542    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06543 
06544    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06545 }
06546 
06547 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06548 {
06549    int bytes = 0;
06550    unsigned char buf[256]; 
06551    char buf1[256], buf2[256];
06552    char fn2[PATH_MAX];
06553 
06554    char cid[256] = "";
06555    char *val;
06556    char *name, *num;
06557    char datetime[21] = "";
06558    FILE *f;
06559 
06560    unsigned char keys[8];
06561 
06562    int x;
06563 
06564    if (!ast_adsi_available(chan))
06565       return;
06566 
06567    /* Retrieve important info */
06568    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06569    f = fopen(fn2, "r");
06570    if (f) {
06571       while (!feof(f)) {   
06572          if (!fgets((char *) buf, sizeof(buf), f)) {
06573             continue;
06574          }
06575          if (!feof(f)) {
06576             char *stringp = NULL;
06577             stringp = (char *) buf;
06578             strsep(&stringp, "=");
06579             val = strsep(&stringp, "=");
06580             if (!ast_strlen_zero(val)) {
06581                if (!strcmp((char *) buf, "callerid"))
06582                   ast_copy_string(cid, val, sizeof(cid));
06583                if (!strcmp((char *) buf, "origdate"))
06584                   ast_copy_string(datetime, val, sizeof(datetime));
06585             }
06586          }
06587       }
06588       fclose(f);
06589    }
06590    /* New meaning for keys */
06591    for (x = 0; x < 5; x++)
06592       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06593    keys[6] = 0x0;
06594    keys[7] = 0x0;
06595 
06596    if (!vms->curmsg) {
06597       /* No prev key, provide "Folder" instead */
06598       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06599    }
06600    if (vms->curmsg >= vms->lastmsg) {
06601       /* If last message ... */
06602       if (vms->curmsg) {
06603          /* but not only message, provide "Folder" instead */
06604          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06605          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06606 
06607       } else {
06608          /* Otherwise if only message, leave blank */
06609          keys[3] = 1;
06610       }
06611    }
06612 
06613    if (!ast_strlen_zero(cid)) {
06614       ast_callerid_parse(cid, &name, &num);
06615       if (!name)
06616          name = num;
06617    } else
06618       name = "Unknown Caller";
06619 
06620    /* If deleted, show "undeleted" */
06621 #ifdef IMAP_STORAGE
06622    ast_mutex_lock(&vms->lock);
06623 #endif
06624    if (vms->deleted[vms->curmsg]) {
06625       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06626    }
06627 #ifdef IMAP_STORAGE
06628    ast_mutex_unlock(&vms->lock);
06629 #endif
06630 
06631    /* Except "Exit" */
06632    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06633    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06634       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06635    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06636 
06637    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06638    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06639    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06640    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06641    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06642    bytes += ast_adsi_set_keys(buf + bytes, keys);
06643    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06644 
06645    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06646 }
06647 
06648 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06649 {
06650    int bytes = 0;
06651    unsigned char buf[256];
06652    unsigned char keys[8];
06653 
06654    int x;
06655 
06656    if (!ast_adsi_available(chan))
06657       return;
06658 
06659    /* New meaning for keys */
06660    for (x = 0; x < 5; x++)
06661       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06662 
06663    keys[6] = 0x0;
06664    keys[7] = 0x0;
06665 
06666    if (!vms->curmsg) {
06667       /* No prev key, provide "Folder" instead */
06668       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06669    }
06670    if (vms->curmsg >= vms->lastmsg) {
06671       /* If last message ... */
06672       if (vms->curmsg) {
06673          /* but not only message, provide "Folder" instead */
06674          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06675       } else {
06676          /* Otherwise if only message, leave blank */
06677          keys[3] = 1;
06678       }
06679    }
06680 
06681    /* If deleted, show "undeleted" */
06682 #ifdef IMAP_STORAGE
06683    ast_mutex_lock(&vms->lock);
06684 #endif
06685    if (vms->deleted[vms->curmsg]) {
06686       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06687    }
06688 #ifdef IMAP_STORAGE
06689    ast_mutex_unlock(&vms->lock);
06690 #endif
06691 
06692    /* Except "Exit" */
06693    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06694    bytes += ast_adsi_set_keys(buf + bytes, keys);
06695    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06696 
06697    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06698 }
06699 
06700 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06701 {
06702    unsigned char buf[256] = "";
06703    char buf1[256] = "", buf2[256] = "";
06704    int bytes = 0;
06705    unsigned char keys[8];
06706    int x;
06707 
06708    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06709    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06710    if (!ast_adsi_available(chan))
06711       return;
06712    if (vms->newmessages) {
06713       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06714       if (vms->oldmessages) {
06715          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06716          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06717       } else {
06718          snprintf(buf2, sizeof(buf2), "%s.", newm);
06719       }
06720    } else if (vms->oldmessages) {
06721       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06722       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06723    } else {
06724       strcpy(buf1, "You have no messages.");
06725       buf2[0] = ' ';
06726       buf2[1] = '\0';
06727    }
06728    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06729    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06730    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06731 
06732    for (x = 0; x < 6; x++)
06733       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06734    keys[6] = 0;
06735    keys[7] = 0;
06736 
06737    /* Don't let them listen if there are none */
06738    if (vms->lastmsg < 0)
06739       keys[0] = 1;
06740    bytes += ast_adsi_set_keys(buf + bytes, keys);
06741 
06742    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06743 
06744    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06745 }
06746 
06747 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06748 {
06749    unsigned char buf[256] = "";
06750    char buf1[256] = "", buf2[256] = "";
06751    int bytes = 0;
06752    unsigned char keys[8];
06753    int x;
06754 
06755    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06756 
06757    if (!ast_adsi_available(chan))
06758       return;
06759 
06760    /* Original command keys */
06761    for (x = 0; x < 6; x++)
06762       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06763 
06764    keys[6] = 0;
06765    keys[7] = 0;
06766 
06767    if ((vms->lastmsg + 1) < 1)
06768       keys[0] = 0;
06769 
06770    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06771       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06772 
06773    if (vms->lastmsg + 1)
06774       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06775    else
06776       strcpy(buf2, "no messages.");
06777    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06778    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06779    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06780    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06781    bytes += ast_adsi_set_keys(buf + bytes, keys);
06782 
06783    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06784 
06785    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06786    
06787 }
06788 
06789 /*
06790 static void adsi_clear(struct ast_channel *chan)
06791 {
06792    char buf[256];
06793    int bytes=0;
06794    if (!ast_adsi_available(chan))
06795       return;
06796    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06797    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06798 
06799    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06800 }
06801 */
06802 
06803 static void adsi_goodbye(struct ast_channel *chan)
06804 {
06805    unsigned char buf[256];
06806    int bytes = 0;
06807 
06808    if (!ast_adsi_available(chan))
06809       return;
06810    bytes += adsi_logo(buf + bytes);
06811    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06812    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06813    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06814    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06815 
06816    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06817 }
06818 
06819 /*!\brief get_folder: Folder menu
06820  * Plays "press 1 for INBOX messages" etc.
06821  * Should possibly be internationalized
06822  */
06823 static int get_folder(struct ast_channel *chan, int start)
06824 {
06825    int x;
06826    int d;
06827    char fn[PATH_MAX];
06828    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06829    if (d)
06830       return d;
06831    for (x = start; x < 5; x++) { /* For all folders */
06832       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06833          return d;
06834       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06835       if (d)
06836          return d;
06837       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06838 
06839       /* The inbox folder can have its name changed under certain conditions
06840        * so this checks if the sound file exists for the inbox folder name and
06841        * if it doesn't, plays the default name instead. */
06842       if (x == 0) {
06843          if (ast_fileexists(fn, NULL, NULL)) {
06844             d = vm_play_folder_name(chan, fn);
06845          } else {
06846             ast_verb(1, "failed to find %s\n", fn);
06847             d = vm_play_folder_name(chan, "vm-INBOX");
06848          }
06849       } else {
06850          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06851          d = vm_play_folder_name(chan, fn);
06852       }
06853 
06854       if (d)
06855          return d;
06856       d = ast_waitfordigit(chan, 500);
06857       if (d)
06858          return d;
06859    }
06860 
06861    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06862    if (d)
06863       return d;
06864    d = ast_waitfordigit(chan, 4000);
06865    return d;
06866 }
06867 
06868 /*!
06869  * \brief plays a prompt and waits for a keypress.
06870  * \param chan
06871  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06872  * \param start Does not appear to be used at this time.
06873  *
06874  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06875  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06876  * prompting for the number inputs that correspond to the available folders.
06877  * 
06878  * \return zero on success, or -1 on error.
06879  */
06880 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06881 {
06882    int res = 0;
06883    int loops = 0;
06884 
06885    res = ast_play_and_wait(chan, fn);  /* Folder name */
06886    while (((res < '0') || (res > '9')) &&
06887          (res != '#') && (res >= 0) &&
06888          loops < 4) {
06889       res = get_folder(chan, 0);
06890       loops++;
06891    }
06892    if (loops == 4) { /* give up */
06893       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06894       return '#';
06895    }
06896    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06897    return res;
06898 }
06899 
06900 /*!
06901  * \brief presents the option to prepend to an existing message when forwarding it.
06902  * \param chan
06903  * \param vmu
06904  * \param curdir
06905  * \param curmsg
06906  * \param vm_fmts
06907  * \param context
06908  * \param record_gain
06909  * \param duration
06910  * \param vms
06911  * \param flag 
06912  *
06913  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06914  *
06915  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06916  * \return zero on success, -1 on error.
06917  */
06918 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06919          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06920 {
06921    int cmd = 0;
06922    int retries = 0, prepend_duration = 0, already_recorded = 0;
06923    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06924    char textfile[PATH_MAX];
06925    struct ast_config *msg_cfg;
06926    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06927 #ifndef IMAP_STORAGE
06928    signed char zero_gain = 0;
06929 #endif
06930    const char *duration_str;
06931 
06932    /* Must always populate duration correctly */
06933    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06934    strcpy(textfile, msgfile);
06935    strcpy(backup, msgfile);
06936    strcpy(backup_textfile, msgfile);
06937    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06938    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06939    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06940 
06941    if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06942       *duration = atoi(duration_str);
06943    } else {
06944       *duration = 0;
06945    }
06946 
06947    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06948       if (cmd)
06949          retries = 0;
06950       switch (cmd) {
06951       case '1': 
06952 
06953 #ifdef IMAP_STORAGE
06954          /* Record new intro file */
06955          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06956          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06957          ast_play_and_wait(chan, INTRO);
06958          ast_play_and_wait(chan, "beep");
06959          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06960          if (cmd == -1) {
06961             break;
06962          }
06963          cmd = 't';
06964 #else
06965 
06966          /* prepend a message to the current message, update the metadata and return */
06967 
06968          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06969          strcpy(textfile, msgfile);
06970          strncat(textfile, ".txt", sizeof(textfile) - 1);
06971          *duration = 0;
06972 
06973          /* if we can't read the message metadata, stop now */
06974          if (!valid_config(msg_cfg)) {
06975             cmd = 0;
06976             break;
06977          }
06978 
06979          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06980 #ifndef IMAP_STORAGE
06981          if (already_recorded) {
06982             ast_filecopy(backup, msgfile, NULL);
06983             copy(backup_textfile, textfile);
06984          }
06985          else {
06986             ast_filecopy(msgfile, backup, NULL);
06987             copy(textfile, backup_textfile);
06988          }
06989 #endif
06990          already_recorded = 1;
06991 
06992          if (record_gain)
06993             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06994 
06995          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06996 
06997          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06998             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06999             ast_stream_and_wait(chan, vm_prepend_timeout, "");
07000             ast_filerename(backup, msgfile, NULL);
07001          }
07002 
07003          if (record_gain)
07004             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
07005 
07006          
07007          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
07008             *duration = atoi(duration_str);
07009 
07010          if (prepend_duration) {
07011             struct ast_category *msg_cat;
07012             /* need enough space for a maximum-length message duration */
07013             char duration_buf[12];
07014 
07015             *duration += prepend_duration;
07016             msg_cat = ast_category_get(msg_cfg, "message");
07017             snprintf(duration_buf, 11, "%ld", *duration);
07018             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
07019                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
07020             }
07021          }
07022 
07023 #endif
07024          break;
07025       case '2': 
07026          /* NULL out introfile so we know there is no intro! */
07027 #ifdef IMAP_STORAGE
07028          *vms->introfn = '\0';
07029 #endif
07030          cmd = 't';
07031          break;
07032       case '*':
07033          cmd = '*';
07034          break;
07035       default: 
07036          /* If time_out and return to menu, reset already_recorded */
07037          already_recorded = 0;
07038 
07039          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07040             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07041          if (!cmd) {
07042             cmd = ast_play_and_wait(chan, "vm-starmain");
07043             /* "press star to return to the main menu" */
07044          }
07045          if (!cmd) {
07046             cmd = ast_waitfordigit(chan, 6000);
07047          }
07048          if (!cmd) {
07049             retries++;
07050          }
07051          if (retries > 3) {
07052             cmd = '*'; /* Let's cancel this beast */
07053          }
07054          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07055       }
07056    }
07057 
07058    if (valid_config(msg_cfg))
07059       ast_config_destroy(msg_cfg);
07060    if (prepend_duration)
07061       *duration = prepend_duration;
07062 
07063    if (already_recorded && cmd == -1) {
07064       /* restore original message if prepention cancelled */
07065       ast_filerename(backup, msgfile, NULL);
07066       rename(backup_textfile, textfile);
07067    }
07068 
07069    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07070       cmd = 0;
07071    return cmd;
07072 }
07073 
07074 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07075 {
07076    struct ast_event *event;
07077    char *mailbox, *context;
07078 
07079    /* Strip off @default */
07080    context = mailbox = ast_strdupa(box);
07081    strsep(&context, "@");
07082    if (ast_strlen_zero(context))
07083       context = "default";
07084 
07085    if (!(event = ast_event_new(AST_EVENT_MWI,
07086          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07087          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07088          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07089          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07090          AST_EVENT_IE_END))) {
07091       return;
07092    }
07093 
07094    ast_event_queue_and_cache(event);
07095 }
07096 
07097 /*!
07098  * \brief Sends email notification that a user has a new voicemail waiting for them.
07099  * \param chan
07100  * \param vmu
07101  * \param vms
07102  * \param msgnum
07103  * \param duration
07104  * \param fmt
07105  * \param cidnum The Caller ID phone number value.
07106  * \param cidname The Caller ID name value.
07107  * \param flag
07108  *
07109  * \return zero on success, -1 on error.
07110  */
07111 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)
07112 {
07113    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07114    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07115    const char *category;
07116    char *myserveremail = serveremail;
07117 
07118    ast_channel_lock(chan);
07119    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07120       category = ast_strdupa(category);
07121    }
07122    ast_channel_unlock(chan);
07123 
07124 #ifndef IMAP_STORAGE
07125    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07126 #else
07127    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07128 #endif
07129    make_file(fn, sizeof(fn), todir, msgnum);
07130    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07131 
07132    if (!ast_strlen_zero(vmu->attachfmt)) {
07133       if (strstr(fmt, vmu->attachfmt))
07134          fmt = vmu->attachfmt;
07135       else
07136          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);
07137    }
07138 
07139    /* Attach only the first format */
07140    fmt = ast_strdupa(fmt);
07141    stringp = fmt;
07142    strsep(&stringp, "|");
07143 
07144    if (!ast_strlen_zero(vmu->serveremail))
07145       myserveremail = vmu->serveremail;
07146 
07147    if (!ast_strlen_zero(vmu->email)) {
07148       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07149 
07150       if (attach_user_voicemail)
07151          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07152 
07153       /* XXX possible imap issue, should category be NULL XXX */
07154       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07155 
07156       if (attach_user_voicemail)
07157          DISPOSE(todir, msgnum);
07158    }
07159 
07160    if (!ast_strlen_zero(vmu->pager)) {
07161       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07162    }
07163 
07164    if (ast_test_flag(vmu, VM_DELETE))
07165       DELETE(todir, msgnum, fn, vmu);
07166 
07167    /* Leave voicemail for someone */
07168    if (ast_app_has_voicemail(ext_context, NULL)) 
07169       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07170 
07171    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07172 
07173    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);
07174    run_externnotify(vmu->context, vmu->mailbox, flag);
07175 
07176 #ifdef IMAP_STORAGE
07177    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07178    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07179       vm_imap_delete(NULL, vms->curmsg, vmu);
07180       vms->newmessages--;  /* Fix new message count */
07181    }
07182 #endif
07183 
07184    return 0;
07185 }
07186 
07187 /*!
07188  * \brief Sends a voicemail message to a mailbox recipient.
07189  * \param chan
07190  * \param context
07191  * \param vms
07192  * \param sender
07193  * \param fmt
07194  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07195  *             Will be 0 when called to forward an existing message (option 8)
07196  *             Will be 1 when called to leave a message (option 3->5)
07197  * \param record_gain 
07198  * \param urgent
07199  *
07200  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07201  * 
07202  * When in the leave message mode (is_new_message == 1):
07203  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07204  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07205  *
07206  * When in the forward message mode (is_new_message == 0):
07207  *   - retreives the current message to be forwarded
07208  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07209  *   - determines the target mailbox and folders
07210  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07211  *
07212  * \return zero on success, -1 on error.
07213  */
07214 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)
07215 {
07216 #ifdef IMAP_STORAGE
07217    int todircount = 0;
07218    struct vm_state *dstvms;
07219 #endif
07220    char username[70]="";
07221    char fn[PATH_MAX]; /* for playback of name greeting */
07222    char ecodes[16] = "#";
07223    int res = 0, cmd = 0;
07224    struct ast_vm_user *receiver = NULL, *vmtmp;
07225    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07226    char *stringp;
07227    const char *s;
07228    int saved_messages = 0;
07229    int valid_extensions = 0;
07230    char *dir;
07231    int curmsg;
07232    char urgent_str[7] = "";
07233    int prompt_played = 0;
07234 #ifndef IMAP_STORAGE
07235    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07236 #endif
07237    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07238       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07239    }
07240 
07241    if (vms == NULL) return -1;
07242    dir = vms->curdir;
07243    curmsg = vms->curmsg;
07244 
07245    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07246    while (!res && !valid_extensions) {
07247       int use_directory = 0;
07248       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07249          int done = 0;
07250          int retries = 0;
07251          cmd = 0;
07252          while ((cmd >= 0) && !done ){
07253             if (cmd)
07254                retries = 0;
07255             switch (cmd) {
07256             case '1': 
07257                use_directory = 0;
07258                done = 1;
07259                break;
07260             case '2': 
07261                use_directory = 1;
07262                done = 1;
07263                break;
07264             case '*': 
07265                cmd = 't';
07266                done = 1;
07267                break;
07268             default: 
07269                /* Press 1 to enter an extension press 2 to use the directory */
07270                cmd = ast_play_and_wait(chan, "vm-forward");
07271                if (!cmd) {
07272                   cmd = ast_waitfordigit(chan, 3000);
07273                }
07274                if (!cmd) {
07275                   retries++;
07276                }
07277                if (retries > 3) {
07278                   cmd = 't';
07279                   done = 1;
07280                }
07281                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07282             }
07283          }
07284          if (cmd < 0 || cmd == 't')
07285             break;
07286       }
07287       
07288       if (use_directory) {
07289          /* use app_directory */
07290          
07291          char old_context[sizeof(chan->context)];
07292          char old_exten[sizeof(chan->exten)];
07293          int old_priority;
07294          struct ast_app* directory_app;
07295 
07296          directory_app = pbx_findapp("Directory");
07297          if (directory_app) {
07298             char vmcontext[256];
07299             /* make backup copies */
07300             memcpy(old_context, chan->context, sizeof(chan->context));
07301             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07302             old_priority = chan->priority;
07303             
07304             /* call the the Directory, changes the channel */
07305             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07306             res = pbx_exec(chan, directory_app, vmcontext);
07307             
07308             ast_copy_string(username, chan->exten, sizeof(username));
07309             
07310             /* restore the old context, exten, and priority */
07311             memcpy(chan->context, old_context, sizeof(chan->context));
07312             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07313             chan->priority = old_priority;
07314          } else {
07315             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07316             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07317          }
07318       } else {
07319          /* Ask for an extension */
07320          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07321          prompt_played++;
07322          if (res || prompt_played > 4)
07323             break;
07324          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07325             break;
07326       }
07327       
07328       /* start all over if no username */
07329       if (ast_strlen_zero(username))
07330          continue;
07331       stringp = username;
07332       s = strsep(&stringp, "*");
07333       /* start optimistic */
07334       valid_extensions = 1;
07335       while (s) {
07336          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07337             int oldmsgs;
07338             int newmsgs;
07339             int capacity;
07340             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07341                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07342                /* Shouldn't happen, but allow trying another extension if it does */
07343                res = ast_play_and_wait(chan, "pbx-invalid");
07344                valid_extensions = 0;
07345                break;
07346             }
07347             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07348             if ((newmsgs + oldmsgs) >= capacity) {
07349                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07350                res = ast_play_and_wait(chan, "vm-mailboxfull");
07351                valid_extensions = 0;
07352                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07353                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07354                   free_user(vmtmp);
07355                }
07356                inprocess_count(receiver->mailbox, receiver->context, -1);
07357                break;
07358             }
07359             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07360          } else {
07361             /* XXX Optimization for the future.  When we encounter a single bad extension,
07362              * bailing out on all of the extensions may not be the way to go.  We should
07363              * probably just bail on that single extension, then allow the user to enter
07364              * several more. XXX
07365              */
07366             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07367                free_user(receiver);
07368             }
07369             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07370             /* "I am sorry, that's not a valid extension.  Please try again." */
07371             res = ast_play_and_wait(chan, "pbx-invalid");
07372             valid_extensions = 0;
07373             break;
07374          }
07375 
07376          /* play name if available, else play extension number */
07377          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07378          RETRIEVE(fn, -1, s, receiver->context);
07379          if (ast_fileexists(fn, NULL, NULL) > 0) {
07380             res = ast_stream_and_wait(chan, fn, ecodes);
07381             if (res) {
07382                DISPOSE(fn, -1);
07383                return res;
07384             }
07385          } else {
07386             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07387          }
07388          DISPOSE(fn, -1);
07389 
07390          s = strsep(&stringp, "*");
07391       }
07392       /* break from the loop of reading the extensions */
07393       if (valid_extensions)
07394          break;
07395    }
07396    /* check if we're clear to proceed */
07397    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07398       return res;
07399    if (is_new_message == 1) {
07400       struct leave_vm_options leave_options;
07401       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07402       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07403 
07404       /* Send VoiceMail */
07405       memset(&leave_options, 0, sizeof(leave_options));
07406       leave_options.record_gain = record_gain;
07407       cmd = leave_voicemail(chan, mailbox, &leave_options);
07408    } else {
07409       /* Forward VoiceMail */
07410       long duration = 0;
07411       struct vm_state vmstmp;
07412       int copy_msg_result = 0;
07413       memcpy(&vmstmp, vms, sizeof(vmstmp));
07414 
07415       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07416 
07417       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07418       if (!cmd) {
07419          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07420 #ifdef IMAP_STORAGE
07421             int attach_user_voicemail;
07422             char *myserveremail = serveremail;
07423             
07424             /* get destination mailbox */
07425             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07426             if (!dstvms) {
07427                dstvms = create_vm_state_from_user(vmtmp);
07428             }
07429             if (dstvms) {
07430                init_mailstream(dstvms, 0);
07431                if (!dstvms->mailstream) {
07432                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07433                } else {
07434                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07435                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07436                }
07437             } else {
07438                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07439             }
07440             if (!ast_strlen_zero(vmtmp->serveremail))
07441                myserveremail = vmtmp->serveremail;
07442             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07443             /* NULL category for IMAP storage */
07444             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07445                dstvms->curbox,
07446                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07447                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07448                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07449                NULL, urgent_str);
07450 #else
07451             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07452 #endif
07453             saved_messages++;
07454             AST_LIST_REMOVE_CURRENT(list);
07455             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07456             free_user(vmtmp);
07457             if (res)
07458                break;
07459          }
07460          AST_LIST_TRAVERSE_SAFE_END;
07461          if (saved_messages > 0 && !copy_msg_result) {
07462             /* give confirmation that the message was saved */
07463             /* commented out since we can't forward batches yet
07464             if (saved_messages == 1)
07465                res = ast_play_and_wait(chan, "vm-message");
07466             else
07467                res = ast_play_and_wait(chan, "vm-messages");
07468             if (!res)
07469                res = ast_play_and_wait(chan, "vm-saved"); */
07470 #ifdef IMAP_STORAGE
07471             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07472             if (ast_strlen_zero(vmstmp.introfn))
07473 #endif
07474             res = ast_play_and_wait(chan, "vm-msgsaved");
07475          }
07476 #ifndef IMAP_STORAGE
07477          else {
07478             /* with IMAP, mailbox full warning played by imap_check_limits */
07479             res = ast_play_and_wait(chan, "vm-mailboxfull");
07480          }
07481          /* Restore original message without prepended message if backup exists */
07482          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07483          strcpy(textfile, msgfile);
07484          strcpy(backup, msgfile);
07485          strcpy(backup_textfile, msgfile);
07486          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07487          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07488          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07489          if (ast_fileexists(backup, NULL, NULL) > 0) {
07490             ast_filerename(backup, msgfile, NULL);
07491             rename(backup_textfile, textfile);
07492          }
07493 #endif
07494       }
07495       DISPOSE(dir, curmsg);
07496 #ifndef IMAP_STORAGE
07497       if (cmd) { /* assuming hangup, cleanup backup file */
07498          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07499          strcpy(textfile, msgfile);
07500          strcpy(backup_textfile, msgfile);
07501          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07502          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07503          rename(backup_textfile, textfile);
07504       }
07505 #endif
07506    }
07507 
07508    /* If anything failed above, we still have this list to free */
07509    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07510       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07511       free_user(vmtmp);
07512    }
07513    return res ? res : cmd;
07514 }
07515 
07516 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07517 {
07518    int res;
07519    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07520       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07521    return res;
07522 }
07523 
07524 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07525 {
07526    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07527    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);
07528 }
07529 
07530 static int play_message_category(struct ast_channel *chan, const char *category)
07531 {
07532    int res = 0;
07533 
07534    if (!ast_strlen_zero(category))
07535       res = ast_play_and_wait(chan, category);
07536 
07537    if (res) {
07538       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07539       res = 0;
07540    }
07541 
07542    return res;
07543 }
07544 
07545 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07546 {
07547    int res = 0;
07548    struct vm_zone *the_zone = NULL;
07549    time_t t;
07550 
07551    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07552       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07553       return 0;
07554    }
07555 
07556    /* Does this user have a timezone specified? */
07557    if (!ast_strlen_zero(vmu->zonetag)) {
07558       /* Find the zone in the list */
07559       struct vm_zone *z;
07560       AST_LIST_LOCK(&zones);
07561       AST_LIST_TRAVERSE(&zones, z, list) {
07562          if (!strcmp(z->name, vmu->zonetag)) {
07563             the_zone = z;
07564             break;
07565          }
07566       }
07567       AST_LIST_UNLOCK(&zones);
07568    }
07569 
07570 /* No internal variable parsing for now, so we'll comment it out for the time being */
07571 #if 0
07572    /* Set the DIFF_* variables */
07573    ast_localtime(&t, &time_now, NULL);
07574    tv_now = ast_tvnow();
07575    ast_localtime(&tv_now, &time_then, NULL);
07576 
07577    /* Day difference */
07578    if (time_now.tm_year == time_then.tm_year)
07579       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07580    else
07581       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07582    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07583 
07584    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07585 #endif
07586    if (the_zone) {
07587       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07588    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07589       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07590    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07591       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07592    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07593       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);
07594    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07595       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07596    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07597       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07598    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07599       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07600    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07601       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);
07602    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07603       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07604    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07605       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07606    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07607       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);
07608    } else {
07609       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07610    }
07611 #if 0
07612    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07613 #endif
07614    return res;
07615 }
07616 
07617 
07618 
07619 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07620 {
07621    int res = 0;
07622    int i;
07623    char *callerid, *name;
07624    char prefile[PATH_MAX] = "";
07625    
07626 
07627    /* If voicemail cid is not enabled, or we didn't get cid or context from
07628     * the attribute file, leave now.
07629     *
07630     * TODO Still need to change this so that if this function is called by the
07631     * message envelope (and someone is explicitly requesting to hear the CID),
07632     * it does not check to see if CID is enabled in the config file.
07633     */
07634    if ((cid == NULL)||(context == NULL))
07635       return res;
07636 
07637    /* Strip off caller ID number from name */
07638    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07639    ast_callerid_parse(cid, &name, &callerid);
07640    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07641       /* Check for internal contexts and only */
07642       /* say extension when the call didn't come from an internal context in the list */
07643       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07644          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07645          if ((strcmp(cidinternalcontexts[i], context) == 0))
07646             break;
07647       }
07648       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07649          if (!res) {
07650             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07651             if (!ast_strlen_zero(prefile)) {
07652             /* See if we can find a recorded name for this person instead of their extension number */
07653                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07654                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07655                   if (!callback)
07656                      res = wait_file2(chan, vms, "vm-from");
07657                   res = ast_stream_and_wait(chan, prefile, "");
07658                } else {
07659                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07660                   /* Say "from extension" as one saying to sound smoother */
07661                   if (!callback)
07662                      res = wait_file2(chan, vms, "vm-from-extension");
07663                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07664                }
07665             }
07666          }
07667       } else if (!res) {
07668          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07669          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07670          if (!callback)
07671             res = wait_file2(chan, vms, "vm-from-phonenumber");
07672          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07673       }
07674    } else {
07675       /* Number unknown */
07676       ast_debug(1, "VM-CID: From an unknown number\n");
07677       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07678       res = wait_file2(chan, vms, "vm-unknown-caller");
07679    }
07680    return res;
07681 }
07682 
07683 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07684 {
07685    int res = 0;
07686    int durationm;
07687    int durations;
07688    /* Verify that we have a duration for the message */
07689    if (duration == NULL)
07690       return res;
07691 
07692    /* Convert from seconds to minutes */
07693    durations = atoi(duration);
07694    durationm = (durations / 60);
07695 
07696    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07697 
07698    if ((!res) && (durationm >= minduration)) {
07699       res = wait_file2(chan, vms, "vm-duration");
07700 
07701       /* POLISH syntax */
07702       if (!strncasecmp(chan->language, "pl", 2)) {
07703          div_t num = div(durationm, 10);
07704 
07705          if (durationm == 1) {
07706             res = ast_play_and_wait(chan, "digits/1z");
07707             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07708          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07709             if (num.rem == 2) {
07710                if (!num.quot) {
07711                   res = ast_play_and_wait(chan, "digits/2-ie");
07712                } else {
07713                   res = say_and_wait(chan, durationm - 2 , chan->language);
07714                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07715                }
07716             } else {
07717                res = say_and_wait(chan, durationm, chan->language);
07718             }
07719             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07720          } else {
07721             res = say_and_wait(chan, durationm, chan->language);
07722             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07723          }
07724       /* DEFAULT syntax */
07725       } else {
07726          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07727          res = wait_file2(chan, vms, "vm-minutes");
07728       }
07729    }
07730    return res;
07731 }
07732 
07733 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07734 {
07735    int res = 0;
07736    char filename[256], *cid;
07737    const char *origtime, *context, *category, *duration, *flag;
07738    struct ast_config *msg_cfg;
07739    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07740 
07741    vms->starting = 0;
07742    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07743    adsi_message(chan, vms);
07744    if (!vms->curmsg) {
07745       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07746    } else if (vms->curmsg == vms->lastmsg) {
07747       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07748    }
07749 
07750    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07751    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07752    msg_cfg = ast_config_load(filename, config_flags);
07753    if (!valid_config(msg_cfg)) {
07754       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07755       return 0;
07756    }
07757    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07758 
07759    /* Play the word urgent if we are listening to urgent messages */
07760    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07761       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07762    }
07763 
07764    if (!res) {
07765       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07766       /* POLISH syntax */
07767       if (!strncasecmp(chan->language, "pl", 2)) {
07768          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07769             int ten, one;
07770             char nextmsg[256];
07771             ten = (vms->curmsg + 1) / 10;
07772             one = (vms->curmsg + 1) % 10;
07773 
07774             if (vms->curmsg < 20) {
07775                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07776                res = wait_file2(chan, vms, nextmsg);
07777             } else {
07778                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07779                res = wait_file2(chan, vms, nextmsg);
07780                if (one > 0) {
07781                   if (!res) {
07782                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07783                      res = wait_file2(chan, vms, nextmsg);
07784                   }
07785                }
07786             }
07787          }
07788          if (!res)
07789             res = wait_file2(chan, vms, "vm-message");
07790       /* HEBREW syntax */
07791       } else if (!strncasecmp(chan->language, "he", 2)) {
07792          if (!vms->curmsg) {
07793             res = wait_file2(chan, vms, "vm-message");
07794             res = wait_file2(chan, vms, "vm-first");
07795          } else if (vms->curmsg == vms->lastmsg) {
07796             res = wait_file2(chan, vms, "vm-message");
07797             res = wait_file2(chan, vms, "vm-last");
07798          } else {
07799             res = wait_file2(chan, vms, "vm-message");
07800             res = wait_file2(chan, vms, "vm-number");
07801             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07802          }
07803       /* VIETNAMESE syntax */
07804       } else if (!strncasecmp(chan->language, "vi", 2)) {
07805          if (!vms->curmsg) {
07806             res = wait_file2(chan, vms, "vm-message");
07807             res = wait_file2(chan, vms, "vm-first");
07808          } else if (vms->curmsg == vms->lastmsg) {
07809             res = wait_file2(chan, vms, "vm-message");
07810             res = wait_file2(chan, vms, "vm-last");
07811          } else {
07812             res = wait_file2(chan, vms, "vm-message");
07813             res = wait_file2(chan, vms, "vm-number");
07814             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07815          }
07816       } else {
07817          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07818             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07819          } else { /* DEFAULT syntax */
07820             res = wait_file2(chan, vms, "vm-message");
07821          }
07822          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07823             if (!res) {
07824                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07825                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07826             }
07827          }
07828       }
07829    }
07830 
07831    if (!valid_config(msg_cfg)) {
07832       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07833       return 0;
07834    }
07835 
07836    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07837       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07838       DISPOSE(vms->curdir, vms->curmsg);
07839       ast_config_destroy(msg_cfg);
07840       return 0;
07841    }
07842 
07843    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07844    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07845    category = ast_variable_retrieve(msg_cfg, "message", "category");
07846 
07847    context = ast_variable_retrieve(msg_cfg, "message", "context");
07848    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07849       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07850    if (!res) {
07851       res = play_message_category(chan, category);
07852    }
07853    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07854       res = play_message_datetime(chan, vmu, origtime, filename);
07855    }
07856    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07857       res = play_message_callerid(chan, vms, cid, context, 0);
07858    }
07859    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07860       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07861    }
07862    /* Allow pressing '1' to skip envelope / callerid */
07863    if (res == '1') {
07864       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07865       res = 0;
07866    }
07867    ast_config_destroy(msg_cfg);
07868 
07869    if (!res) {
07870       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07871 #ifdef IMAP_STORAGE
07872       ast_mutex_lock(&vms->lock);
07873 #endif
07874       vms->heard[vms->curmsg] = 1;
07875 #ifdef IMAP_STORAGE
07876       ast_mutex_unlock(&vms->lock);
07877       /*IMAP storage stores any prepended message from a forward
07878        * as a separate file from the rest of the message
07879        */
07880       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07881          wait_file(chan, vms, vms->introfn);
07882       }
07883 #endif
07884       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07885          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07886          res = 0;
07887       }
07888       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07889    }
07890    DISPOSE(vms->curdir, vms->curmsg);
07891    return res;
07892 }
07893 
07894 #ifdef IMAP_STORAGE
07895 static int imap_remove_file(char *dir, int msgnum)
07896 {
07897    char fn[PATH_MAX];
07898    char full_fn[PATH_MAX];
07899    char intro[PATH_MAX] = {0,};
07900    
07901    if (msgnum > -1) {
07902       make_file(fn, sizeof(fn), dir, msgnum);
07903       snprintf(intro, sizeof(intro), "%sintro", fn);
07904    } else
07905       ast_copy_string(fn, dir, sizeof(fn));
07906    
07907    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07908       ast_filedelete(fn, NULL);
07909       if (!ast_strlen_zero(intro)) {
07910          ast_filedelete(intro, NULL);
07911       }
07912       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07913       unlink(full_fn);
07914    }
07915    return 0;
07916 }
07917 
07918 
07919 
07920 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07921 {
07922    char *file, *filename;
07923    char *attachment;
07924    char arg[10];
07925    int i;
07926    BODY* body;
07927 
07928    file = strrchr(ast_strdupa(dir), '/');
07929    if (file) {
07930       *file++ = '\0';
07931    } else {
07932       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07933       return -1;
07934    }
07935 
07936    ast_mutex_lock(&vms->lock);
07937    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07938       mail_fetchstructure(vms->mailstream, i + 1, &body);
07939       /* We have the body, now we extract the file name of the first attachment. */
07940       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07941          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07942       } else {
07943          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07944          ast_mutex_unlock(&vms->lock);
07945          return -1;
07946       }
07947       filename = strsep(&attachment, ".");
07948       if (!strcmp(filename, file)) {
07949          sprintf(arg, "%d", i + 1);
07950          mail_setflag(vms->mailstream, arg, "\\DELETED");
07951       }
07952    }
07953    mail_expunge(vms->mailstream);
07954    ast_mutex_unlock(&vms->lock);
07955    return 0;
07956 }
07957 
07958 #elif !defined(IMAP_STORAGE)
07959 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07960 {
07961    int count_msg, last_msg;
07962 
07963    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07964 
07965    /* Rename the member vmbox HERE so that we don't try to return before
07966     * we know what's going on.
07967     */
07968    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07969 
07970    /* Faster to make the directory than to check if it exists. */
07971    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07972 
07973    /* traverses directory using readdir (or select query for ODBC) */
07974    count_msg = count_messages(vmu, vms->curdir);
07975    if (count_msg < 0) {
07976       return count_msg;
07977    } else {
07978       vms->lastmsg = count_msg - 1;
07979    }
07980 
07981    if (vm_allocate_dh(vms, vmu, count_msg)) {
07982       return -1;
07983    }
07984 
07985    /*
07986    The following test is needed in case sequencing gets messed up.
07987    There appears to be more than one way to mess up sequence, so
07988    we will not try to find all of the root causes--just fix it when
07989    detected.
07990    */
07991 
07992    if (vm_lock_path(vms->curdir)) {
07993       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07994       return ERROR_LOCK_PATH;
07995    }
07996 
07997    /* for local storage, checks directory for messages up to maxmsg limit */
07998    last_msg = last_message_index(vmu, vms->curdir);
07999    ast_unlock_path(vms->curdir);
08000 
08001    if (last_msg < -1) {
08002       return last_msg;
08003    } else if (vms->lastmsg != last_msg) {
08004       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);
08005       resequence_mailbox(vmu, vms->curdir, count_msg);
08006    }
08007 
08008    return 0;
08009 }
08010 #endif
08011 
08012 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
08013 {
08014    int x = 0;
08015    int last_msg_idx = 0;
08016 
08017 #ifndef IMAP_STORAGE
08018    int res = 0, nummsg;
08019    char fn2[PATH_MAX];
08020 #endif
08021 
08022    if (vms->lastmsg <= -1) {
08023       goto done;
08024    }
08025 
08026    vms->curmsg = -1;
08027 #ifndef IMAP_STORAGE
08028    /* Get the deleted messages fixed */
08029    if (vm_lock_path(vms->curdir)) {
08030       return ERROR_LOCK_PATH;
08031    }
08032 
08033    /* update count as message may have arrived while we've got mailbox open */
08034    last_msg_idx = last_message_index(vmu, vms->curdir);
08035    if (last_msg_idx != vms->lastmsg) {
08036       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08037    }
08038 
08039    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08040    for (x = 0; x < last_msg_idx + 1; x++) {
08041       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08042          /* Save this message.  It's not in INBOX or hasn't been heard */
08043          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08044          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08045             break;
08046          }
08047          vms->curmsg++;
08048          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08049          if (strcmp(vms->fn, fn2)) {
08050             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08051          }
08052       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08053          /* Move to old folder before deleting */
08054          res = save_to_folder(vmu, vms, x, 1);
08055          if (res == ERROR_LOCK_PATH) {
08056             /* If save failed do not delete the message */
08057             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08058             vms->deleted[x] = 0;
08059             vms->heard[x] = 0;
08060             --x;
08061          }
08062       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08063          /* Move to deleted folder */
08064          res = save_to_folder(vmu, vms, x, 10);
08065          if (res == ERROR_LOCK_PATH) {
08066             /* If save failed do not delete the message */
08067             vms->deleted[x] = 0;
08068             vms->heard[x] = 0;
08069             --x;
08070          }
08071       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08072          /* If realtime storage enabled - we should explicitly delete this message,
08073          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08074          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08075          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08076             DELETE(vms->curdir, x, vms->fn, vmu);
08077          }
08078       }
08079    }
08080 
08081    /* Delete ALL remaining messages */
08082    nummsg = x - 1;
08083    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08084       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08085       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08086          DELETE(vms->curdir, x, vms->fn, vmu);
08087       }
08088    }
08089    ast_unlock_path(vms->curdir);
08090 #else /* defined(IMAP_STORAGE) */
08091    ast_mutex_lock(&vms->lock);
08092    if (vms->deleted) {
08093       /* Since we now expunge after each delete, deleting in reverse order
08094        * ensures that no reordering occurs between each step. */
08095       last_msg_idx = vms->dh_arraysize;
08096       for (x = last_msg_idx - 1; x >= 0; x--) {
08097          if (vms->deleted[x]) {
08098             ast_debug(3, "IMAP delete of %d\n", x);
08099             DELETE(vms->curdir, x, vms->fn, vmu);
08100          }
08101       }
08102    }
08103 #endif
08104 
08105 done:
08106    if (vms->deleted) {
08107       ast_free(vms->deleted);
08108       vms->deleted = NULL;
08109    }
08110    if (vms->heard) {
08111       ast_free(vms->heard);
08112       vms->heard = NULL;
08113    }
08114    vms->dh_arraysize = 0;
08115 #ifdef IMAP_STORAGE
08116    ast_mutex_unlock(&vms->lock);
08117 #endif
08118 
08119    return 0;
08120 }
08121 
08122 /* In Greek even though we CAN use a syntax like "friends messages"
08123  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08124  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08125  * syntax for the above three categories which is more elegant.
08126  */
08127 
08128 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08129 {
08130    int cmd;
08131    char *buf;
08132 
08133    buf = ast_alloca(strlen(box) + 2);
08134    strcpy(buf, box);
08135    strcat(buf, "s");
08136 
08137    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08138       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08139       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08140    } else {
08141       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08142       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08143    }
08144 }
08145 
08146 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08147 {
08148    int cmd;
08149 
08150    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08151       if (!strcasecmp(box, "vm-INBOX"))
08152          cmd = ast_play_and_wait(chan, "vm-new-e");
08153       else
08154          cmd = ast_play_and_wait(chan, "vm-old-e");
08155       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08156    } else {
08157       cmd = ast_play_and_wait(chan, "vm-messages");
08158       return cmd ? cmd : ast_play_and_wait(chan, box);
08159    }
08160 }
08161 
08162 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08163 {
08164    int cmd;
08165 
08166    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08167       cmd = ast_play_and_wait(chan, "vm-messages");
08168       return cmd ? cmd : ast_play_and_wait(chan, box);
08169    } else {
08170       cmd = ast_play_and_wait(chan, box);
08171       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08172    }
08173 }
08174 
08175 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08176 {
08177    int cmd;
08178 
08179    if (  !strncasecmp(chan->language, "it", 2) ||
08180         !strncasecmp(chan->language, "es", 2) ||
08181         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08182       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08183       return cmd ? cmd : ast_play_and_wait(chan, box);
08184    } else if (!strncasecmp(chan->language, "gr", 2)) {
08185       return vm_play_folder_name_gr(chan, box);
08186    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08187       return ast_play_and_wait(chan, box);
08188    } else if (!strncasecmp(chan->language, "pl", 2)) {
08189       return vm_play_folder_name_pl(chan, box);
08190    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08191       return vm_play_folder_name_ua(chan, box);
08192    } else if (!strncasecmp(chan->language, "vi", 2)) {
08193       return ast_play_and_wait(chan, box);
08194    } else {  /* Default English */
08195       cmd = ast_play_and_wait(chan, box);
08196       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08197    }
08198 }
08199 
08200 /* GREEK SYNTAX
08201    In greek the plural for old/new is
08202    different so we need the following files
08203    We also need vm-denExeteMynhmata because
08204    this syntax is different.
08205 
08206    -> vm-Olds.wav : "Palia"
08207    -> vm-INBOXs.wav : "Nea"
08208    -> vm-denExeteMynhmata : "den exete mynhmata"
08209 */
08210 
08211 
08212 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08213 {
08214    int res = 0;
08215 
08216    if (vms->newmessages) {
08217       res = ast_play_and_wait(chan, "vm-youhave");
08218       if (!res) 
08219          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08220       if (!res) {
08221          if ((vms->newmessages == 1)) {
08222             res = ast_play_and_wait(chan, "vm-INBOX");
08223             if (!res)
08224                res = ast_play_and_wait(chan, "vm-message");
08225          } else {
08226             res = ast_play_and_wait(chan, "vm-INBOXs");
08227             if (!res)
08228                res = ast_play_and_wait(chan, "vm-messages");
08229          }
08230       }
08231    } else if (vms->oldmessages){
08232       res = ast_play_and_wait(chan, "vm-youhave");
08233       if (!res)
08234          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08235       if ((vms->oldmessages == 1)){
08236          res = ast_play_and_wait(chan, "vm-Old");
08237          if (!res)
08238             res = ast_play_and_wait(chan, "vm-message");
08239       } else {
08240          res = ast_play_and_wait(chan, "vm-Olds");
08241          if (!res)
08242             res = ast_play_and_wait(chan, "vm-messages");
08243       }
08244    } else if (!vms->oldmessages && !vms->newmessages) 
08245       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08246    return res;
08247 }
08248 
08249 /* Version of vm_intro() designed to work for many languages.
08250  *
08251  * It is hoped that this function can prevent the proliferation of 
08252  * language-specific vm_intro() functions and in time replace the language-
08253  * specific functions which already exist.  An examination of the language-
08254  * specific functions revealed that they all corrected the same deficiencies
08255  * in vm_intro_en() (which was the default function). Namely:
08256  *
08257  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08258  *     wording of the voicemail greeting hides this problem.  For example,
08259  *     vm-INBOX contains only the word "new".  This means that both of these
08260  *     sequences produce valid utterances:
08261  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08262  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08263  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08264  *     in many languages) the first utterance becomes "you have 1 the new message".
08265  *  2) The function contains hardcoded rules for pluralizing the word "message".
08266  *     These rules are correct for English, but not for many other languages.
08267  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08268  *     required in many languages.
08269  *  4) The gender of the word for "message" is not specified. This is a problem
08270  *     because in many languages the gender of the number in phrases such
08271  *     as "you have one new message" must match the gender of the word
08272  *     meaning "message".
08273  *
08274  * Fixing these problems for each new language has meant duplication of effort.
08275  * This new function solves the problems in the following general ways:
08276  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08277  *     and vm-Old respectively for those languages where it makes sense.
08278  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08279  *     on vm-message.
08280  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08281  *     prefix on vm-new and vm-old (none for English).
08282  *  4) Pass the gender of the language's word for "message" as an agument to
08283  *     this function which is can in turn pass on to the functions which 
08284  *     say numbers and put endings on nounds and adjectives.
08285  *
08286  * All languages require these messages:
08287  *  vm-youhave    "You have..."
08288  *  vm-and     "and"
08289  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08290  *
08291  * To use it for English, you will need these additional sound files:
08292  *  vm-new     "new"
08293  *  vm-message    "message", singular
08294  *  vm-messages      "messages", plural
08295  *
08296  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08297  *
08298  *  vm-newn    "novoye" (singular, neuter)
08299  *  vm-newx    "novikh" (counting plural form, genative plural)
08300  *  vm-message    "sobsheniye" (singular form)
08301  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08302  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08303  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08304  *  digits/2n     "dva" (neuter singular)
08305  */
08306 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08307 {
08308    int res;
08309    int lastnum = 0;
08310 
08311    res = ast_play_and_wait(chan, "vm-youhave");
08312 
08313    if (!res && vms->newmessages) {
08314       lastnum = vms->newmessages;
08315 
08316       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08317          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08318       }
08319 
08320       if (!res && vms->oldmessages) {
08321          res = ast_play_and_wait(chan, "vm-and");
08322       }
08323    }
08324 
08325    if (!res && vms->oldmessages) {
08326       lastnum = vms->oldmessages;
08327 
08328       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08329          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08330       }
08331    }
08332 
08333    if (!res) {
08334       if (lastnum == 0) {
08335          res = ast_play_and_wait(chan, "vm-no");
08336       }
08337       if (!res) {
08338          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08339       }
08340    }
08341 
08342    return res;
08343 }
08344 
08345 /* Default Hebrew syntax */
08346 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08347 {
08348    int res = 0;
08349 
08350    /* Introduce messages they have */
08351    if (!res) {
08352       if ((vms->newmessages) || (vms->oldmessages)) {
08353          res = ast_play_and_wait(chan, "vm-youhave");
08354       }
08355       /*
08356        * The word "shtei" refers to the number 2 in hebrew when performing a count
08357        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08358        * an element, this is one of them.
08359        */
08360       if (vms->newmessages) {
08361          if (!res) {
08362             if (vms->newmessages == 1) {
08363                res = ast_play_and_wait(chan, "vm-INBOX1");
08364             } else {
08365                if (vms->newmessages == 2) {
08366                   res = ast_play_and_wait(chan, "vm-shtei");
08367                } else {
08368                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08369                }
08370                res = ast_play_and_wait(chan, "vm-INBOX");
08371             }
08372          }
08373          if (vms->oldmessages && !res) {
08374             res = ast_play_and_wait(chan, "vm-and");
08375             if (vms->oldmessages == 1) {
08376                res = ast_play_and_wait(chan, "vm-Old1");
08377             } else {
08378                if (vms->oldmessages == 2) {
08379                   res = ast_play_and_wait(chan, "vm-shtei");
08380                } else {
08381                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08382                }
08383                res = ast_play_and_wait(chan, "vm-Old");
08384             }
08385          }
08386       }
08387       if (!res && vms->oldmessages && !vms->newmessages) {
08388          if (!res) {
08389             if (vms->oldmessages == 1) {
08390                res = ast_play_and_wait(chan, "vm-Old1");
08391             } else {
08392                if (vms->oldmessages == 2) {
08393                   res = ast_play_and_wait(chan, "vm-shtei");
08394                } else {
08395                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08396                }
08397                res = ast_play_and_wait(chan, "vm-Old");
08398             }
08399          }
08400       }
08401       if (!res) {
08402          if (!vms->oldmessages && !vms->newmessages) {
08403             if (!res) {
08404                res = ast_play_and_wait(chan, "vm-nomessages");
08405             }
08406          }
08407       }
08408    }
08409    return res;
08410 }
08411    
08412 /* Default English syntax */
08413 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08414 {
08415    int res;
08416 
08417    /* Introduce messages they have */
08418    res = ast_play_and_wait(chan, "vm-youhave");
08419    if (!res) {
08420       if (vms->urgentmessages) {
08421          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08422          if (!res)
08423             res = ast_play_and_wait(chan, "vm-Urgent");
08424          if ((vms->oldmessages || vms->newmessages) && !res) {
08425             res = ast_play_and_wait(chan, "vm-and");
08426          } else if (!res) {
08427             if ((vms->urgentmessages == 1))
08428                res = ast_play_and_wait(chan, "vm-message");
08429             else
08430                res = ast_play_and_wait(chan, "vm-messages");
08431          }
08432       }
08433       if (vms->newmessages) {
08434          res = say_and_wait(chan, vms->newmessages, chan->language);
08435          if (!res)
08436             res = ast_play_and_wait(chan, "vm-INBOX");
08437          if (vms->oldmessages && !res)
08438             res = ast_play_and_wait(chan, "vm-and");
08439          else if (!res) {
08440             if ((vms->newmessages == 1))
08441                res = ast_play_and_wait(chan, "vm-message");
08442             else
08443                res = ast_play_and_wait(chan, "vm-messages");
08444          }
08445             
08446       }
08447       if (!res && vms->oldmessages) {
08448          res = say_and_wait(chan, vms->oldmessages, chan->language);
08449          if (!res)
08450             res = ast_play_and_wait(chan, "vm-Old");
08451          if (!res) {
08452             if (vms->oldmessages == 1)
08453                res = ast_play_and_wait(chan, "vm-message");
08454             else
08455                res = ast_play_and_wait(chan, "vm-messages");
08456          }
08457       }
08458       if (!res) {
08459          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08460             res = ast_play_and_wait(chan, "vm-no");
08461             if (!res)
08462                res = ast_play_and_wait(chan, "vm-messages");
08463          }
08464       }
08465    }
08466    return res;
08467 }
08468 
08469 /* ITALIAN syntax */
08470 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08471 {
08472    /* Introduce messages they have */
08473    int res;
08474    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08475       res = ast_play_and_wait(chan, "vm-no") ||
08476          ast_play_and_wait(chan, "vm-message");
08477    else
08478       res = ast_play_and_wait(chan, "vm-youhave");
08479    if (!res && vms->newmessages) {
08480       res = (vms->newmessages == 1) ?
08481          ast_play_and_wait(chan, "digits/un") ||
08482          ast_play_and_wait(chan, "vm-nuovo") ||
08483          ast_play_and_wait(chan, "vm-message") :
08484          /* 2 or more new messages */
08485          say_and_wait(chan, vms->newmessages, chan->language) ||
08486          ast_play_and_wait(chan, "vm-nuovi") ||
08487          ast_play_and_wait(chan, "vm-messages");
08488       if (!res && vms->oldmessages)
08489          res = ast_play_and_wait(chan, "vm-and");
08490    }
08491    if (!res && vms->oldmessages) {
08492       res = (vms->oldmessages == 1) ?
08493          ast_play_and_wait(chan, "digits/un") ||
08494          ast_play_and_wait(chan, "vm-vecchio") ||
08495          ast_play_and_wait(chan, "vm-message") :
08496          /* 2 or more old messages */
08497          say_and_wait(chan, vms->oldmessages, chan->language) ||
08498          ast_play_and_wait(chan, "vm-vecchi") ||
08499          ast_play_and_wait(chan, "vm-messages");
08500    }
08501    return res;
08502 }
08503 
08504 /* POLISH syntax */
08505 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08506 {
08507    /* Introduce messages they have */
08508    int res;
08509    div_t num;
08510 
08511    if (!vms->oldmessages && !vms->newmessages) {
08512       res = ast_play_and_wait(chan, "vm-no");
08513       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08514       return res;
08515    } else {
08516       res = ast_play_and_wait(chan, "vm-youhave");
08517    }
08518 
08519    if (vms->newmessages) {
08520       num = div(vms->newmessages, 10);
08521       if (vms->newmessages == 1) {
08522          res = ast_play_and_wait(chan, "digits/1-a");
08523          res = res ? res : ast_play_and_wait(chan, "vm-new-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->newmessages - 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->newmessages, chan->language);
08535          }
08536          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08537          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08538       } else {
08539          res = say_and_wait(chan, vms->newmessages, chan->language);
08540          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08541          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08542       }
08543       if (!res && vms->oldmessages)
08544          res = ast_play_and_wait(chan, "vm-and");
08545    }
08546    if (!res && vms->oldmessages) {
08547       num = div(vms->oldmessages, 10);
08548       if (vms->oldmessages == 1) {
08549          res = ast_play_and_wait(chan, "digits/1-a");
08550          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08551          res = res ? res : ast_play_and_wait(chan, "vm-message");
08552       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08553          if (num.rem == 2) {
08554             if (!num.quot) {
08555                res = ast_play_and_wait(chan, "digits/2-ie");
08556             } else {
08557                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08558                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08559             }
08560          } else {
08561             res = say_and_wait(chan, vms->oldmessages, chan->language);
08562          }
08563          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08564          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08565       } else {
08566          res = say_and_wait(chan, vms->oldmessages, chan->language);
08567          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08568          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08569       }
08570    }
08571 
08572    return res;
08573 }
08574 
08575 /* SWEDISH syntax */
08576 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08577 {
08578    /* Introduce messages they have */
08579    int res;
08580 
08581    res = ast_play_and_wait(chan, "vm-youhave");
08582    if (res)
08583       return res;
08584 
08585    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08586       res = ast_play_and_wait(chan, "vm-no");
08587       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08588       return res;
08589    }
08590 
08591    if (vms->newmessages) {
08592       if ((vms->newmessages == 1)) {
08593          res = ast_play_and_wait(chan, "digits/ett");
08594          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08595          res = res ? res : ast_play_and_wait(chan, "vm-message");
08596       } else {
08597          res = say_and_wait(chan, vms->newmessages, chan->language);
08598          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08599          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08600       }
08601       if (!res && vms->oldmessages)
08602          res = ast_play_and_wait(chan, "vm-and");
08603    }
08604    if (!res && vms->oldmessages) {
08605       if (vms->oldmessages == 1) {
08606          res = ast_play_and_wait(chan, "digits/ett");
08607          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08608          res = res ? res : ast_play_and_wait(chan, "vm-message");
08609       } else {
08610          res = say_and_wait(chan, vms->oldmessages, chan->language);
08611          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08612          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08613       }
08614    }
08615 
08616    return res;
08617 }
08618 
08619 /* NORWEGIAN syntax */
08620 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08621 {
08622    /* Introduce messages they have */
08623    int res;
08624 
08625    res = ast_play_and_wait(chan, "vm-youhave");
08626    if (res)
08627       return res;
08628 
08629    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08630       res = ast_play_and_wait(chan, "vm-no");
08631       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08632       return res;
08633    }
08634 
08635    if (vms->newmessages) {
08636       if ((vms->newmessages == 1)) {
08637          res = ast_play_and_wait(chan, "digits/1");
08638          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08639          res = res ? res : ast_play_and_wait(chan, "vm-message");
08640       } else {
08641          res = say_and_wait(chan, vms->newmessages, chan->language);
08642          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08643          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08644       }
08645       if (!res && vms->oldmessages)
08646          res = ast_play_and_wait(chan, "vm-and");
08647    }
08648    if (!res && vms->oldmessages) {
08649       if (vms->oldmessages == 1) {
08650          res = ast_play_and_wait(chan, "digits/1");
08651          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08652          res = res ? res : ast_play_and_wait(chan, "vm-message");
08653       } else {
08654          res = say_and_wait(chan, vms->oldmessages, chan->language);
08655          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08656          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08657       }
08658    }
08659 
08660    return res;
08661 }
08662 
08663 /* GERMAN syntax */
08664 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08665 {
08666    /* Introduce messages they have */
08667    int res;
08668    res = ast_play_and_wait(chan, "vm-youhave");
08669    if (!res) {
08670       if (vms->newmessages) {
08671          if ((vms->newmessages == 1))
08672             res = ast_play_and_wait(chan, "digits/1F");
08673          else
08674             res = say_and_wait(chan, vms->newmessages, chan->language);
08675          if (!res)
08676             res = ast_play_and_wait(chan, "vm-INBOX");
08677          if (vms->oldmessages && !res)
08678             res = ast_play_and_wait(chan, "vm-and");
08679          else if (!res) {
08680             if ((vms->newmessages == 1))
08681                res = ast_play_and_wait(chan, "vm-message");
08682             else
08683                res = ast_play_and_wait(chan, "vm-messages");
08684          }
08685             
08686       }
08687       if (!res && vms->oldmessages) {
08688          if (vms->oldmessages == 1)
08689             res = ast_play_and_wait(chan, "digits/1F");
08690          else
08691             res = say_and_wait(chan, vms->oldmessages, chan->language);
08692          if (!res)
08693             res = ast_play_and_wait(chan, "vm-Old");
08694          if (!res) {
08695             if (vms->oldmessages == 1)
08696                res = ast_play_and_wait(chan, "vm-message");
08697             else
08698                res = ast_play_and_wait(chan, "vm-messages");
08699          }
08700       }
08701       if (!res) {
08702          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08703             res = ast_play_and_wait(chan, "vm-no");
08704             if (!res)
08705                res = ast_play_and_wait(chan, "vm-messages");
08706          }
08707       }
08708    }
08709    return res;
08710 }
08711 
08712 /* SPANISH syntax */
08713 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08714 {
08715    /* Introduce messages they have */
08716    int res;
08717    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08718       res = ast_play_and_wait(chan, "vm-youhaveno");
08719       if (!res)
08720          res = ast_play_and_wait(chan, "vm-messages");
08721    } else {
08722       res = ast_play_and_wait(chan, "vm-youhave");
08723    }
08724    if (!res) {
08725       if (vms->newmessages) {
08726          if (!res) {
08727             if ((vms->newmessages == 1)) {
08728                res = ast_play_and_wait(chan, "digits/1M");
08729                if (!res)
08730                   res = ast_play_and_wait(chan, "vm-message");
08731                if (!res)
08732                   res = ast_play_and_wait(chan, "vm-INBOXs");
08733             } else {
08734                res = say_and_wait(chan, vms->newmessages, chan->language);
08735                if (!res)
08736                   res = ast_play_and_wait(chan, "vm-messages");
08737                if (!res)
08738                   res = ast_play_and_wait(chan, "vm-INBOX");
08739             }
08740          }
08741          if (vms->oldmessages && !res)
08742             res = ast_play_and_wait(chan, "vm-and");
08743       }
08744       if (vms->oldmessages) {
08745          if (!res) {
08746             if (vms->oldmessages == 1) {
08747                res = ast_play_and_wait(chan, "digits/1M");
08748                if (!res)
08749                   res = ast_play_and_wait(chan, "vm-message");
08750                if (!res)
08751                   res = ast_play_and_wait(chan, "vm-Olds");
08752             } else {
08753                res = say_and_wait(chan, vms->oldmessages, chan->language);
08754                if (!res)
08755                   res = ast_play_and_wait(chan, "vm-messages");
08756                if (!res)
08757                   res = ast_play_and_wait(chan, "vm-Old");
08758             }
08759          }
08760       }
08761    }
08762 return res;
08763 }
08764 
08765 /* BRAZILIAN PORTUGUESE syntax */
08766 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08767    /* Introduce messages they have */
08768    int res;
08769    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08770       res = ast_play_and_wait(chan, "vm-nomessages");
08771       return res;
08772    } else {
08773       res = ast_play_and_wait(chan, "vm-youhave");
08774    }
08775    if (vms->newmessages) {
08776       if (!res)
08777          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08778       if ((vms->newmessages == 1)) {
08779          if (!res)
08780             res = ast_play_and_wait(chan, "vm-message");
08781          if (!res)
08782             res = ast_play_and_wait(chan, "vm-INBOXs");
08783       } else {
08784          if (!res)
08785             res = ast_play_and_wait(chan, "vm-messages");
08786          if (!res)
08787             res = ast_play_and_wait(chan, "vm-INBOX");
08788       }
08789       if (vms->oldmessages && !res)
08790          res = ast_play_and_wait(chan, "vm-and");
08791    }
08792    if (vms->oldmessages) {
08793       if (!res)
08794          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08795       if (vms->oldmessages == 1) {
08796          if (!res)
08797             res = ast_play_and_wait(chan, "vm-message");
08798          if (!res)
08799             res = ast_play_and_wait(chan, "vm-Olds");
08800       } else {
08801          if (!res)
08802             res = ast_play_and_wait(chan, "vm-messages");
08803          if (!res)
08804             res = ast_play_and_wait(chan, "vm-Old");
08805       }
08806    }
08807    return res;
08808 }
08809 
08810 /* FRENCH syntax */
08811 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08812 {
08813    /* Introduce messages they have */
08814    int res;
08815    res = ast_play_and_wait(chan, "vm-youhave");
08816    if (!res) {
08817       if (vms->newmessages) {
08818          res = say_and_wait(chan, vms->newmessages, chan->language);
08819          if (!res)
08820             res = ast_play_and_wait(chan, "vm-INBOX");
08821          if (vms->oldmessages && !res)
08822             res = ast_play_and_wait(chan, "vm-and");
08823          else if (!res) {
08824             if ((vms->newmessages == 1))
08825                res = ast_play_and_wait(chan, "vm-message");
08826             else
08827                res = ast_play_and_wait(chan, "vm-messages");
08828          }
08829             
08830       }
08831       if (!res && vms->oldmessages) {
08832          res = say_and_wait(chan, vms->oldmessages, chan->language);
08833          if (!res)
08834             res = ast_play_and_wait(chan, "vm-Old");
08835          if (!res) {
08836             if (vms->oldmessages == 1)
08837                res = ast_play_and_wait(chan, "vm-message");
08838             else
08839                res = ast_play_and_wait(chan, "vm-messages");
08840          }
08841       }
08842       if (!res) {
08843          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08844             res = ast_play_and_wait(chan, "vm-no");
08845             if (!res)
08846                res = ast_play_and_wait(chan, "vm-messages");
08847          }
08848       }
08849    }
08850    return res;
08851 }
08852 
08853 /* DUTCH syntax */
08854 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08855 {
08856    /* Introduce messages they have */
08857    int res;
08858    res = ast_play_and_wait(chan, "vm-youhave");
08859    if (!res) {
08860       if (vms->newmessages) {
08861          res = say_and_wait(chan, vms->newmessages, chan->language);
08862          if (!res) {
08863             if (vms->newmessages == 1)
08864                res = ast_play_and_wait(chan, "vm-INBOXs");
08865             else
08866                res = ast_play_and_wait(chan, "vm-INBOX");
08867          }
08868          if (vms->oldmessages && !res)
08869             res = ast_play_and_wait(chan, "vm-and");
08870          else if (!res) {
08871             if ((vms->newmessages == 1))
08872                res = ast_play_and_wait(chan, "vm-message");
08873             else
08874                res = ast_play_and_wait(chan, "vm-messages");
08875          }
08876             
08877       }
08878       if (!res && vms->oldmessages) {
08879          res = say_and_wait(chan, vms->oldmessages, chan->language);
08880          if (!res) {
08881             if (vms->oldmessages == 1)
08882                res = ast_play_and_wait(chan, "vm-Olds");
08883             else
08884                res = ast_play_and_wait(chan, "vm-Old");
08885          }
08886          if (!res) {
08887             if (vms->oldmessages == 1)
08888                res = ast_play_and_wait(chan, "vm-message");
08889             else
08890                res = ast_play_and_wait(chan, "vm-messages");
08891          }
08892       }
08893       if (!res) {
08894          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08895             res = ast_play_and_wait(chan, "vm-no");
08896             if (!res)
08897                res = ast_play_and_wait(chan, "vm-messages");
08898          }
08899       }
08900    }
08901    return res;
08902 }
08903 
08904 /* PORTUGUESE syntax */
08905 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08906 {
08907    /* Introduce messages they have */
08908    int res;
08909    res = ast_play_and_wait(chan, "vm-youhave");
08910    if (!res) {
08911       if (vms->newmessages) {
08912          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08913          if (!res) {
08914             if ((vms->newmessages == 1)) {
08915                res = ast_play_and_wait(chan, "vm-message");
08916                if (!res)
08917                   res = ast_play_and_wait(chan, "vm-INBOXs");
08918             } else {
08919                res = ast_play_and_wait(chan, "vm-messages");
08920                if (!res)
08921                   res = ast_play_and_wait(chan, "vm-INBOX");
08922             }
08923          }
08924          if (vms->oldmessages && !res)
08925             res = ast_play_and_wait(chan, "vm-and");
08926       }
08927       if (!res && vms->oldmessages) {
08928          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08929          if (!res) {
08930             if (vms->oldmessages == 1) {
08931                res = ast_play_and_wait(chan, "vm-message");
08932                if (!res)
08933                   res = ast_play_and_wait(chan, "vm-Olds");
08934             } else {
08935                res = ast_play_and_wait(chan, "vm-messages");
08936                if (!res)
08937                   res = ast_play_and_wait(chan, "vm-Old");
08938             }
08939          }
08940       }
08941       if (!res) {
08942          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08943             res = ast_play_and_wait(chan, "vm-no");
08944             if (!res)
08945                res = ast_play_and_wait(chan, "vm-messages");
08946          }
08947       }
08948    }
08949    return res;
08950 }
08951 
08952 
08953 /* CZECH syntax */
08954 /* in czech there must be declension of word new and message
08955  * czech        : english        : czech      : english
08956  * --------------------------------------------------------
08957  * vm-youhave   : you have 
08958  * vm-novou     : one new        : vm-zpravu  : message
08959  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08960  * vm-novych    : 5-infinite new : vm-zprav   : messages
08961  * vm-starou   : one old
08962  * vm-stare     : 2-4 old 
08963  * vm-starych   : 5-infinite old
08964  * jednu        : one   - falling 4. 
08965  * vm-no        : no  ( no messages )
08966  */
08967 
08968 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08969 {
08970    int res;
08971    res = ast_play_and_wait(chan, "vm-youhave");
08972    if (!res) {
08973       if (vms->newmessages) {
08974          if (vms->newmessages == 1) {
08975             res = ast_play_and_wait(chan, "digits/jednu");
08976          } else {
08977             res = say_and_wait(chan, vms->newmessages, chan->language);
08978          }
08979          if (!res) {
08980             if ((vms->newmessages == 1))
08981                res = ast_play_and_wait(chan, "vm-novou");
08982             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08983                res = ast_play_and_wait(chan, "vm-nove");
08984             if (vms->newmessages > 4)
08985                res = ast_play_and_wait(chan, "vm-novych");
08986          }
08987          if (vms->oldmessages && !res)
08988             res = ast_play_and_wait(chan, "vm-and");
08989          else if (!res) {
08990             if ((vms->newmessages == 1))
08991                res = ast_play_and_wait(chan, "vm-zpravu");
08992             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08993                res = ast_play_and_wait(chan, "vm-zpravy");
08994             if (vms->newmessages > 4)
08995                res = ast_play_and_wait(chan, "vm-zprav");
08996          }
08997       }
08998       if (!res && vms->oldmessages) {
08999          res = say_and_wait(chan, vms->oldmessages, chan->language);
09000          if (!res) {
09001             if ((vms->oldmessages == 1))
09002                res = ast_play_and_wait(chan, "vm-starou");
09003             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09004                res = ast_play_and_wait(chan, "vm-stare");
09005             if (vms->oldmessages > 4)
09006                res = ast_play_and_wait(chan, "vm-starych");
09007          }
09008          if (!res) {
09009             if ((vms->oldmessages == 1))
09010                res = ast_play_and_wait(chan, "vm-zpravu");
09011             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09012                res = ast_play_and_wait(chan, "vm-zpravy");
09013             if (vms->oldmessages > 4)
09014                res = ast_play_and_wait(chan, "vm-zprav");
09015          }
09016       }
09017       if (!res) {
09018          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09019             res = ast_play_and_wait(chan, "vm-no");
09020             if (!res)
09021                res = ast_play_and_wait(chan, "vm-zpravy");
09022          }
09023       }
09024    }
09025    return res;
09026 }
09027 
09028 /* CHINESE (Taiwan) syntax */
09029 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09030 {
09031    int res;
09032    /* Introduce messages they have */
09033    res = ast_play_and_wait(chan, "vm-you");
09034 
09035    if (!res && vms->newmessages) {
09036       res = ast_play_and_wait(chan, "vm-have");
09037       if (!res)
09038          res = say_and_wait(chan, vms->newmessages, chan->language);
09039       if (!res)
09040          res = ast_play_and_wait(chan, "vm-tong");
09041       if (!res)
09042          res = ast_play_and_wait(chan, "vm-INBOX");
09043       if (vms->oldmessages && !res)
09044          res = ast_play_and_wait(chan, "vm-and");
09045       else if (!res) 
09046          res = ast_play_and_wait(chan, "vm-messages");
09047    }
09048    if (!res && vms->oldmessages) {
09049       res = ast_play_and_wait(chan, "vm-have");
09050       if (!res)
09051          res = say_and_wait(chan, vms->oldmessages, chan->language);
09052       if (!res)
09053          res = ast_play_and_wait(chan, "vm-tong");
09054       if (!res)
09055          res = ast_play_and_wait(chan, "vm-Old");
09056       if (!res)
09057          res = ast_play_and_wait(chan, "vm-messages");
09058    }
09059    if (!res && !vms->oldmessages && !vms->newmessages) {
09060       res = ast_play_and_wait(chan, "vm-haveno");
09061       if (!res)
09062          res = ast_play_and_wait(chan, "vm-messages");
09063    }
09064    return res;
09065 }
09066 
09067 /* Vietnamese syntax */
09068 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09069 {
09070    int res;
09071 
09072    /* Introduce messages they have */
09073    res = ast_play_and_wait(chan, "vm-youhave");
09074    if (!res) {
09075       if (vms->newmessages) {
09076          res = say_and_wait(chan, vms->newmessages, chan->language);
09077          if (!res)
09078             res = ast_play_and_wait(chan, "vm-INBOX");
09079          if (vms->oldmessages && !res)
09080             res = ast_play_and_wait(chan, "vm-and");
09081       }
09082       if (!res && vms->oldmessages) {
09083          res = say_and_wait(chan, vms->oldmessages, chan->language);
09084          if (!res)
09085             res = ast_play_and_wait(chan, "vm-Old");        
09086       }
09087       if (!res) {
09088          if (!vms->oldmessages && !vms->newmessages) {
09089             res = ast_play_and_wait(chan, "vm-no");
09090             if (!res)
09091                res = ast_play_and_wait(chan, "vm-message");
09092          }
09093       }
09094    }
09095    return res;
09096 }
09097 
09098 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09099 {
09100    char prefile[256];
09101    
09102    /* Notify the user that the temp greeting is set and give them the option to remove it */
09103    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09104    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09105       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09106       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09107          ast_play_and_wait(chan, "vm-tempgreetactive");
09108       }
09109       DISPOSE(prefile, -1);
09110    }
09111 
09112    /* Play voicemail intro - syntax is different for different languages */
09113    if (0) {
09114       return 0;
09115    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09116       return vm_intro_cs(chan, vms);
09117    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09118       static int deprecation_warning = 0;
09119       if (deprecation_warning++ % 10 == 0) {
09120          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09121       }
09122       return vm_intro_cs(chan, vms);
09123    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09124       return vm_intro_de(chan, vms);
09125    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09126       return vm_intro_es(chan, vms);
09127    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09128       return vm_intro_fr(chan, vms);
09129    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09130       return vm_intro_gr(chan, vms);
09131    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09132       return vm_intro_he(chan, vms);
09133    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09134       return vm_intro_it(chan, vms);
09135    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09136       return vm_intro_nl(chan, vms);
09137    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09138       return vm_intro_no(chan, vms);
09139    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09140       return vm_intro_pl(chan, vms);
09141    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09142       return vm_intro_pt_BR(chan, vms);
09143    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09144       return vm_intro_pt(chan, vms);
09145    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09146       return vm_intro_multilang(chan, vms, "n");
09147    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09148       return vm_intro_se(chan, vms);
09149    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09150       return vm_intro_multilang(chan, vms, "n");
09151    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09152       return vm_intro_vi(chan, vms);
09153    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09154       return vm_intro_zh(chan, vms);
09155    } else {                                             /* Default to ENGLISH */
09156       return vm_intro_en(chan, vms);
09157    }
09158 }
09159 
09160 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09161 {
09162    int res = 0;
09163    /* Play instructions and wait for new command */
09164    while (!res) {
09165       if (vms->starting) {
09166          if (vms->lastmsg > -1) {
09167             if (skipadvanced)
09168                res = ast_play_and_wait(chan, "vm-onefor-full");
09169             else
09170                res = ast_play_and_wait(chan, "vm-onefor");
09171             if (!res)
09172                res = vm_play_folder_name(chan, vms->vmbox);
09173          }
09174          if (!res) {
09175             if (skipadvanced)
09176                res = ast_play_and_wait(chan, "vm-opts-full");
09177             else
09178                res = ast_play_and_wait(chan, "vm-opts");
09179          }
09180       } else {
09181          /* Added for additional help */
09182          if (skipadvanced) {
09183             res = ast_play_and_wait(chan, "vm-onefor-full");
09184             if (!res)
09185                res = vm_play_folder_name(chan, vms->vmbox);
09186             res = ast_play_and_wait(chan, "vm-opts-full");
09187          }
09188          /* Logic:
09189           * If the current message is not the first OR
09190           * if we're listening to the first new message and there are
09191           * also urgent messages, then prompt for navigation to the
09192           * previous message
09193           */
09194          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09195             res = ast_play_and_wait(chan, "vm-prev");
09196          }
09197          if (!res && !skipadvanced)
09198             res = ast_play_and_wait(chan, "vm-advopts");
09199          if (!res)
09200             res = ast_play_and_wait(chan, "vm-repeat");
09201          /* Logic:
09202           * If we're not listening to the last message OR
09203           * we're listening to the last urgent message and there are
09204           * also new non-urgent messages, then prompt for navigation
09205           * to the next message
09206           */
09207          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09208             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09209             res = ast_play_and_wait(chan, "vm-next");
09210          }
09211          if (!res) {
09212             int curmsg_deleted;
09213 #ifdef IMAP_STORAGE
09214             ast_mutex_lock(&vms->lock);
09215 #endif
09216             curmsg_deleted = vms->deleted[vms->curmsg];
09217 #ifdef IMAP_STORAGE
09218             ast_mutex_unlock(&vms->lock);
09219 #endif
09220             if (!curmsg_deleted) {
09221                res = ast_play_and_wait(chan, "vm-delete");
09222             } else {
09223                res = ast_play_and_wait(chan, "vm-undelete");
09224             }
09225             if (!res) {
09226                res = ast_play_and_wait(chan, "vm-toforward");
09227             }
09228             if (!res) {
09229                res = ast_play_and_wait(chan, "vm-savemessage");
09230             }
09231          }
09232       }
09233       if (!res) {
09234          res = ast_play_and_wait(chan, "vm-helpexit");
09235       }
09236       if (!res)
09237          res = ast_waitfordigit(chan, 6000);
09238       if (!res) {
09239          vms->repeats++;
09240          if (vms->repeats > 2) {
09241             res = 't';
09242          }
09243       }
09244    }
09245    return res;
09246 }
09247 
09248 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09249 {
09250    int res = 0;
09251    /* Play instructions and wait for new command */
09252    while (!res) {
09253       if (vms->lastmsg > -1) {
09254          res = ast_play_and_wait(chan, "vm-listen");
09255          if (!res)
09256             res = vm_play_folder_name(chan, vms->vmbox);
09257          if (!res)
09258             res = ast_play_and_wait(chan, "press");
09259          if (!res)
09260             res = ast_play_and_wait(chan, "digits/1");
09261       }
09262       if (!res)
09263          res = ast_play_and_wait(chan, "vm-opts");
09264       if (!res) {
09265          vms->starting = 0;
09266          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09267       }
09268    }
09269    return res;
09270 }
09271 
09272 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09273 {
09274    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09275       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09276    } else {             /* Default to ENGLISH */
09277       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09278    }
09279 }
09280 
09281 
09282 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09283 {
09284    int cmd = 0;
09285    int duration = 0;
09286    int tries = 0;
09287    char newpassword[80] = "";
09288    char newpassword2[80] = "";
09289    char prefile[PATH_MAX] = "";
09290    unsigned char buf[256];
09291    int bytes = 0;
09292 
09293    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09294    if (ast_adsi_available(chan)) {
09295       bytes += adsi_logo(buf + bytes);
09296       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09297       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09298       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09299       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09300       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09301    }
09302 
09303    /* If forcename is set, have the user record their name */
09304    if (ast_test_flag(vmu, VM_FORCENAME)) {
09305       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09306       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09307          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09308          if (cmd < 0 || cmd == 't' || cmd == '#')
09309             return cmd;
09310       }
09311    }
09312 
09313    /* If forcegreetings is set, have the user record their greetings */
09314    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09315       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09316       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09317          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09318          if (cmd < 0 || cmd == 't' || cmd == '#')
09319             return cmd;
09320       }
09321 
09322       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09323       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09324          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09325          if (cmd < 0 || cmd == 't' || cmd == '#')
09326             return cmd;
09327       }
09328    }
09329 
09330    /*
09331     * Change the password last since new users will be able to skip over any steps this one comes before
09332     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09333     */
09334    for (;;) {
09335       newpassword[1] = '\0';
09336       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09337       if (cmd == '#')
09338          newpassword[0] = '\0';
09339       if (cmd < 0 || cmd == 't' || cmd == '#')
09340          return cmd;
09341       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09342       if (cmd < 0 || cmd == 't' || cmd == '#')
09343          return cmd;
09344       cmd = check_password(vmu, newpassword); /* perform password validation */
09345       if (cmd != 0) {
09346          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09347          cmd = ast_play_and_wait(chan, vm_invalid_password);
09348       } else {
09349          newpassword2[1] = '\0';
09350          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09351          if (cmd == '#')
09352             newpassword2[0] = '\0';
09353          if (cmd < 0 || cmd == 't' || cmd == '#')
09354             return cmd;
09355          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09356          if (cmd < 0 || cmd == 't' || cmd == '#')
09357             return cmd;
09358          if (!strcmp(newpassword, newpassword2))
09359             break;
09360          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09361          cmd = ast_play_and_wait(chan, vm_mismatch);
09362       }
09363       if (++tries == 3)
09364          return -1;
09365       if (cmd != 0) {
09366          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09367       }
09368    }
09369    if (pwdchange & PWDCHANGE_INTERNAL)
09370       vm_change_password(vmu, newpassword);
09371    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09372       vm_change_password_shell(vmu, newpassword);
09373 
09374    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09375    cmd = ast_play_and_wait(chan, vm_passchanged);
09376 
09377    return cmd;
09378 }
09379 
09380 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09381 {
09382    int cmd = 0;
09383    int retries = 0;
09384    int duration = 0;
09385    char newpassword[80] = "";
09386    char newpassword2[80] = "";
09387    char prefile[PATH_MAX] = "";
09388    unsigned char buf[256];
09389    int bytes = 0;
09390 
09391    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09392    if (ast_adsi_available(chan)) {
09393       bytes += adsi_logo(buf + bytes);
09394       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09395       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09396       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09397       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09398       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09399    }
09400    while ((cmd >= 0) && (cmd != 't')) {
09401       if (cmd)
09402          retries = 0;
09403       switch (cmd) {
09404       case '1': /* Record your unavailable message */
09405          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09406          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09407          break;
09408       case '2':  /* Record your busy message */
09409          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09410          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09411          break;
09412       case '3': /* Record greeting */
09413          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09414          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09415          break;
09416       case '4':  /* manage the temporary greeting */
09417          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09418          break;
09419       case '5': /* change password */
09420          if (vmu->password[0] == '-') {
09421             cmd = ast_play_and_wait(chan, "vm-no");
09422             break;
09423          }
09424          newpassword[1] = '\0';
09425          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09426          if (cmd == '#')
09427             newpassword[0] = '\0';
09428          else {
09429             if (cmd < 0)
09430                break;
09431             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09432                break;
09433             }
09434          }
09435          cmd = check_password(vmu, newpassword); /* perform password validation */
09436          if (cmd != 0) {
09437             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09438             cmd = ast_play_and_wait(chan, vm_invalid_password);
09439             if (!cmd) {
09440                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09441             }
09442             break;
09443          }
09444          newpassword2[1] = '\0';
09445          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09446          if (cmd == '#')
09447             newpassword2[0] = '\0';
09448          else {
09449             if (cmd < 0)
09450                break;
09451 
09452             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09453                break;
09454             }
09455          }
09456          if (strcmp(newpassword, newpassword2)) {
09457             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09458             cmd = ast_play_and_wait(chan, vm_mismatch);
09459             if (!cmd) {
09460                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09461             }
09462             break;
09463          }
09464 
09465          if (pwdchange & PWDCHANGE_INTERNAL) {
09466             vm_change_password(vmu, newpassword);
09467          }
09468          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09469             vm_change_password_shell(vmu, newpassword);
09470          }
09471 
09472          ast_debug(1, "User %s set password to %s of length %d\n",
09473             vms->username, newpassword, (int) strlen(newpassword));
09474          cmd = ast_play_and_wait(chan, vm_passchanged);
09475          break;
09476       case '*': 
09477          cmd = 't';
09478          break;
09479       default: 
09480          cmd = 0;
09481          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09482          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09483          if (ast_fileexists(prefile, NULL, NULL)) {
09484             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09485          }
09486          DISPOSE(prefile, -1);
09487          if (!cmd) {
09488             cmd = ast_play_and_wait(chan, "vm-options");
09489          }
09490          if (!cmd) {
09491             cmd = ast_waitfordigit(chan, 6000);
09492          }
09493          if (!cmd) {
09494             retries++;
09495          }
09496          if (retries > 3) {
09497             cmd = 't';
09498          }
09499          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09500       }
09501    }
09502    if (cmd == 't')
09503       cmd = 0;
09504    return cmd;
09505 }
09506 
09507 /*!
09508  * \brief The handler for 'record a temporary greeting'. 
09509  * \param chan
09510  * \param vmu
09511  * \param vms
09512  * \param fmtc
09513  * \param record_gain
09514  *
09515  * This is option 4 from the mailbox options menu.
09516  * This function manages the following promptings:
09517  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09518  * 2: remove (delete) the temporary greeting.
09519  * *: return to the main menu.
09520  *
09521  * \return zero on success, -1 on error.
09522  */
09523 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09524 {
09525    int cmd = 0;
09526    int retries = 0;
09527    int duration = 0;
09528    char prefile[PATH_MAX] = "";
09529    unsigned char buf[256];
09530    int bytes = 0;
09531 
09532    if (ast_adsi_available(chan)) {
09533       bytes += adsi_logo(buf + bytes);
09534       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09535       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09536       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09537       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09538       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09539    }
09540 
09541    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09542    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09543    while ((cmd >= 0) && (cmd != 't')) {
09544       if (cmd)
09545          retries = 0;
09546       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09547       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09548          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09549          if (cmd == -1) {
09550             break;
09551          }
09552          cmd = 't';  
09553       } else {
09554          switch (cmd) {
09555          case '1':
09556             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09557             break;
09558          case '2':
09559             DELETE(prefile, -1, prefile, vmu);
09560             ast_play_and_wait(chan, "vm-tempremoved");
09561             cmd = 't';  
09562             break;
09563          case '*': 
09564             cmd = 't';
09565             break;
09566          default:
09567             cmd = ast_play_and_wait(chan,
09568                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09569                   "vm-tempgreeting2" : "vm-tempgreeting");
09570             if (!cmd) {
09571                cmd = ast_waitfordigit(chan, 6000);
09572             }
09573             if (!cmd) {
09574                retries++;
09575             }
09576             if (retries > 3) {
09577                cmd = 't';
09578             }
09579             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09580          }
09581       }
09582       DISPOSE(prefile, -1);
09583    }
09584    if (cmd == 't')
09585       cmd = 0;
09586    return cmd;
09587 }
09588 
09589 /*!
09590  * \brief Greek syntax for 'You have N messages' greeting.
09591  * \param chan
09592  * \param vms
09593  * \param vmu
09594  *
09595  * \return zero on success, -1 on error.
09596  */   
09597 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09598 {
09599    int cmd = 0;
09600 
09601    if (vms->lastmsg > -1) {
09602       cmd = play_message(chan, vmu, vms);
09603    } else {
09604       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09605       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09606          if (!cmd) {
09607             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09608             cmd = ast_play_and_wait(chan, vms->fn);
09609          }
09610          if (!cmd)
09611             cmd = ast_play_and_wait(chan, "vm-messages");
09612       } else {
09613          if (!cmd)
09614             cmd = ast_play_and_wait(chan, "vm-messages");
09615          if (!cmd) {
09616             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09617             cmd = ast_play_and_wait(chan, vms->fn);
09618          }
09619       }
09620    } 
09621    return cmd;
09622 }
09623 
09624 /* Hebrew Syntax */
09625 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09626 {
09627    int cmd = 0;
09628 
09629    if (vms->lastmsg > -1) {
09630       cmd = play_message(chan, vmu, vms);
09631    } else {
09632       if (!strcasecmp(vms->fn, "INBOX")) {
09633          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09634       } else {
09635          cmd = ast_play_and_wait(chan, "vm-nomessages");
09636       }
09637    }
09638    return cmd;
09639 }
09640 
09641 /*! 
09642  * \brief Default English syntax for 'You have N messages' greeting.
09643  * \param chan
09644  * \param vms
09645  * \param vmu
09646  *
09647  * \return zero on success, -1 on error.
09648  */
09649 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09650 {
09651    int cmd = 0;
09652 
09653    if (vms->lastmsg > -1) {
09654       cmd = play_message(chan, vmu, vms);
09655    } else {
09656       cmd = ast_play_and_wait(chan, "vm-youhave");
09657       if (!cmd) 
09658          cmd = ast_play_and_wait(chan, "vm-no");
09659       if (!cmd) {
09660          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09661          cmd = ast_play_and_wait(chan, vms->fn);
09662       }
09663       if (!cmd)
09664          cmd = ast_play_and_wait(chan, "vm-messages");
09665    }
09666    return cmd;
09667 }
09668 
09669 /*! 
09670  *\brief Italian syntax for 'You have N messages' greeting.
09671  * \param chan
09672  * \param vms
09673  * \param vmu
09674  *
09675  * \return zero on success, -1 on error.
09676  */
09677 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09678 {
09679    int cmd;
09680 
09681    if (vms->lastmsg > -1) {
09682       cmd = play_message(chan, vmu, vms);
09683    } else {
09684       cmd = ast_play_and_wait(chan, "vm-no");
09685       if (!cmd)
09686          cmd = ast_play_and_wait(chan, "vm-message");
09687       if (!cmd) {
09688          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09689          cmd = ast_play_and_wait(chan, vms->fn);
09690       }
09691    }
09692    return cmd;
09693 }
09694 
09695 /*! 
09696  * \brief Spanish syntax for 'You have N messages' greeting.
09697  * \param chan
09698  * \param vms
09699  * \param vmu
09700  *
09701  * \return zero on success, -1 on error.
09702  */
09703 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09704 {
09705    int cmd;
09706 
09707    if (vms->lastmsg > -1) {
09708       cmd = play_message(chan, vmu, vms);
09709    } else {
09710       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09711       if (!cmd)
09712          cmd = ast_play_and_wait(chan, "vm-messages");
09713       if (!cmd) {
09714          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09715          cmd = ast_play_and_wait(chan, vms->fn);
09716       }
09717    }
09718    return cmd;
09719 }
09720 
09721 /*! 
09722  * \brief Portuguese syntax for 'You have N messages' greeting.
09723  * \param chan
09724  * \param vms
09725  * \param vmu
09726  *
09727  * \return zero on success, -1 on error.
09728  */
09729 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09730 {
09731    int cmd;
09732 
09733    if (vms->lastmsg > -1) {
09734       cmd = play_message(chan, vmu, vms);
09735    } else {
09736       cmd = ast_play_and_wait(chan, "vm-no");
09737       if (!cmd) {
09738          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09739          cmd = ast_play_and_wait(chan, vms->fn);
09740       }
09741       if (!cmd)
09742          cmd = ast_play_and_wait(chan, "vm-messages");
09743    }
09744    return cmd;
09745 }
09746 
09747 /*! 
09748  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09749  * \param chan
09750  * \param vms
09751  * \param vmu
09752  *
09753  * \return zero on success, -1 on error.
09754  */
09755 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09756 {
09757    int cmd;
09758 
09759    if (vms->lastmsg > -1) {
09760       cmd = play_message(chan, vmu, vms);
09761    } else {
09762       cmd = ast_play_and_wait(chan, "vm-you");
09763       if (!cmd) 
09764          cmd = ast_play_and_wait(chan, "vm-haveno");
09765       if (!cmd)
09766          cmd = ast_play_and_wait(chan, "vm-messages");
09767       if (!cmd) {
09768          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09769          cmd = ast_play_and_wait(chan, vms->fn);
09770       }
09771    }
09772    return cmd;
09773 }
09774 
09775 /*! 
09776  * \brief Vietnamese syntax for 'You have N messages' greeting.
09777  * \param chan
09778  * \param vms
09779  * \param vmu
09780  *
09781  * \return zero on success, -1 on error.
09782  */
09783 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09784 {
09785    int cmd = 0;
09786 
09787    if (vms->lastmsg > -1) {
09788       cmd = play_message(chan, vmu, vms);
09789    } else {
09790       cmd = ast_play_and_wait(chan, "vm-no");
09791       if (!cmd) {
09792          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09793          cmd = ast_play_and_wait(chan, vms->fn);
09794       }
09795    }
09796    return cmd;
09797 }
09798 
09799 /*!
09800  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09801  * \param chan The channel for the current user. We read the language property from this.
09802  * \param vms passed into the language-specific vm_browse_messages function.
09803  * \param vmu passed into the language-specific vm_browse_messages function.
09804  * 
09805  * The method to be invoked is determined by the value of language code property in the user's channel.
09806  * The default (when unable to match) is to use english.
09807  *
09808  * \return zero on success, -1 on error.
09809  */
09810 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09811 {
09812    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09813       return vm_browse_messages_es(chan, vms, vmu);
09814    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09815       return vm_browse_messages_gr(chan, vms, vmu);
09816    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09817       return vm_browse_messages_he(chan, vms, vmu);
09818    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09819       return vm_browse_messages_it(chan, vms, vmu);
09820    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09821       return vm_browse_messages_pt(chan, vms, vmu);
09822    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09823       return vm_browse_messages_vi(chan, vms, vmu);
09824    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09825       return vm_browse_messages_zh(chan, vms, vmu);
09826    } else {                                             /* Default to English syntax */
09827       return vm_browse_messages_en(chan, vms, vmu);
09828    }
09829 }
09830 
09831 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09832          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09833          int skipuser, int max_logins, int silent)
09834 {
09835    int useadsi = 0, valid = 0, logretries = 0;
09836    char password[AST_MAX_EXTENSION]="", *passptr;
09837    struct ast_vm_user vmus, *vmu = NULL;
09838 
09839    /* If ADSI is supported, setup login screen */
09840    adsi_begin(chan, &useadsi);
09841    if (!skipuser && useadsi)
09842       adsi_login(chan);
09843    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09844       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09845       return -1;
09846    }
09847 
09848    /* Authenticate them and get their mailbox/password */
09849 
09850    while (!valid && (logretries < max_logins)) {
09851       /* Prompt for, and read in the username */
09852       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09853          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09854          return -1;
09855       }
09856       if (ast_strlen_zero(mailbox)) {
09857          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09858             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09859          } else {
09860             ast_verb(3, "Username not entered\n"); 
09861             return -1;
09862          }
09863       } else if (mailbox[0] == '*') {
09864          /* user entered '*' */
09865          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09866          if (ast_exists_extension(chan, chan->context, "a", 1,
09867             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09868             return -1;
09869          }
09870          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09871          mailbox[0] = '\0';
09872       }
09873 
09874       if (useadsi)
09875          adsi_password(chan);
09876 
09877       if (!ast_strlen_zero(prefix)) {
09878          char fullusername[80] = "";
09879          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09880          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09881          ast_copy_string(mailbox, fullusername, mailbox_size);
09882       }
09883 
09884       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09885       vmu = find_user(&vmus, context, mailbox);
09886       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09887          /* saved password is blank, so don't bother asking */
09888          password[0] = '\0';
09889       } else {
09890          if (ast_streamfile(chan, vm_password, chan->language)) {
09891             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09892             return -1;
09893          }
09894          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09895             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09896             return -1;
09897          } else if (password[0] == '*') {
09898             /* user entered '*' */
09899             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09900             if (ast_exists_extension(chan, chan->context, "a", 1,
09901                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09902                mailbox[0] = '*';
09903                return -1;
09904             }
09905             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09906             mailbox[0] = '\0';
09907             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09908             vmu = NULL;
09909          }
09910       }
09911 
09912       if (vmu) {
09913          passptr = vmu->password;
09914          if (passptr[0] == '-') passptr++;
09915       }
09916       if (vmu && !strcmp(passptr, password))
09917          valid++;
09918       else {
09919          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09920          if (!ast_strlen_zero(prefix))
09921             mailbox[0] = '\0';
09922       }
09923       logretries++;
09924       if (!valid) {
09925          if (skipuser || logretries >= max_logins) {
09926             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09927                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09928                return -1;
09929             }
09930          } else {
09931             if (useadsi)
09932                adsi_login(chan);
09933             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09934                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09935                return -1;
09936             }
09937          }
09938          if (ast_waitstream(chan, "")) /* Channel is hung up */
09939             return -1;
09940       }
09941    }
09942    if (!valid && (logretries >= max_logins)) {
09943       ast_stopstream(chan);
09944       ast_play_and_wait(chan, "vm-goodbye");
09945       return -1;
09946    }
09947    if (vmu && !skipuser) {
09948       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09949    }
09950    return 0;
09951 }
09952 
09953 static int vm_execmain(struct ast_channel *chan, const char *data)
09954 {
09955    /* XXX This is, admittedly, some pretty horrendous code.  For some
09956       reason it just seemed a lot easier to do with GOTO's.  I feel
09957       like I'm back in my GWBASIC days. XXX */
09958    int res = -1;
09959    int cmd = 0;
09960    int valid = 0;
09961    char prefixstr[80] ="";
09962    char ext_context[256]="";
09963    int box;
09964    int useadsi = 0;
09965    int skipuser = 0;
09966    struct vm_state vms;
09967    struct ast_vm_user *vmu = NULL, vmus;
09968    char *context = NULL;
09969    int silentexit = 0;
09970    struct ast_flags flags = { 0 };
09971    signed char record_gain = 0;
09972    int play_auto = 0;
09973    int play_folder = 0;
09974    int in_urgent = 0;
09975 #ifdef IMAP_STORAGE
09976    int deleted = 0;
09977 #endif
09978 
09979    /* Add the vm_state to the active list and keep it active */
09980    memset(&vms, 0, sizeof(vms));
09981 
09982    vms.lastmsg = -1;
09983 
09984    memset(&vmus, 0, sizeof(vmus));
09985 
09986    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09987    if (chan->_state != AST_STATE_UP) {
09988       ast_debug(1, "Before ast_answer\n");
09989       ast_answer(chan);
09990    }
09991 
09992    if (!ast_strlen_zero(data)) {
09993       char *opts[OPT_ARG_ARRAY_SIZE];
09994       char *parse;
09995       AST_DECLARE_APP_ARGS(args,
09996          AST_APP_ARG(argv0);
09997          AST_APP_ARG(argv1);
09998       );
09999 
10000       parse = ast_strdupa(data);
10001 
10002       AST_STANDARD_APP_ARGS(args, parse);
10003 
10004       if (args.argc == 2) {
10005          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10006             return -1;
10007          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10008             int gain;
10009             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
10010                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10011                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10012                   return -1;
10013                } else {
10014                   record_gain = (signed char) gain;
10015                }
10016             } else {
10017                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
10018             }
10019          }
10020          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
10021             play_auto = 1;
10022             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
10023                /* See if it is a folder name first */
10024                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
10025                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
10026                      play_folder = -1;
10027                   }
10028                } else {
10029                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10030                }
10031             } else {
10032                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10033             }
10034             if (play_folder > 9 || play_folder < 0) {
10035                ast_log(AST_LOG_WARNING,
10036                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10037                   opts[OPT_ARG_PLAYFOLDER]);
10038                play_folder = 0;
10039             }
10040          }
10041       } else {
10042          /* old style options parsing */
10043          while (*(args.argv0)) {
10044             if (*(args.argv0) == 's')
10045                ast_set_flag(&flags, OPT_SILENT);
10046             else if (*(args.argv0) == 'p')
10047                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10048             else 
10049                break;
10050             (args.argv0)++;
10051          }
10052 
10053       }
10054 
10055       valid = ast_test_flag(&flags, OPT_SILENT);
10056 
10057       if ((context = strchr(args.argv0, '@')))
10058          *context++ = '\0';
10059 
10060       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10061          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10062       else
10063          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10064 
10065       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10066          skipuser++;
10067       else
10068          valid = 0;
10069    }
10070 
10071    if (!valid)
10072       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10073 
10074    ast_debug(1, "After vm_authenticate\n");
10075 
10076    if (vms.username[0] == '*') {
10077       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10078 
10079       /* user entered '*' */
10080       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10081          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10082          res = 0; /* prevent hangup */
10083          goto out;
10084       }
10085    }
10086 
10087    if (!res) {
10088       valid = 1;
10089       if (!skipuser)
10090          vmu = &vmus;
10091    } else {
10092       res = 0;
10093    }
10094 
10095    /* If ADSI is supported, setup login screen */
10096    adsi_begin(chan, &useadsi);
10097 
10098    ast_test_suite_assert(valid);
10099    if (!valid) {
10100       goto out;
10101    }
10102    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10103 
10104 #ifdef IMAP_STORAGE
10105    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10106    pthread_setspecific(ts_vmstate.key, &vms);
10107 
10108    vms.interactive = 1;
10109    vms.updated = 1;
10110    if (vmu)
10111       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10112    vmstate_insert(&vms);
10113    init_vm_state(&vms);
10114 #endif
10115    
10116    /* Set language from config to override channel language */
10117    if (!ast_strlen_zero(vmu->language))
10118       ast_string_field_set(chan, language, vmu->language);
10119 
10120    /* Retrieve urgent, old and new message counts */
10121    ast_debug(1, "Before open_mailbox\n");
10122    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10123    if (res < 0)
10124       goto out;
10125    vms.oldmessages = vms.lastmsg + 1;
10126    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10127    /* check INBOX */
10128    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10129    if (res < 0)
10130       goto out;
10131    vms.newmessages = vms.lastmsg + 1;
10132    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10133    /* Start in Urgent */
10134    in_urgent = 1;
10135    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10136    if (res < 0)
10137       goto out;
10138    vms.urgentmessages = vms.lastmsg + 1;
10139    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10140 
10141    /* Select proper mailbox FIRST!! */
10142    if (play_auto) {
10143       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10144       if (vms.urgentmessages) {
10145          in_urgent = 1;
10146          res = open_mailbox(&vms, vmu, 11);
10147       } else {
10148          in_urgent = 0;
10149          res = open_mailbox(&vms, vmu, play_folder);
10150       }
10151       if (res < 0)
10152          goto out;
10153 
10154       /* If there are no new messages, inform the user and hangup */
10155       if (vms.lastmsg == -1) {
10156          in_urgent = 0;
10157          cmd = vm_browse_messages(chan, &vms, vmu);
10158          res = 0;
10159          goto out;
10160       }
10161    } else {
10162       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10163          /* If we only have old messages start here */
10164          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10165          in_urgent = 0;
10166          play_folder = 1;
10167          if (res < 0)
10168             goto out;
10169       } else if (!vms.urgentmessages && vms.newmessages) {
10170          /* If we have new messages but none are urgent */
10171          in_urgent = 0;
10172          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10173          if (res < 0)
10174             goto out;
10175       }
10176    }
10177 
10178    if (useadsi)
10179       adsi_status(chan, &vms);
10180    res = 0;
10181 
10182    /* Check to see if this is a new user */
10183    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10184       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10185       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10186          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10187       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10188       if ((cmd == 't') || (cmd == '#')) {
10189          /* Timeout */
10190          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10191          res = 0;
10192          goto out;
10193       } else if (cmd < 0) {
10194          /* Hangup */
10195          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10196          res = -1;
10197          goto out;
10198       }
10199    }
10200 #ifdef IMAP_STORAGE
10201       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10202       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10203          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10204          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10205       }
10206       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10207       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10208          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10209          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10210       }
10211 #endif
10212 
10213    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10214    if (play_auto) {
10215       cmd = '1';
10216    } else {
10217       cmd = vm_intro(chan, vmu, &vms);
10218    }
10219    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10220 
10221    vms.repeats = 0;
10222    vms.starting = 1;
10223    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10224       /* Run main menu */
10225       switch (cmd) {
10226       case '1': /* First message */
10227          vms.curmsg = 0;
10228          /* Fall through */
10229       case '5': /* Play current message */
10230          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10231          cmd = vm_browse_messages(chan, &vms, vmu);
10232          break;
10233       case '2': /* Change folders */
10234          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10235          if (useadsi)
10236             adsi_folders(chan, 0, "Change to folder...");
10237 
10238          cmd = get_folder2(chan, "vm-changeto", 0);
10239          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10240          if (cmd == '#') {
10241             cmd = 0;
10242          } else if (cmd > 0) {
10243             cmd = cmd - '0';
10244             res = close_mailbox(&vms, vmu);
10245             if (res == ERROR_LOCK_PATH)
10246                goto out;
10247             /* If folder is not urgent, set in_urgent to zero! */
10248             if (cmd != 11) in_urgent = 0;
10249             res = open_mailbox(&vms, vmu, cmd);
10250             if (res < 0)
10251                goto out;
10252             play_folder = cmd;
10253             cmd = 0;
10254          }
10255          if (useadsi)
10256             adsi_status2(chan, &vms);
10257 
10258          if (!cmd) {
10259             cmd = vm_play_folder_name(chan, vms.vmbox);
10260          }
10261 
10262          vms.starting = 1;
10263          vms.curmsg = 0;
10264          break;
10265       case '3': /* Advanced options */
10266          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10267          cmd = 0;
10268          vms.repeats = 0;
10269          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10270             switch (cmd) {
10271             case '1': /* Reply */
10272                if (vms.lastmsg > -1 && !vms.starting) {
10273                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10274                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10275                      res = cmd;
10276                      goto out;
10277                   }
10278                } else {
10279                   cmd = ast_play_and_wait(chan, "vm-sorry");
10280                }
10281                cmd = 't';
10282                break;
10283             case '2': /* Callback */
10284                if (!vms.starting)
10285                   ast_verb(3, "Callback Requested\n");
10286                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10287                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10288                   if (cmd == 9) {
10289                      silentexit = 1;
10290                      goto out;
10291                   } else if (cmd == ERROR_LOCK_PATH) {
10292                      res = cmd;
10293                      goto out;
10294                   }
10295                } else {
10296                   cmd = ast_play_and_wait(chan, "vm-sorry");
10297                }
10298                cmd = 't';
10299                break;
10300             case '3': /* Envelope */
10301                if (vms.lastmsg > -1 && !vms.starting) {
10302                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10303                   if (cmd == ERROR_LOCK_PATH) {
10304                      res = cmd;
10305                      goto out;
10306                   }
10307                } else {
10308                   cmd = ast_play_and_wait(chan, "vm-sorry");
10309                }
10310                cmd = 't';
10311                break;
10312             case '4': /* Dialout */
10313                if (!ast_strlen_zero(vmu->dialout)) {
10314                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10315                   if (cmd == 9) {
10316                      silentexit = 1;
10317                      goto out;
10318                   }
10319                } else {
10320                   cmd = ast_play_and_wait(chan, "vm-sorry");
10321                }
10322                cmd = 't';
10323                break;
10324 
10325             case '5': /* Leave VoiceMail */
10326                if (ast_test_flag(vmu, VM_SVMAIL)) {
10327                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10328                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10329                      res = cmd;
10330                      goto out;
10331                   }
10332                } else {
10333                   cmd = ast_play_and_wait(chan, "vm-sorry");
10334                }
10335                cmd = 't';
10336                break;
10337 
10338             case '*': /* Return to main menu */
10339                cmd = 't';
10340                break;
10341 
10342             default:
10343                cmd = 0;
10344                if (!vms.starting) {
10345                   cmd = ast_play_and_wait(chan, "vm-toreply");
10346                }
10347                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10348                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10349                }
10350                if (!cmd && !vms.starting) {
10351                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10352                }
10353                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10354                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10355                }
10356                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10357                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10358                }
10359                if (!cmd) {
10360                   cmd = ast_play_and_wait(chan, "vm-starmain");
10361                }
10362                if (!cmd) {
10363                   cmd = ast_waitfordigit(chan, 6000);
10364                }
10365                if (!cmd) {
10366                   vms.repeats++;
10367                }
10368                if (vms.repeats > 3) {
10369                   cmd = 't';
10370                }
10371                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10372             }
10373          }
10374          if (cmd == 't') {
10375             cmd = 0;
10376             vms.repeats = 0;
10377          }
10378          break;
10379       case '4': /* Go to the previous message */
10380          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10381          if (vms.curmsg > 0) {
10382             vms.curmsg--;
10383             cmd = play_message(chan, vmu, &vms);
10384          } else {
10385             /* Check if we were listening to new
10386                messages.  If so, go to Urgent messages
10387                instead of saying "no more messages"
10388             */
10389             if (in_urgent == 0 && vms.urgentmessages > 0) {
10390                /* Check for Urgent messages */
10391                in_urgent = 1;
10392                res = close_mailbox(&vms, vmu);
10393                if (res == ERROR_LOCK_PATH)
10394                   goto out;
10395                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10396                if (res < 0)
10397                   goto out;
10398                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10399                vms.curmsg = vms.lastmsg;
10400                if (vms.lastmsg < 0) {
10401                   cmd = ast_play_and_wait(chan, "vm-nomore");
10402                }
10403             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10404                vms.curmsg = vms.lastmsg;
10405                cmd = play_message(chan, vmu, &vms);
10406             } else {
10407                cmd = ast_play_and_wait(chan, "vm-nomore");
10408             }
10409          }
10410          break;
10411       case '6': /* Go to the next message */
10412          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10413          if (vms.curmsg < vms.lastmsg) {
10414             vms.curmsg++;
10415             cmd = play_message(chan, vmu, &vms);
10416          } else {
10417             if (in_urgent && vms.newmessages > 0) {
10418                /* Check if we were listening to urgent
10419                 * messages.  If so, go to regular new messages
10420                 * instead of saying "no more messages"
10421                 */
10422                in_urgent = 0;
10423                res = close_mailbox(&vms, vmu);
10424                if (res == ERROR_LOCK_PATH)
10425                   goto out;
10426                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10427                if (res < 0)
10428                   goto out;
10429                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10430                vms.curmsg = -1;
10431                if (vms.lastmsg < 0) {
10432                   cmd = ast_play_and_wait(chan, "vm-nomore");
10433                }
10434             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10435                vms.curmsg = 0;
10436                cmd = play_message(chan, vmu, &vms);
10437             } else {
10438                cmd = ast_play_and_wait(chan, "vm-nomore");
10439             }
10440          }
10441          break;
10442       case '7': /* Delete the current message */
10443          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10444             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10445             if (useadsi)
10446                adsi_delete(chan, &vms);
10447             if (vms.deleted[vms.curmsg]) {
10448                if (play_folder == 0) {
10449                   if (in_urgent) {
10450                      vms.urgentmessages--;
10451                   } else {
10452                      vms.newmessages--;
10453                   }
10454                }
10455                else if (play_folder == 1)
10456                   vms.oldmessages--;
10457                cmd = ast_play_and_wait(chan, "vm-deleted");
10458             } else {
10459                if (play_folder == 0) {
10460                   if (in_urgent) {
10461                      vms.urgentmessages++;
10462                   } else {
10463                      vms.newmessages++;
10464                   }
10465                }
10466                else if (play_folder == 1)
10467                   vms.oldmessages++;
10468                cmd = ast_play_and_wait(chan, "vm-undeleted");
10469             }
10470             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10471                if (vms.curmsg < vms.lastmsg) {
10472                   vms.curmsg++;
10473                   cmd = play_message(chan, vmu, &vms);
10474                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10475                   vms.curmsg = 0;
10476                   cmd = play_message(chan, vmu, &vms);
10477                } else {
10478                   /* Check if we were listening to urgent
10479                      messages.  If so, go to regular new messages
10480                      instead of saying "no more messages"
10481                   */
10482                   if (in_urgent == 1) {
10483                      /* Check for new messages */
10484                      in_urgent = 0;
10485                      res = close_mailbox(&vms, vmu);
10486                      if (res == ERROR_LOCK_PATH)
10487                         goto out;
10488                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10489                      if (res < 0)
10490                         goto out;
10491                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10492                      vms.curmsg = -1;
10493                      if (vms.lastmsg < 0) {
10494                         cmd = ast_play_and_wait(chan, "vm-nomore");
10495                      }
10496                   } else {
10497                      cmd = ast_play_and_wait(chan, "vm-nomore");
10498                   }
10499                }
10500             }
10501          } else /* Delete not valid if we haven't selected a message */
10502             cmd = 0;
10503 #ifdef IMAP_STORAGE
10504          deleted = 1;
10505 #endif
10506          break;
10507    
10508       case '8': /* Forward the current message */
10509          if (vms.lastmsg > -1) {
10510             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10511             if (cmd == ERROR_LOCK_PATH) {
10512                res = cmd;
10513                goto out;
10514             }
10515          } else {
10516             /* Check if we were listening to urgent
10517                messages.  If so, go to regular new messages
10518                instead of saying "no more messages"
10519             */
10520             if (in_urgent == 1 && vms.newmessages > 0) {
10521                /* Check for new messages */
10522                in_urgent = 0;
10523                res = close_mailbox(&vms, vmu);
10524                if (res == ERROR_LOCK_PATH)
10525                   goto out;
10526                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10527                if (res < 0)
10528                   goto out;
10529                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10530                vms.curmsg = -1;
10531                if (vms.lastmsg < 0) {
10532                   cmd = ast_play_and_wait(chan, "vm-nomore");
10533                }
10534             } else {
10535                cmd = ast_play_and_wait(chan, "vm-nomore");
10536             }
10537          }
10538          break;
10539       case '9': /* Save message to folder */
10540          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10541          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10542             /* No message selected */
10543             cmd = 0;
10544             break;
10545          }
10546          if (useadsi)
10547             adsi_folders(chan, 1, "Save to folder...");
10548          cmd = get_folder2(chan, "vm-savefolder", 1);
10549          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10550          box = 0; /* Shut up compiler */
10551          if (cmd == '#') {
10552             cmd = 0;
10553             break;
10554          } else if (cmd > 0) {
10555             box = cmd = cmd - '0';
10556             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10557             if (cmd == ERROR_LOCK_PATH) {
10558                res = cmd;
10559                goto out;
10560 #ifndef IMAP_STORAGE
10561             } else if (!cmd) {
10562                vms.deleted[vms.curmsg] = 1;
10563 #endif
10564             } else {
10565                vms.deleted[vms.curmsg] = 0;
10566                vms.heard[vms.curmsg] = 0;
10567             }
10568          }
10569          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10570          if (useadsi)
10571             adsi_message(chan, &vms);
10572          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10573          if (!cmd) {
10574             cmd = ast_play_and_wait(chan, "vm-message");
10575             if (!cmd) 
10576                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10577             if (!cmd)
10578                cmd = ast_play_and_wait(chan, "vm-savedto");
10579             if (!cmd)
10580                cmd = vm_play_folder_name(chan, vms.fn);
10581          } else {
10582             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10583          }
10584          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10585             if (vms.curmsg < vms.lastmsg) {
10586                vms.curmsg++;
10587                cmd = play_message(chan, vmu, &vms);
10588             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10589                vms.curmsg = 0;
10590                cmd = play_message(chan, vmu, &vms);
10591             } else {
10592                /* Check if we were listening to urgent
10593                   messages.  If so, go to regular new messages
10594                   instead of saying "no more messages"
10595                */
10596                if (in_urgent == 1 && vms.newmessages > 0) {
10597                   /* Check for new messages */
10598                   in_urgent = 0;
10599                   res = close_mailbox(&vms, vmu);
10600                   if (res == ERROR_LOCK_PATH)
10601                      goto out;
10602                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10603                   if (res < 0)
10604                      goto out;
10605                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10606                   vms.curmsg = -1;
10607                   if (vms.lastmsg < 0) {
10608                      cmd = ast_play_and_wait(chan, "vm-nomore");
10609                   }
10610                } else {
10611                   cmd = ast_play_and_wait(chan, "vm-nomore");
10612                }
10613             }
10614          }
10615          break;
10616       case '*': /* Help */
10617          if (!vms.starting) {
10618             cmd = ast_play_and_wait(chan, "vm-onefor");
10619             if (!strncasecmp(chan->language, "he", 2)) {
10620                cmd = ast_play_and_wait(chan, "vm-for");
10621             }
10622             if (!cmd)
10623                cmd = vm_play_folder_name(chan, vms.vmbox);
10624             if (!cmd)
10625                cmd = ast_play_and_wait(chan, "vm-opts");
10626             if (!cmd)
10627                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10628          } else
10629             cmd = 0;
10630          break;
10631       case '0': /* Mailbox options */
10632          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10633          if (useadsi)
10634             adsi_status(chan, &vms);
10635          break;
10636       default: /* Nothing */
10637          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10638          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10639          break;
10640       }
10641    }
10642    if ((cmd == 't') || (cmd == '#')) {
10643       /* Timeout */
10644       res = 0;
10645    } else {
10646       /* Hangup */
10647       res = -1;
10648    }
10649 
10650 out:
10651    if (res > -1) {
10652       ast_stopstream(chan);
10653       adsi_goodbye(chan);
10654       if (valid && res != OPERATOR_EXIT) {
10655          if (silentexit)
10656             res = ast_play_and_wait(chan, "vm-dialout");
10657          else 
10658             res = ast_play_and_wait(chan, "vm-goodbye");
10659       }
10660       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10661          res = 0;
10662       }
10663       if (useadsi)
10664          ast_adsi_unload_session(chan);
10665    }
10666    if (vmu)
10667       close_mailbox(&vms, vmu);
10668    if (valid) {
10669       int new = 0, old = 0, urgent = 0;
10670       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10671       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10672       /* Urgent flag not passwd to externnotify here */
10673       run_externnotify(vmu->context, vmu->mailbox, NULL);
10674       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10675       queue_mwi_event(ext_context, urgent, new, old);
10676    }
10677 #ifdef IMAP_STORAGE
10678    /* expunge message - use UID Expunge if supported on IMAP server*/
10679    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10680    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10681       ast_mutex_lock(&vms.lock);
10682 #ifdef HAVE_IMAP_TK2006
10683       if (LEVELUIDPLUS (vms.mailstream)) {
10684          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10685       } else 
10686 #endif
10687          mail_expunge(vms.mailstream);
10688       ast_mutex_unlock(&vms.lock);
10689    }
10690    /*  before we delete the state, we should copy pertinent info
10691     *  back to the persistent model */
10692    if (vmu) {
10693       vmstate_delete(&vms);
10694    }
10695 #endif
10696    if (vmu)
10697       free_user(vmu);
10698 
10699 #ifdef IMAP_STORAGE
10700    pthread_setspecific(ts_vmstate.key, NULL);
10701 #endif
10702    return res;
10703 }
10704 
10705 static int vm_exec(struct ast_channel *chan, const char *data)
10706 {
10707    int res = 0;
10708    char *tmp;
10709    struct leave_vm_options leave_options;
10710    struct ast_flags flags = { 0 };
10711    char *opts[OPT_ARG_ARRAY_SIZE];
10712    AST_DECLARE_APP_ARGS(args,
10713       AST_APP_ARG(argv0);
10714       AST_APP_ARG(argv1);
10715    );
10716    
10717    memset(&leave_options, 0, sizeof(leave_options));
10718 
10719    if (chan->_state != AST_STATE_UP)
10720       ast_answer(chan);
10721 
10722    if (!ast_strlen_zero(data)) {
10723       tmp = ast_strdupa(data);
10724       AST_STANDARD_APP_ARGS(args, tmp);
10725       if (args.argc == 2) {
10726          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10727             return -1;
10728          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10729          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10730             int gain;
10731 
10732             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10733                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10734                return -1;
10735             } else {
10736                leave_options.record_gain = (signed char) gain;
10737             }
10738          }
10739          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10740             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10741                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10742          }
10743       }
10744    } else {
10745       char temp[256];
10746       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10747       if (res < 0)
10748          return res;
10749       if (ast_strlen_zero(temp))
10750          return 0;
10751       args.argv0 = ast_strdupa(temp);
10752    }
10753 
10754    res = leave_voicemail(chan, args.argv0, &leave_options);
10755    if (res == 't') {
10756       ast_play_and_wait(chan, "vm-goodbye");
10757       res = 0;
10758    }
10759 
10760    if (res == OPERATOR_EXIT) {
10761       res = 0;
10762    }
10763 
10764    if (res == ERROR_LOCK_PATH) {
10765       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10766       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10767       res = 0;
10768    }
10769 
10770    return res;
10771 }
10772 
10773 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10774 {
10775    struct ast_vm_user *vmu;
10776 
10777    if (!ast_strlen_zero(box) && box[0] == '*') {
10778       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10779             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10780             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10781             "\n\tand will be ignored.\n", box, context);
10782       return NULL;
10783    }
10784 
10785    AST_LIST_TRAVERSE(&users, vmu, list) {
10786       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10787          if (strcasecmp(vmu->context, context)) {
10788             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10789                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10790                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10791                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10792          }
10793          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10794          return NULL;
10795       }
10796       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10797          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10798          return NULL;
10799       }
10800    }
10801    
10802    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10803       return NULL;
10804    
10805    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10806    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10807 
10808    AST_LIST_INSERT_TAIL(&users, vmu, list);
10809    
10810    return vmu;
10811 }
10812 
10813 static int append_mailbox(const char *context, const char *box, const char *data)
10814 {
10815    /* Assumes lock is already held */
10816    char *tmp;
10817    char *stringp;
10818    char *s;
10819    struct ast_vm_user *vmu;
10820    char *mailbox_full;
10821    int new = 0, old = 0, urgent = 0;
10822    char secretfn[PATH_MAX] = "";
10823 
10824    tmp = ast_strdupa(data);
10825 
10826    if (!(vmu = find_or_create(context, box)))
10827       return -1;
10828 
10829    populate_defaults(vmu);
10830 
10831    stringp = tmp;
10832    if ((s = strsep(&stringp, ","))) {
10833       if (!ast_strlen_zero(s) && s[0] == '*') {
10834          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10835             "\n\tmust be reset in voicemail.conf.\n", box);
10836       }
10837       /* assign password regardless of validity to prevent NULL password from being assigned */
10838       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10839    }
10840    if (stringp && (s = strsep(&stringp, ","))) {
10841       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10842    }
10843    if (stringp && (s = strsep(&stringp, ","))) {
10844       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10845    }
10846    if (stringp && (s = strsep(&stringp, ","))) {
10847       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10848    }
10849    if (stringp && (s = strsep(&stringp, ","))) {
10850       apply_options(vmu, s);
10851    }
10852 
10853    switch (vmu->passwordlocation) {
10854    case OPT_PWLOC_SPOOLDIR:
10855       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10856       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10857    }
10858 
10859    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10860    strcpy(mailbox_full, box);
10861    strcat(mailbox_full, "@");
10862    strcat(mailbox_full, context);
10863 
10864    inboxcount2(mailbox_full, &urgent, &new, &old);
10865    queue_mwi_event(mailbox_full, urgent, new, old);
10866 
10867    return 0;
10868 }
10869 
10870 AST_TEST_DEFINE(test_voicemail_vmuser)
10871 {
10872    int res = 0;
10873    struct ast_vm_user *vmu;
10874    /* language parameter seems to only be used for display in manager action */
10875    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10876       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10877       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10878       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10879       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10880       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10881       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10882       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10883       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10884 #ifdef IMAP_STORAGE
10885    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10886       "imapfolder=INBOX|imapvmshareid=6000";
10887 #endif
10888 
10889    switch (cmd) {
10890    case TEST_INIT:
10891       info->name = "vmuser";
10892       info->category = "/apps/app_voicemail/";
10893       info->summary = "Vmuser unit test";
10894       info->description =
10895          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10896       return AST_TEST_NOT_RUN;
10897    case TEST_EXECUTE:
10898       break;
10899    }
10900 
10901    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10902       return AST_TEST_NOT_RUN;
10903    }
10904    populate_defaults(vmu);
10905    ast_set_flag(vmu, VM_ALLOCED);
10906 
10907    apply_options(vmu, options_string);
10908 
10909    if (!ast_test_flag(vmu, VM_ATTACH)) {
10910       ast_test_status_update(test, "Parse failure for attach option\n");
10911       res = 1;
10912    }
10913    if (strcasecmp(vmu->attachfmt, "wav49")) {
10914       ast_test_status_update(test, "Parse failure for attachftm option\n");
10915       res = 1;
10916    }
10917    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10918       ast_test_status_update(test, "Parse failure for serveremail option\n");
10919       res = 1;
10920    }
10921    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10922       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10923       res = 1;
10924    }
10925    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10926       ast_test_status_update(test, "Parse failure for emailbody option\n");
10927       res = 1;
10928    }
10929    if (strcasecmp(vmu->zonetag, "central")) {
10930       ast_test_status_update(test, "Parse failure for tz option\n");
10931       res = 1;
10932    }
10933    if (!ast_test_flag(vmu, VM_DELETE)) {
10934       ast_test_status_update(test, "Parse failure for delete option\n");
10935       res = 1;
10936    }
10937    if (!ast_test_flag(vmu, VM_SAYCID)) {
10938       ast_test_status_update(test, "Parse failure for saycid option\n");
10939       res = 1;
10940    }
10941    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10942       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10943       res = 1;
10944    }
10945    if (!ast_test_flag(vmu, VM_REVIEW)) {
10946       ast_test_status_update(test, "Parse failure for review option\n");
10947       res = 1;
10948    }
10949    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10950       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10951       res = 1;
10952    }
10953    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10954       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10955       res = 1;
10956    }
10957    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10958       ast_test_status_update(test, "Parse failure for operator option\n");
10959       res = 1;
10960    }
10961    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10962       ast_test_status_update(test, "Parse failure for envelope option\n");
10963       res = 1;
10964    }
10965    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10966       ast_test_status_update(test, "Parse failure for moveheard option\n");
10967       res = 1;
10968    }
10969    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10970       ast_test_status_update(test, "Parse failure for sayduration option\n");
10971       res = 1;
10972    }
10973    if (vmu->saydurationm != 5) {
10974       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10975       res = 1;
10976    }
10977    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10978       ast_test_status_update(test, "Parse failure for forcename option\n");
10979       res = 1;
10980    }
10981    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10982       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10983       res = 1;
10984    }
10985    if (strcasecmp(vmu->callback, "somecontext")) {
10986       ast_test_status_update(test, "Parse failure for callbacks option\n");
10987       res = 1;
10988    }
10989    if (strcasecmp(vmu->dialout, "somecontext2")) {
10990       ast_test_status_update(test, "Parse failure for dialout option\n");
10991       res = 1;
10992    }
10993    if (strcasecmp(vmu->exit, "somecontext3")) {
10994       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10995       res = 1;
10996    }
10997    if (vmu->minsecs != 10) {
10998       ast_test_status_update(test, "Parse failure for minsecs option\n");
10999       res = 1;
11000    }
11001    if (vmu->maxsecs != 100) {
11002       ast_test_status_update(test, "Parse failure for maxsecs option\n");
11003       res = 1;
11004    }
11005    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
11006       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
11007       res = 1;
11008    }
11009    if (vmu->maxdeletedmsg != 50) {
11010       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
11011       res = 1;
11012    }
11013    if (vmu->volgain != 1.3) {
11014       ast_test_status_update(test, "Parse failure for volgain option\n");
11015       res = 1;
11016    }
11017    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
11018       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
11019       res = 1;
11020    }
11021 #ifdef IMAP_STORAGE
11022    apply_options(vmu, option_string2);
11023 
11024    if (strcasecmp(vmu->imapuser, "imapuser")) {
11025       ast_test_status_update(test, "Parse failure for imapuser option\n");
11026       res = 1;
11027    }
11028    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11029       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11030       res = 1;
11031    }
11032    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11033       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11034       res = 1;
11035    }
11036    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11037       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11038       res = 1;
11039    }
11040 #endif
11041 
11042    free_user(vmu);
11043    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11044 }
11045 
11046 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11047 {
11048    struct ast_vm_user svm;
11049    char *context, *box;
11050    AST_DECLARE_APP_ARGS(args,
11051       AST_APP_ARG(mbox);
11052       AST_APP_ARG(options);
11053    );
11054    static int dep_warning = 0;
11055 
11056    if (ast_strlen_zero(data)) {
11057       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11058       return -1;
11059    }
11060 
11061    if (!dep_warning) {
11062       dep_warning = 1;
11063       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11064    }
11065 
11066    box = ast_strdupa(data);
11067 
11068    AST_STANDARD_APP_ARGS(args, box);
11069 
11070    if (args.options) {
11071    }
11072 
11073    if ((context = strchr(args.mbox, '@'))) {
11074       *context = '\0';
11075       context++;
11076    }
11077 
11078    if (find_user(&svm, context, args.mbox)) {
11079       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11080    } else
11081       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11082 
11083    return 0;
11084 }
11085 
11086 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11087 {
11088    struct ast_vm_user svm;
11089    AST_DECLARE_APP_ARGS(arg,
11090       AST_APP_ARG(mbox);
11091       AST_APP_ARG(context);
11092    );
11093 
11094    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11095 
11096    if (ast_strlen_zero(arg.mbox)) {
11097       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11098       return -1;
11099    }
11100 
11101    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11102    return 0;
11103 }
11104 
11105 static struct ast_custom_function mailbox_exists_acf = {
11106    .name = "MAILBOX_EXISTS",
11107    .read = acf_mailbox_exists,
11108 };
11109 
11110 static int vmauthenticate(struct ast_channel *chan, const char *data)
11111 {
11112    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11113    struct ast_vm_user vmus;
11114    char *options = NULL;
11115    int silent = 0, skipuser = 0;
11116    int res = -1;
11117    
11118    if (data) {
11119       s = ast_strdupa(data);
11120       user = strsep(&s, ",");
11121       options = strsep(&s, ",");
11122       if (user) {
11123          s = user;
11124          user = strsep(&s, "@");
11125          context = strsep(&s, "");
11126          if (!ast_strlen_zero(user))
11127             skipuser++;
11128          ast_copy_string(mailbox, user, sizeof(mailbox));
11129       }
11130    }
11131 
11132    if (options) {
11133       silent = (strchr(options, 's')) != NULL;
11134    }
11135 
11136    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11137       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11138       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11139       ast_play_and_wait(chan, "auth-thankyou");
11140       res = 0;
11141    } else if (mailbox[0] == '*') {
11142       /* user entered '*' */
11143       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11144          res = 0; /* prevent hangup */
11145       }
11146    }
11147 
11148    return res;
11149 }
11150 
11151 static char *show_users_realtime(int fd, const char *context)
11152 {
11153    struct ast_config *cfg;
11154    const char *cat = NULL;
11155 
11156    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11157       "context", context, SENTINEL))) {
11158       return CLI_FAILURE;
11159    }
11160 
11161    ast_cli(fd,
11162       "\n"
11163       "=============================================================\n"
11164       "=== Configured Voicemail Users ==============================\n"
11165       "=============================================================\n"
11166       "===\n");
11167 
11168    while ((cat = ast_category_browse(cfg, cat))) {
11169       struct ast_variable *var = NULL;
11170       ast_cli(fd,
11171          "=== Mailbox ...\n"
11172          "===\n");
11173       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11174          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11175       ast_cli(fd,
11176          "===\n"
11177          "=== ---------------------------------------------------------\n"
11178          "===\n");
11179    }
11180 
11181    ast_cli(fd,
11182       "=============================================================\n"
11183       "\n");
11184 
11185    ast_config_destroy(cfg);
11186 
11187    return CLI_SUCCESS;
11188 }
11189 
11190 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11191 {
11192    int which = 0;
11193    int wordlen;
11194    struct ast_vm_user *vmu;
11195    const char *context = "";
11196 
11197    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11198    if (pos > 4)
11199       return NULL;
11200    if (pos == 3)
11201       return (state == 0) ? ast_strdup("for") : NULL;
11202    wordlen = strlen(word);
11203    AST_LIST_TRAVERSE(&users, vmu, list) {
11204       if (!strncasecmp(word, vmu->context, wordlen)) {
11205          if (context && strcmp(context, vmu->context) && ++which > state)
11206             return ast_strdup(vmu->context);
11207          /* ignore repeated contexts ? */
11208          context = vmu->context;
11209       }
11210    }
11211    return NULL;
11212 }
11213 
11214 /*! \brief Show a list of voicemail users in the CLI */
11215 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11216 {
11217    struct ast_vm_user *vmu;
11218 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11219    const char *context = NULL;
11220    int users_counter = 0;
11221 
11222    switch (cmd) {
11223    case CLI_INIT:
11224       e->command = "voicemail show users";
11225       e->usage =
11226          "Usage: voicemail show users [for <context>]\n"
11227          "       Lists all mailboxes currently set up\n";
11228       return NULL;
11229    case CLI_GENERATE:
11230       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11231    }  
11232 
11233    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11234       return CLI_SHOWUSAGE;
11235    if (a->argc == 5) {
11236       if (strcmp(a->argv[3],"for"))
11237          return CLI_SHOWUSAGE;
11238       context = a->argv[4];
11239    }
11240 
11241    if (ast_check_realtime("voicemail")) {
11242       if (!context) {
11243          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11244          return CLI_SHOWUSAGE;
11245       }
11246       return show_users_realtime(a->fd, context);
11247    }
11248 
11249    AST_LIST_LOCK(&users);
11250    if (AST_LIST_EMPTY(&users)) {
11251       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11252       AST_LIST_UNLOCK(&users);
11253       return CLI_FAILURE;
11254    }
11255    if (!context) {
11256       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11257    } else {
11258       int count = 0;
11259       AST_LIST_TRAVERSE(&users, vmu, list) {
11260          if (!strcmp(context, vmu->context)) {
11261             count++;
11262             break;
11263          }
11264       }
11265       if (count) {
11266          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11267       } else {
11268          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11269          AST_LIST_UNLOCK(&users);
11270          return CLI_FAILURE;
11271       }
11272    }
11273    AST_LIST_TRAVERSE(&users, vmu, list) {
11274       int newmsgs = 0, oldmsgs = 0;
11275       char count[12], tmp[256] = "";
11276 
11277       if (!context || !strcmp(context, vmu->context)) {
11278          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11279          inboxcount(tmp, &newmsgs, &oldmsgs);
11280          snprintf(count, sizeof(count), "%d", newmsgs);
11281          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11282          users_counter++;
11283       }
11284    }
11285    AST_LIST_UNLOCK(&users);
11286    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11287    return CLI_SUCCESS;
11288 }
11289 
11290 /*! \brief Show a list of voicemail zones in the CLI */
11291 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11292 {
11293    struct vm_zone *zone;
11294 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11295    char *res = CLI_SUCCESS;
11296 
11297    switch (cmd) {
11298    case CLI_INIT:
11299       e->command = "voicemail show zones";
11300       e->usage =
11301          "Usage: voicemail show zones\n"
11302          "       Lists zone message formats\n";
11303       return NULL;
11304    case CLI_GENERATE:
11305       return NULL;
11306    }
11307 
11308    if (a->argc != 3)
11309       return CLI_SHOWUSAGE;
11310 
11311    AST_LIST_LOCK(&zones);
11312    if (!AST_LIST_EMPTY(&zones)) {
11313       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11314       AST_LIST_TRAVERSE(&zones, zone, list) {
11315          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11316       }
11317    } else {
11318       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11319       res = CLI_FAILURE;
11320    }
11321    AST_LIST_UNLOCK(&zones);
11322 
11323    return res;
11324 }
11325 
11326 /*! \brief Reload voicemail configuration from the CLI */
11327 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11328 {
11329    switch (cmd) {
11330    case CLI_INIT:
11331       e->command = "voicemail reload";
11332       e->usage =
11333          "Usage: voicemail reload\n"
11334          "       Reload voicemail configuration\n";
11335       return NULL;
11336    case CLI_GENERATE:
11337       return NULL;
11338    }
11339 
11340    if (a->argc != 2)
11341       return CLI_SHOWUSAGE;
11342 
11343    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11344    load_config(1);
11345    
11346    return CLI_SUCCESS;
11347 }
11348 
11349 static struct ast_cli_entry cli_voicemail[] = {
11350    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11351    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11352    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11353 };
11354 
11355 #ifdef IMAP_STORAGE
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, imapuser, AST_DATA_STRING)       \
11379       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11380       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11381       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11382 #else
11383    #define DATA_EXPORT_VM_USERS(USER)              \
11384       USER(ast_vm_user, context, AST_DATA_STRING)        \
11385       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11386       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11387       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11388       USER(ast_vm_user, email, AST_DATA_STRING)       \
11389       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11390       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11391       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11392       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11393       USER(ast_vm_user, language, AST_DATA_STRING)       \
11394       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11395       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11396       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11397       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11398       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11399       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11400       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11401       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11402       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11403       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11404       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11405       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11406 #endif
11407 
11408 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11409 
11410 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11411    ZONE(vm_zone, name, AST_DATA_STRING)      \
11412    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11413    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11414 
11415 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11416 
11417 /*!
11418  * \internal
11419  * \brief Add voicemail user to the data_root.
11420  * \param[in] search The search tree.
11421  * \param[in] data_root The main result node.
11422  * \param[in] user The voicemail user.
11423  */
11424 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11425     struct ast_data *data_root, struct ast_vm_user *user)
11426 {
11427    struct ast_data *data_user, *data_zone;
11428    struct ast_data *data_state;
11429    struct vm_zone *zone = NULL;
11430    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11431    char ext_context[256] = "";
11432 
11433    data_user = ast_data_add_node(data_root, "user");
11434    if (!data_user) {
11435       return -1;
11436    }
11437 
11438    ast_data_add_structure(ast_vm_user, data_user, user);
11439 
11440    AST_LIST_LOCK(&zones);
11441    AST_LIST_TRAVERSE(&zones, zone, list) {
11442       if (!strcmp(zone->name, user->zonetag)) {
11443          break;
11444       }
11445    }
11446    AST_LIST_UNLOCK(&zones);
11447 
11448    /* state */
11449    data_state = ast_data_add_node(data_user, "state");
11450    if (!data_state) {
11451       return -1;
11452    }
11453    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11454    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11455    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11456    ast_data_add_int(data_state, "newmsg", newmsg);
11457    ast_data_add_int(data_state, "oldmsg", oldmsg);
11458 
11459    if (zone) {
11460       data_zone = ast_data_add_node(data_user, "zone");
11461       ast_data_add_structure(vm_zone, data_zone, zone);
11462    }
11463 
11464    if (!ast_data_search_match(search, data_user)) {
11465       ast_data_remove_node(data_root, data_user);
11466    }
11467 
11468    return 0;
11469 }
11470 
11471 static int vm_users_data_provider_get(const struct ast_data_search *search,
11472    struct ast_data *data_root)
11473 {
11474    struct ast_vm_user *user;
11475 
11476    AST_LIST_LOCK(&users);
11477    AST_LIST_TRAVERSE(&users, user, list) {
11478       vm_users_data_provider_get_helper(search, data_root, user);
11479    }
11480    AST_LIST_UNLOCK(&users);
11481 
11482    return 0;
11483 }
11484 
11485 static const struct ast_data_handler vm_users_data_provider = {
11486    .version = AST_DATA_HANDLER_VERSION,
11487    .get = vm_users_data_provider_get
11488 };
11489 
11490 static const struct ast_data_entry vm_data_providers[] = {
11491    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11492 };
11493 
11494 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11495 {
11496    int new = 0, old = 0, urgent = 0;
11497 
11498    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11499 
11500    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11501       mwi_sub->old_urgent = urgent;
11502       mwi_sub->old_new = new;
11503       mwi_sub->old_old = old;
11504       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11505       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11506    }
11507 }
11508 
11509 static void poll_subscribed_mailboxes(void)
11510 {
11511    struct mwi_sub *mwi_sub;
11512 
11513    AST_RWLIST_RDLOCK(&mwi_subs);
11514    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11515       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11516          poll_subscribed_mailbox(mwi_sub);
11517       }
11518    }
11519    AST_RWLIST_UNLOCK(&mwi_subs);
11520 }
11521 
11522 static void *mb_poll_thread(void *data)
11523 {
11524    while (poll_thread_run) {
11525       struct timespec ts = { 0, };
11526       struct timeval wait;
11527 
11528       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11529       ts.tv_sec = wait.tv_sec;
11530       ts.tv_nsec = wait.tv_usec * 1000;
11531 
11532       ast_mutex_lock(&poll_lock);
11533       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11534       ast_mutex_unlock(&poll_lock);
11535 
11536       if (!poll_thread_run)
11537          break;
11538 
11539       poll_subscribed_mailboxes();
11540    }
11541 
11542    return NULL;
11543 }
11544 
11545 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11546 {
11547    ast_free(mwi_sub);
11548 }
11549 
11550 static int handle_unsubscribe(void *datap)
11551 {
11552    struct mwi_sub *mwi_sub;
11553    uint32_t *uniqueid = datap;
11554    
11555    AST_RWLIST_WRLOCK(&mwi_subs);
11556    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11557       if (mwi_sub->uniqueid == *uniqueid) {
11558          AST_LIST_REMOVE_CURRENT(entry);
11559          break;
11560       }
11561    }
11562    AST_RWLIST_TRAVERSE_SAFE_END
11563    AST_RWLIST_UNLOCK(&mwi_subs);
11564 
11565    if (mwi_sub)
11566       mwi_sub_destroy(mwi_sub);
11567 
11568    ast_free(uniqueid);  
11569    return 0;
11570 }
11571 
11572 static int handle_subscribe(void *datap)
11573 {
11574    unsigned int len;
11575    struct mwi_sub *mwi_sub;
11576    struct mwi_sub_task *p = datap;
11577 
11578    len = sizeof(*mwi_sub);
11579    if (!ast_strlen_zero(p->mailbox))
11580       len += strlen(p->mailbox);
11581 
11582    if (!ast_strlen_zero(p->context))
11583       len += strlen(p->context) + 1; /* Allow for seperator */
11584 
11585    if (!(mwi_sub = ast_calloc(1, len)))
11586       return -1;
11587 
11588    mwi_sub->uniqueid = p->uniqueid;
11589    if (!ast_strlen_zero(p->mailbox))
11590       strcpy(mwi_sub->mailbox, p->mailbox);
11591 
11592    if (!ast_strlen_zero(p->context)) {
11593       strcat(mwi_sub->mailbox, "@");
11594       strcat(mwi_sub->mailbox, p->context);
11595    }
11596 
11597    AST_RWLIST_WRLOCK(&mwi_subs);
11598    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11599    AST_RWLIST_UNLOCK(&mwi_subs);
11600    ast_free((void *) p->mailbox);
11601    ast_free((void *) p->context);
11602    ast_free(p);
11603    poll_subscribed_mailbox(mwi_sub);
11604    return 0;
11605 }
11606 
11607 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11608 {
11609    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11610 
11611    if (!uniqueid) {
11612       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11613       return;
11614    }
11615 
11616    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11617       ast_free(uniqueid);
11618       return;
11619    }
11620 
11621    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11622       ast_free(uniqueid);
11623       return;
11624    }
11625 
11626    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11627    *uniqueid = u;
11628    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11629       ast_free(uniqueid);
11630    }
11631 }
11632 
11633 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11634 {
11635    struct mwi_sub_task *mwist;
11636    
11637    if (ast_event_get_type(event) != AST_EVENT_SUB)
11638       return;
11639 
11640    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11641       return;
11642 
11643    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11644       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11645       return;
11646    }
11647    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11648    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11649    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11650    
11651    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11652       ast_free(mwist);
11653    }
11654 }
11655 
11656 static void start_poll_thread(void)
11657 {
11658    int errcode;
11659    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11660       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11661       AST_EVENT_IE_END);
11662 
11663    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11664       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11665       AST_EVENT_IE_END);
11666 
11667    if (mwi_sub_sub)
11668       ast_event_report_subs(mwi_sub_sub);
11669 
11670    poll_thread_run = 1;
11671 
11672    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11673       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11674    }
11675 }
11676 
11677 static void stop_poll_thread(void)
11678 {
11679    poll_thread_run = 0;
11680 
11681    if (mwi_sub_sub) {
11682       ast_event_unsubscribe(mwi_sub_sub);
11683       mwi_sub_sub = NULL;
11684    }
11685 
11686    if (mwi_unsub_sub) {
11687       ast_event_unsubscribe(mwi_unsub_sub);
11688       mwi_unsub_sub = NULL;
11689    }
11690 
11691    ast_mutex_lock(&poll_lock);
11692    ast_cond_signal(&poll_cond);
11693    ast_mutex_unlock(&poll_lock);
11694 
11695    pthread_join(poll_thread, NULL);
11696 
11697    poll_thread = AST_PTHREADT_NULL;
11698 }
11699 
11700 /*! \brief Manager list voicemail users command */
11701 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11702 {
11703    struct ast_vm_user *vmu = NULL;
11704    const char *id = astman_get_header(m, "ActionID");
11705    char actionid[128] = "";
11706 
11707    if (!ast_strlen_zero(id))
11708       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11709 
11710    AST_LIST_LOCK(&users);
11711 
11712    if (AST_LIST_EMPTY(&users)) {
11713       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11714       AST_LIST_UNLOCK(&users);
11715       return RESULT_SUCCESS;
11716    }
11717    
11718    astman_send_ack(s, m, "Voicemail user list will follow");
11719    
11720    AST_LIST_TRAVERSE(&users, vmu, list) {
11721       char dirname[256];
11722 
11723 #ifdef IMAP_STORAGE
11724       int new, old;
11725       inboxcount(vmu->mailbox, &new, &old);
11726 #endif
11727       
11728       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11729       astman_append(s,
11730          "%s"
11731          "Event: VoicemailUserEntry\r\n"
11732          "VMContext: %s\r\n"
11733          "VoiceMailbox: %s\r\n"
11734          "Fullname: %s\r\n"
11735          "Email: %s\r\n"
11736          "Pager: %s\r\n"
11737          "ServerEmail: %s\r\n"
11738          "MailCommand: %s\r\n"
11739          "Language: %s\r\n"
11740          "TimeZone: %s\r\n"
11741          "Callback: %s\r\n"
11742          "Dialout: %s\r\n"
11743          "UniqueID: %s\r\n"
11744          "ExitContext: %s\r\n"
11745          "SayDurationMinimum: %d\r\n"
11746          "SayEnvelope: %s\r\n"
11747          "SayCID: %s\r\n"
11748          "AttachMessage: %s\r\n"
11749          "AttachmentFormat: %s\r\n"
11750          "DeleteMessage: %s\r\n"
11751          "VolumeGain: %.2f\r\n"
11752          "CanReview: %s\r\n"
11753          "CallOperator: %s\r\n"
11754          "MaxMessageCount: %d\r\n"
11755          "MaxMessageLength: %d\r\n"
11756          "NewMessageCount: %d\r\n"
11757 #ifdef IMAP_STORAGE
11758          "OldMessageCount: %d\r\n"
11759          "IMAPUser: %s\r\n"
11760 #endif
11761          "\r\n",
11762          actionid,
11763          vmu->context,
11764          vmu->mailbox,
11765          vmu->fullname,
11766          vmu->email,
11767          vmu->pager,
11768          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11769          mailcmd,
11770          vmu->language,
11771          vmu->zonetag,
11772          vmu->callback,
11773          vmu->dialout,
11774          vmu->uniqueid,
11775          vmu->exit,
11776          vmu->saydurationm,
11777          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11778          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11779          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11780          vmu->attachfmt,
11781          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11782          vmu->volgain,
11783          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11784          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11785          vmu->maxmsg,
11786          vmu->maxsecs,
11787 #ifdef IMAP_STORAGE
11788          new, old, vmu->imapuser
11789 #else
11790          count_messages(vmu, dirname)
11791 #endif
11792          );
11793    }     
11794    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11795 
11796    AST_LIST_UNLOCK(&users);
11797 
11798    return RESULT_SUCCESS;
11799 }
11800 
11801 /*! \brief Free the users structure. */
11802 static void free_vm_users(void) 
11803 {
11804    struct ast_vm_user *current;
11805    AST_LIST_LOCK(&users);
11806    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11807       ast_set_flag(current, VM_ALLOCED);
11808       free_user(current);
11809    }
11810    AST_LIST_UNLOCK(&users);
11811 }
11812 
11813 /*! \brief Free the zones structure. */
11814 static void free_vm_zones(void)
11815 {
11816    struct vm_zone *zcur;
11817    AST_LIST_LOCK(&zones);
11818    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11819       free_zone(zcur);
11820    AST_LIST_UNLOCK(&zones);
11821 }
11822 
11823 static const char *substitute_escapes(const char *value)
11824 {
11825    char *current;
11826 
11827    /* Add 16 for fudge factor */
11828    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11829 
11830    ast_str_reset(str);
11831    
11832    /* Substitute strings \r, \n, and \t into the appropriate characters */
11833    for (current = (char *) value; *current; current++) {
11834       if (*current == '\\') {
11835          current++;
11836          if (!*current) {
11837             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11838             break;
11839          }
11840          switch (*current) {
11841          case '\\':
11842             ast_str_append(&str, 0, "\\");
11843             break;
11844          case 'r':
11845             ast_str_append(&str, 0, "\r");
11846             break;
11847          case 'n':
11848 #ifdef IMAP_STORAGE
11849             if (!str->used || str->str[str->used - 1] != '\r') {
11850                ast_str_append(&str, 0, "\r");
11851             }
11852 #endif
11853             ast_str_append(&str, 0, "\n");
11854             break;
11855          case 't':
11856             ast_str_append(&str, 0, "\t");
11857             break;
11858          default:
11859             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11860             break;
11861          }
11862       } else {
11863          ast_str_append(&str, 0, "%c", *current);
11864       }
11865    }
11866 
11867    return ast_str_buffer(str);
11868 }
11869 
11870 static int load_config(int reload)
11871 {
11872    struct ast_config *cfg, *ucfg;
11873    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11874    int res;
11875 
11876    ast_unload_realtime("voicemail");
11877    ast_unload_realtime("voicemail_data");
11878 
11879    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11880       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11881          return 0;
11882       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11883          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11884          ucfg = NULL;
11885       }
11886       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11887       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11888          ast_config_destroy(ucfg);
11889          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11890          return 0;
11891       }
11892    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11893       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11894       return 0;
11895    } else {
11896       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11897       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11898          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11899          ucfg = NULL;
11900       }
11901    }
11902 
11903    res = actual_load_config(reload, cfg, ucfg);
11904 
11905    ast_config_destroy(cfg);
11906    ast_config_destroy(ucfg);
11907 
11908    return res;
11909 }
11910 
11911 #ifdef TEST_FRAMEWORK
11912 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11913 {
11914    ast_unload_realtime("voicemail");
11915    ast_unload_realtime("voicemail_data");
11916    return actual_load_config(reload, cfg, ucfg);
11917 }
11918 #endif
11919 
11920 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11921 {
11922    struct ast_vm_user *current;
11923    char *cat;
11924    struct ast_variable *var;
11925    const char *val;
11926    char *q, *stringp, *tmp;
11927    int x;
11928    unsigned int tmpadsi[4];
11929    char secretfn[PATH_MAX] = "";
11930 
11931 #ifdef IMAP_STORAGE
11932    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11933 #endif
11934    /* set audio control prompts */
11935    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11936    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11937    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11938    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11939    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11940 
11941    /* Free all the users structure */  
11942    free_vm_users();
11943 
11944    /* Free all the zones structure */
11945    free_vm_zones();
11946 
11947    AST_LIST_LOCK(&users);  
11948 
11949    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11950    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11951 
11952    if (cfg) {
11953       /* General settings */
11954 
11955       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11956          val = "default";
11957       ast_copy_string(userscontext, val, sizeof(userscontext));
11958       /* Attach voice message to mail message ? */
11959       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11960          val = "yes";
11961       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11962 
11963       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11964          val = "no";
11965       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11966 
11967       volgain = 0.0;
11968       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11969          sscanf(val, "%30lf", &volgain);
11970 
11971 #ifdef ODBC_STORAGE
11972       strcpy(odbc_database, "asterisk");
11973       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11974          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11975       }
11976       strcpy(odbc_table, "voicemessages");
11977       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11978          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11979       }
11980 #endif      
11981       /* Mail command */
11982       strcpy(mailcmd, SENDMAIL);
11983       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11984          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11985 
11986       maxsilence = 0;
11987       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11988          maxsilence = atoi(val);
11989          if (maxsilence > 0)
11990             maxsilence *= 1000;
11991       }
11992       
11993       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11994          maxmsg = MAXMSG;
11995       } else {
11996          maxmsg = atoi(val);
11997          if (maxmsg < 0) {
11998             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11999             maxmsg = MAXMSG;
12000          } else if (maxmsg > MAXMSGLIMIT) {
12001             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12002             maxmsg = MAXMSGLIMIT;
12003          }
12004       }
12005 
12006       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
12007          maxdeletedmsg = 0;
12008       } else {
12009          if (sscanf(val, "%30d", &x) == 1)
12010             maxdeletedmsg = x;
12011          else if (ast_true(val))
12012             maxdeletedmsg = MAXMSG;
12013          else
12014             maxdeletedmsg = 0;
12015 
12016          if (maxdeletedmsg < 0) {
12017             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
12018             maxdeletedmsg = MAXMSG;
12019          } else if (maxdeletedmsg > MAXMSGLIMIT) {
12020             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12021             maxdeletedmsg = MAXMSGLIMIT;
12022          }
12023       }
12024 
12025       /* Load date format config for voicemail mail */
12026       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
12027          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
12028       }
12029 
12030       /* Load date format config for voicemail pager mail */
12031       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12032          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12033       }
12034 
12035       /* External password changing command */
12036       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12037          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12038          pwdchange = PWDCHANGE_EXTERNAL;
12039       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12040          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12041          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12042       }
12043  
12044       /* External password validation command */
12045       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12046          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12047          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12048       }
12049 
12050 #ifdef IMAP_STORAGE
12051       /* IMAP server address */
12052       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12053          ast_copy_string(imapserver, val, sizeof(imapserver));
12054       } else {
12055          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12056       }
12057       /* IMAP server port */
12058       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12059          ast_copy_string(imapport, val, sizeof(imapport));
12060       } else {
12061          ast_copy_string(imapport, "143", sizeof(imapport));
12062       }
12063       /* IMAP server flags */
12064       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12065          ast_copy_string(imapflags, val, sizeof(imapflags));
12066       }
12067       /* IMAP server master username */
12068       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12069          ast_copy_string(authuser, val, sizeof(authuser));
12070       }
12071       /* IMAP server master password */
12072       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12073          ast_copy_string(authpassword, val, sizeof(authpassword));
12074       }
12075       /* Expunge on exit */
12076       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12077          if (ast_false(val))
12078             expungeonhangup = 0;
12079          else
12080             expungeonhangup = 1;
12081       } else {
12082          expungeonhangup = 1;
12083       }
12084       /* IMAP voicemail folder */
12085       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12086          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12087       } else {
12088          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12089       }
12090       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12091          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12092       }
12093       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12094          imapgreetings = ast_true(val);
12095       } else {
12096          imapgreetings = 0;
12097       }
12098       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12099          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12100       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12101          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12102          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12103       } else {
12104          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12105       }
12106 
12107       /* There is some very unorthodox casting done here. This is due
12108        * to the way c-client handles the argument passed in. It expects a 
12109        * void pointer and casts the pointer directly to a long without
12110        * first dereferencing it. */
12111       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12112          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12113       } else {
12114          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12115       }
12116 
12117       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12118          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12119       } else {
12120          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12121       }
12122 
12123       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12124          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12125       } else {
12126          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12127       }
12128 
12129       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12130          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12131       } else {
12132          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12133       }
12134 
12135       /* Increment configuration version */
12136       imapversion++;
12137 #endif
12138       /* External voicemail notify application */
12139       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12140          ast_copy_string(externnotify, val, sizeof(externnotify));
12141          ast_debug(1, "found externnotify: %s\n", externnotify);
12142       } else {
12143          externnotify[0] = '\0';
12144       }
12145 
12146       /* SMDI voicemail notification */
12147       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12148          ast_debug(1, "Enabled SMDI voicemail notification\n");
12149          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12150             smdi_iface = ast_smdi_interface_find(val);
12151          } else {
12152             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12153             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12154          }
12155          if (!smdi_iface) {
12156             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12157          } 
12158       }
12159 
12160       /* Silence treshold */
12161       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12162       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12163          silencethreshold = atoi(val);
12164       
12165       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12166          val = ASTERISK_USERNAME;
12167       ast_copy_string(serveremail, val, sizeof(serveremail));
12168       
12169       vmmaxsecs = 0;
12170       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12171          if (sscanf(val, "%30d", &x) == 1) {
12172             vmmaxsecs = x;
12173          } else {
12174             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12175          }
12176       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12177          static int maxmessage_deprecate = 0;
12178          if (maxmessage_deprecate == 0) {
12179             maxmessage_deprecate = 1;
12180             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12181          }
12182          if (sscanf(val, "%30d", &x) == 1) {
12183             vmmaxsecs = x;
12184          } else {
12185             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12186          }
12187       }
12188 
12189       vmminsecs = 0;
12190       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12191          if (sscanf(val, "%30d", &x) == 1) {
12192             vmminsecs = x;
12193             if (maxsilence / 1000 >= vmminsecs) {
12194                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12195             }
12196          } else {
12197             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12198          }
12199       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12200          static int maxmessage_deprecate = 0;
12201          if (maxmessage_deprecate == 0) {
12202             maxmessage_deprecate = 1;
12203             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12204          }
12205          if (sscanf(val, "%30d", &x) == 1) {
12206             vmminsecs = x;
12207             if (maxsilence / 1000 >= vmminsecs) {
12208                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12209             }
12210          } else {
12211             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12212          }
12213       }
12214 
12215       val = ast_variable_retrieve(cfg, "general", "format");
12216       if (!val) {
12217          val = "wav";   
12218       } else {
12219          tmp = ast_strdupa(val);
12220          val = ast_format_str_reduce(tmp);
12221          if (!val) {
12222             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12223             val = "wav";
12224          }
12225       }
12226       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12227 
12228       skipms = 3000;
12229       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12230          if (sscanf(val, "%30d", &x) == 1) {
12231             maxgreet = x;
12232          } else {
12233             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12234          }
12235       }
12236 
12237       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12238          if (sscanf(val, "%30d", &x) == 1) {
12239             skipms = x;
12240          } else {
12241             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12242          }
12243       }
12244 
12245       maxlogins = 3;
12246       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12247          if (sscanf(val, "%30d", &x) == 1) {
12248             maxlogins = x;
12249          } else {
12250             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12251          }
12252       }
12253 
12254       minpassword = MINPASSWORD;
12255       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12256          if (sscanf(val, "%30d", &x) == 1) {
12257             minpassword = x;
12258          } else {
12259             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12260          }
12261       }
12262 
12263       /* Force new user to record name ? */
12264       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12265          val = "no";
12266       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12267 
12268       /* Force new user to record greetings ? */
12269       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12270          val = "no";
12271       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12272 
12273       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12274          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12275          stringp = ast_strdupa(val);
12276          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12277             if (!ast_strlen_zero(stringp)) {
12278                q = strsep(&stringp, ",");
12279                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12280                   q++;
12281                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12282                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12283             } else {
12284                cidinternalcontexts[x][0] = '\0';
12285             }
12286          }
12287       }
12288       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12289          ast_debug(1, "VM Review Option disabled globally\n");
12290          val = "no";
12291       }
12292       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12293 
12294       /* Temporary greeting reminder */
12295       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12296          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12297          val = "no";
12298       } else {
12299          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12300       }
12301       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12302       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12303          ast_debug(1, "VM next message wrap disabled globally\n");
12304          val = "no";
12305       }
12306       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12307 
12308       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12309          ast_debug(1, "VM Operator break disabled globally\n");
12310          val = "no";
12311       }
12312       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12313 
12314       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12315          ast_debug(1, "VM CID Info before msg disabled globally\n");
12316          val = "no";
12317       } 
12318       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12319 
12320       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12321          ast_debug(1, "Send Voicemail msg disabled globally\n");
12322          val = "no";
12323       }
12324       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12325    
12326       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12327          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12328          val = "yes";
12329       }
12330       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12331 
12332       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12333          ast_debug(1, "Move Heard enabled globally\n");
12334          val = "yes";
12335       }
12336       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12337 
12338       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12339          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12340          val = "no";
12341       }
12342       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12343 
12344       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12345          ast_debug(1, "Duration info before msg enabled globally\n");
12346          val = "yes";
12347       }
12348       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12349 
12350       saydurationminfo = 2;
12351       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12352          if (sscanf(val, "%30d", &x) == 1) {
12353             saydurationminfo = x;
12354          } else {
12355             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12356          }
12357       }
12358 
12359       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12360          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12361          val = "no";
12362       }
12363       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12364 
12365       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12366          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12367          ast_debug(1, "found dialout context: %s\n", dialcontext);
12368       } else {
12369          dialcontext[0] = '\0';  
12370       }
12371       
12372       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12373          ast_copy_string(callcontext, val, sizeof(callcontext));
12374          ast_debug(1, "found callback context: %s\n", callcontext);
12375       } else {
12376          callcontext[0] = '\0';
12377       }
12378 
12379       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12380          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12381          ast_debug(1, "found operator context: %s\n", exitcontext);
12382       } else {
12383          exitcontext[0] = '\0';
12384       }
12385       
12386       /* load password sounds configuration */
12387       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12388          ast_copy_string(vm_password, val, sizeof(vm_password));
12389       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12390          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12391       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12392          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12393       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12394          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12395       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12396          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12397       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12398          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12399       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12400          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12401       }
12402       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12403          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12404       }
12405       /* load configurable audio prompts */
12406       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12407          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12408       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12409          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12410       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12411          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12412       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12413          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12414       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12415          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12416 
12417       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12418          val = "no";
12419       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12420 
12421       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12422          val = "voicemail.conf";
12423       }
12424       if (!(strcmp(val, "spooldir"))) {
12425          passwordlocation = OPT_PWLOC_SPOOLDIR;
12426       } else {
12427          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12428       }
12429 
12430       poll_freq = DEFAULT_POLL_FREQ;
12431       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12432          if (sscanf(val, "%30u", &poll_freq) != 1) {
12433             poll_freq = DEFAULT_POLL_FREQ;
12434             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12435          }
12436       }
12437 
12438       poll_mailboxes = 0;
12439       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12440          poll_mailboxes = ast_true(val);
12441 
12442       memset(fromstring, 0, sizeof(fromstring));
12443       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12444       strcpy(charset, "ISO-8859-1");
12445       if (emailbody) {
12446          ast_free(emailbody);
12447          emailbody = NULL;
12448       }
12449       if (emailsubject) {
12450          ast_free(emailsubject);
12451          emailsubject = NULL;
12452       }
12453       if (pagerbody) {
12454          ast_free(pagerbody);
12455          pagerbody = NULL;
12456       }
12457       if (pagersubject) {
12458          ast_free(pagersubject);
12459          pagersubject = NULL;
12460       }
12461       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12462          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12463       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12464          ast_copy_string(fromstring, val, sizeof(fromstring));
12465       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12466          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12467       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12468          ast_copy_string(charset, val, sizeof(charset));
12469       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12470          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12471          for (x = 0; x < 4; x++) {
12472             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12473          }
12474       }
12475       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12476          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12477          for (x = 0; x < 4; x++) {
12478             memcpy(&adsisec[x], &tmpadsi[x], 1);
12479          }
12480       }
12481       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12482          if (atoi(val)) {
12483             adsiver = atoi(val);
12484          }
12485       }
12486       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12487          ast_copy_string(zonetag, val, sizeof(zonetag));
12488       }
12489       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12490          ast_copy_string(locale, val, sizeof(locale));
12491       }
12492       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12493          emailsubject = ast_strdup(substitute_escapes(val));
12494       }
12495       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12496          emailbody = ast_strdup(substitute_escapes(val));
12497       }
12498       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12499          pagersubject = ast_strdup(substitute_escapes(val));
12500       }
12501       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12502          pagerbody = ast_strdup(substitute_escapes(val));
12503       }
12504 
12505       /* load mailboxes from users.conf */
12506       if (ucfg) { 
12507          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12508             if (!strcasecmp(cat, "general")) {
12509                continue;
12510             }
12511             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12512                continue;
12513             if ((current = find_or_create(userscontext, cat))) {
12514                populate_defaults(current);
12515                apply_options_full(current, ast_variable_browse(ucfg, cat));
12516                ast_copy_string(current->context, userscontext, sizeof(current->context));
12517                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12518                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12519                }
12520 
12521                switch (current->passwordlocation) {
12522                case OPT_PWLOC_SPOOLDIR:
12523                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12524                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12525                }
12526             }
12527          }
12528       }
12529 
12530       /* load mailboxes from voicemail.conf */
12531       cat = ast_category_browse(cfg, NULL);
12532       while (cat) {
12533          if (strcasecmp(cat, "general")) {
12534             var = ast_variable_browse(cfg, cat);
12535             if (strcasecmp(cat, "zonemessages")) {
12536                /* Process mailboxes in this context */
12537                while (var) {
12538                   append_mailbox(cat, var->name, var->value);
12539                   var = var->next;
12540                }
12541             } else {
12542                /* Timezones in this context */
12543                while (var) {
12544                   struct vm_zone *z;
12545                   if ((z = ast_malloc(sizeof(*z)))) {
12546                      char *msg_format, *tzone;
12547                      msg_format = ast_strdupa(var->value);
12548                      tzone = strsep(&msg_format, "|,");
12549                      if (msg_format) {
12550                         ast_copy_string(z->name, var->name, sizeof(z->name));
12551                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12552                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12553                         AST_LIST_LOCK(&zones);
12554                         AST_LIST_INSERT_HEAD(&zones, z, list);
12555                         AST_LIST_UNLOCK(&zones);
12556                      } else {
12557                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12558                         ast_free(z);
12559                      }
12560                   } else {
12561                      AST_LIST_UNLOCK(&users);
12562                      return -1;
12563                   }
12564                   var = var->next;
12565                }
12566             }
12567          }
12568          cat = ast_category_browse(cfg, cat);
12569       }
12570 
12571       AST_LIST_UNLOCK(&users);
12572 
12573       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12574          start_poll_thread();
12575       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12576          stop_poll_thread();;
12577 
12578       return 0;
12579    } else {
12580       AST_LIST_UNLOCK(&users);
12581       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12582       return 0;
12583    }
12584 }
12585 
12586 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12587 {
12588    int res = -1;
12589    char dir[PATH_MAX];
12590    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12591    ast_debug(2, "About to try retrieving name file %s\n", dir);
12592    RETRIEVE(dir, -1, mailbox, context);
12593    if (ast_fileexists(dir, NULL, NULL)) {
12594       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12595    }
12596    DISPOSE(dir, -1);
12597    return res;
12598 }
12599 
12600 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12601    struct ast_config *pwconf;
12602    struct ast_flags config_flags = { 0 };
12603 
12604    pwconf = ast_config_load(secretfn, config_flags);
12605    if (valid_config(pwconf)) {
12606       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12607       if (val) {
12608          ast_copy_string(password, val, passwordlen);
12609          ast_config_destroy(pwconf);
12610          return;
12611       }
12612       ast_config_destroy(pwconf);
12613    }
12614    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12615 }
12616 
12617 static int write_password_to_file(const char *secretfn, const char *password) {
12618    struct ast_config *conf;
12619    struct ast_category *cat;
12620    struct ast_variable *var;
12621    int res = -1;
12622 
12623    if (!(conf = ast_config_new())) {
12624       ast_log(LOG_ERROR, "Error creating new config structure\n");
12625       return res;
12626    }
12627    if (!(cat = ast_category_new("general", "", 1))) {
12628       ast_log(LOG_ERROR, "Error creating new category structure\n");
12629       ast_config_destroy(conf);
12630       return res;
12631    }
12632    if (!(var = ast_variable_new("password", password, ""))) {
12633       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12634       ast_config_destroy(conf);
12635       ast_category_destroy(cat);
12636       return res;
12637    }
12638    ast_category_append(conf, cat);
12639    ast_variable_append(cat, var);
12640    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12641       res = 0;
12642    } else {
12643       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12644    }
12645 
12646    ast_config_destroy(conf);
12647    return res;
12648 }
12649 
12650 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12651 {
12652    char *context;
12653    char *args_copy;
12654    int res;
12655 
12656    if (ast_strlen_zero(data)) {
12657       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12658       return -1;
12659    }
12660 
12661    args_copy = ast_strdupa(data);
12662    if ((context = strchr(args_copy, '@'))) {
12663       *context++ = '\0';
12664    } else {
12665       context = "default";
12666    }
12667 
12668    if ((res = sayname(chan, args_copy, context) < 0)) {
12669       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12670       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12671       if (!res) {
12672          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12673       }
12674    }
12675 
12676    return res;
12677 }
12678 
12679 #ifdef TEST_FRAMEWORK
12680 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12681 {
12682    return 0;
12683 }
12684 
12685 static struct ast_frame *fake_read(struct ast_channel *ast)
12686 {
12687    return &ast_null_frame;
12688 }
12689 
12690 AST_TEST_DEFINE(test_voicemail_vmsayname)
12691 {
12692    char dir[PATH_MAX];
12693    char dir2[PATH_MAX];
12694    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12695    static const char TEST_EXTENSION[] = "1234";
12696 
12697    struct ast_channel *test_channel1 = NULL;
12698    int res = -1;
12699 
12700    static const struct ast_channel_tech fake_tech = {
12701       .write = fake_write,
12702       .read = fake_read,
12703    };
12704 
12705    switch (cmd) {
12706    case TEST_INIT:
12707       info->name = "vmsayname_exec";
12708       info->category = "/apps/app_voicemail/";
12709       info->summary = "Vmsayname unit test";
12710       info->description =
12711          "This tests passing various parameters to vmsayname";
12712       return AST_TEST_NOT_RUN;
12713    case TEST_EXECUTE:
12714       break;
12715    }
12716 
12717    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12718         NULL, NULL, 0, 0, "TestChannel1"))) {
12719       goto exit_vmsayname_test;
12720    }
12721 
12722    /* normally this is done in the channel driver */
12723    test_channel1->nativeformats = AST_FORMAT_GSM;
12724    test_channel1->writeformat = AST_FORMAT_GSM;
12725    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12726    test_channel1->readformat = AST_FORMAT_GSM;
12727    test_channel1->rawreadformat = AST_FORMAT_GSM;
12728    test_channel1->tech = &fake_tech;
12729 
12730    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12731    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12732    if (!(res = vmsayname_exec(test_channel1, dir))) {
12733       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12734       if (ast_fileexists(dir, NULL, NULL)) {
12735          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12736          res = -1;
12737          goto exit_vmsayname_test;
12738       } else {
12739          /* no greeting already exists as expected, let's create one to fully test sayname */
12740          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12741             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12742             goto exit_vmsayname_test;
12743          }
12744          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12745          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12746          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12747          if ((res = symlink(dir, dir2))) {
12748             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12749             goto exit_vmsayname_test;
12750          }
12751          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12752          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12753          res = vmsayname_exec(test_channel1, dir);
12754 
12755          /* TODO: there may be a better way to do this */
12756          unlink(dir2);
12757          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12758          rmdir(dir2);
12759          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12760          rmdir(dir2);
12761       }
12762    }
12763 
12764 exit_vmsayname_test:
12765 
12766    if (test_channel1) {
12767       ast_hangup(test_channel1);
12768    }
12769 
12770    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12771 }
12772 
12773 AST_TEST_DEFINE(test_voicemail_msgcount)
12774 {
12775    int i, j, res = AST_TEST_PASS, syserr;
12776    struct ast_vm_user *vmu;
12777    struct ast_vm_user svm;
12778    struct vm_state vms;
12779 #ifdef IMAP_STORAGE
12780    struct ast_channel *chan = NULL;
12781 #endif
12782    struct {
12783       char dir[256];
12784       char file[256];
12785       char txtfile[256];
12786    } tmp[3];
12787    char syscmd[256];
12788    const char origweasels[] = "tt-weasels";
12789    const char testcontext[] = "test";
12790    const char testmailbox[] = "00000000";
12791    const char testspec[] = "00000000@test";
12792    FILE *txt;
12793    int new, old, urgent;
12794    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12795    const int folder2mbox[3] = { 1, 11, 0 };
12796    const int expected_results[3][12] = {
12797       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12798       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12799       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12800       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12801    };
12802 
12803    switch (cmd) {
12804    case TEST_INIT:
12805       info->name = "test_voicemail_msgcount";
12806       info->category = "/apps/app_voicemail/";
12807       info->summary = "Test Voicemail status checks";
12808       info->description =
12809          "Verify that message counts are correct when retrieved through the public API";
12810       return AST_TEST_NOT_RUN;
12811    case TEST_EXECUTE:
12812       break;
12813    }
12814 
12815    /* Make sure the original path was completely empty */
12816    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12817    if ((syserr = ast_safe_system(syscmd))) {
12818       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12819          syserr > 0 ? strerror(syserr) : "unable to fork()");
12820       return AST_TEST_FAIL;
12821    }
12822 
12823 #ifdef IMAP_STORAGE
12824    if (!(chan = ast_dummy_channel_alloc())) {
12825       ast_test_status_update(test, "Unable to create dummy channel\n");
12826       return AST_TEST_FAIL;
12827    }
12828 #endif
12829 
12830    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12831       !(vmu = find_or_create(testcontext, testmailbox))) {
12832       ast_test_status_update(test, "Cannot create vmu structure\n");
12833       ast_unreplace_sigchld();
12834 #ifdef IMAP_STORAGE
12835       chan = ast_channel_unref(chan);
12836 #endif
12837       return AST_TEST_FAIL;
12838    }
12839 
12840    populate_defaults(vmu);
12841    memset(&vms, 0, sizeof(vms));
12842 
12843    /* Create temporary voicemail */
12844    for (i = 0; i < 3; i++) {
12845       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12846       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12847       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12848 
12849       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12850          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12851             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12852          if ((syserr = ast_safe_system(syscmd))) {
12853             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12854                syserr > 0 ? strerror(syserr) : "unable to fork()");
12855             ast_unreplace_sigchld();
12856 #ifdef IMAP_STORAGE
12857             chan = ast_channel_unref(chan);
12858 #endif
12859             return AST_TEST_FAIL;
12860          }
12861       }
12862 
12863       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12864          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12865          fclose(txt);
12866       } else {
12867          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12868          res = AST_TEST_FAIL;
12869          break;
12870       }
12871       open_mailbox(&vms, vmu, folder2mbox[i]);
12872       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12873 
12874       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12875       for (j = 0; j < 3; j++) {
12876          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12877          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12878             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12879                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12880             res = AST_TEST_FAIL;
12881          }
12882       }
12883 
12884       new = old = urgent = 0;
12885       if (ast_app_inboxcount(testspec, &new, &old)) {
12886          ast_test_status_update(test, "inboxcount returned failure\n");
12887          res = AST_TEST_FAIL;
12888       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12889          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12890             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12891          res = AST_TEST_FAIL;
12892       }
12893 
12894       new = old = urgent = 0;
12895       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12896          ast_test_status_update(test, "inboxcount2 returned failure\n");
12897          res = AST_TEST_FAIL;
12898       } else if (old != expected_results[i][6 + 0] ||
12899             urgent != expected_results[i][6 + 1] ||
12900                new != expected_results[i][6 + 2]    ) {
12901          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12902             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12903          res = AST_TEST_FAIL;
12904       }
12905 
12906       new = old = urgent = 0;
12907       for (j = 0; j < 3; j++) {
12908          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12909             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12910                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12911             res = AST_TEST_FAIL;
12912          }
12913       }
12914    }
12915 
12916    for (i = 0; i < 3; i++) {
12917       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12918        * server, in which case, the rm below will not affect the
12919        * voicemails. */
12920       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12921       DISPOSE(tmp[i].dir, 0);
12922    }
12923 
12924    if (vms.deleted) {
12925       ast_free(vms.deleted);
12926    }
12927    if (vms.heard) {
12928       ast_free(vms.heard);
12929    }
12930 
12931 #ifdef IMAP_STORAGE
12932    chan = ast_channel_unref(chan);
12933 #endif
12934 
12935    /* And remove test directory */
12936    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12937    if ((syserr = ast_safe_system(syscmd))) {
12938       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12939          syserr > 0 ? strerror(syserr) : "unable to fork()");
12940    }
12941 
12942    return res;
12943 }
12944 
12945 AST_TEST_DEFINE(test_voicemail_notify_endl)
12946 {
12947    int res = AST_TEST_PASS;
12948    char testcontext[] = "test";
12949    char testmailbox[] = "00000000";
12950    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12951    char attach[256], attach2[256];
12952    char buf[256] = ""; /* No line should actually be longer than 80 */
12953    struct ast_channel *chan = NULL;
12954    struct ast_vm_user *vmu, vmus = {
12955       .flags = 0,
12956    };
12957    FILE *file;
12958    struct {
12959       char *name;
12960       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12961       void *location;
12962       union {
12963          int intval;
12964          char *strval;
12965       } u;
12966    } test_items[] = {
12967       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12968       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12969       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12970       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12971       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12972       { "attach2", STRPTR, attach2, .u.strval = "" },
12973       { "attach", STRPTR, attach, .u.strval = "" },
12974    };
12975    int which;
12976 
12977    switch (cmd) {
12978    case TEST_INIT:
12979       info->name = "test_voicemail_notify_endl";
12980       info->category = "/apps/app_voicemail/";
12981       info->summary = "Test Voicemail notification end-of-line";
12982       info->description =
12983          "Verify that notification emails use a consistent end-of-line character";
12984       return AST_TEST_NOT_RUN;
12985    case TEST_EXECUTE:
12986       break;
12987    }
12988 
12989    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12990    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12991 
12992    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12993       !(vmu = find_or_create(testcontext, testmailbox))) {
12994       ast_test_status_update(test, "Cannot create vmu structure\n");
12995       return AST_TEST_NOT_RUN;
12996    }
12997 
12998    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12999       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
13000       return AST_TEST_NOT_RUN;
13001    }
13002 
13003    populate_defaults(vmu);
13004    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
13005 #ifdef IMAP_STORAGE
13006    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
13007 #endif
13008 
13009    file = tmpfile();
13010    for (which = 0; which < ARRAY_LEN(test_items); which++) {
13011       /* Kill previous test, if any */
13012       rewind(file);
13013       if (ftruncate(fileno(file), 0)) {
13014          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
13015          res = AST_TEST_FAIL;
13016          break;
13017       }
13018 
13019       /* Make each change, in order, to the test mailbox */
13020       if (test_items[which].type == INT) {
13021          *((int *) test_items[which].location) = test_items[which].u.intval;
13022       } else if (test_items[which].type == FLAGVAL) {
13023          if (ast_test_flag(vmu, test_items[which].u.intval)) {
13024             ast_clear_flag(vmu, test_items[which].u.intval);
13025          } else {
13026             ast_set_flag(vmu, test_items[which].u.intval);
13027          }
13028       } else if (test_items[which].type == STATIC) {
13029          strcpy(test_items[which].location, test_items[which].u.strval);
13030       } else if (test_items[which].type == STRPTR) {
13031          test_items[which].location = test_items[which].u.strval;
13032       }
13033 
13034       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13035       rewind(file);
13036       while (fgets(buf, sizeof(buf), file)) {
13037          if (
13038 #ifdef IMAP_STORAGE
13039          buf[strlen(buf) - 2] != '\r'
13040 #else
13041          buf[strlen(buf) - 2] == '\r'
13042 #endif
13043          || buf[strlen(buf) - 1] != '\n') {
13044             res = AST_TEST_FAIL;
13045          }
13046       }
13047    }
13048    fclose(file);
13049    return res;
13050 }
13051 
13052 AST_TEST_DEFINE(test_voicemail_load_config)
13053 {
13054    int res = AST_TEST_PASS;
13055    struct ast_vm_user *vmu;
13056    struct ast_config *cfg;
13057    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13058    int fd;
13059    FILE *file;
13060    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13061 
13062    switch (cmd) {
13063    case TEST_INIT:
13064       info->name = "test_voicemail_load_config";
13065       info->category = "/apps/app_voicemail/";
13066       info->summary = "Test loading Voicemail config";
13067       info->description =
13068          "Verify that configuration is loaded consistently. "
13069          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13070          "some options were loaded after the mailboxes were instantiated, causing "
13071          "those options not to be set correctly.";
13072       return AST_TEST_NOT_RUN;
13073    case TEST_EXECUTE:
13074       break;
13075    }
13076 
13077    /* build a config file by hand... */
13078    if ((fd = mkstemp(config_filename)) < 0) {
13079       return AST_TEST_FAIL;
13080    }
13081    if (!(file = fdopen(fd, "w"))) {
13082       close(fd);
13083       unlink(config_filename);
13084       return AST_TEST_FAIL;
13085    }
13086    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13087    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13088    fputs("00000002 => 9999,Mrs. Test\n", file);
13089    fclose(file);
13090 
13091    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13092       res = AST_TEST_FAIL;
13093       goto cleanup;
13094    }
13095 
13096    load_config_from_memory(1, cfg, NULL);
13097    ast_config_destroy(cfg);
13098 
13099 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13100    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13101    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13102 
13103    AST_LIST_LOCK(&users);
13104    AST_LIST_TRAVERSE(&users, vmu, list) {
13105       if (!strcmp(vmu->mailbox, "00000001")) {
13106          if (0); /* trick to get CHECK to work */
13107          CHECK(vmu, callback, "othercontext")
13108          CHECK(vmu, locale, "nl_NL.UTF-8")
13109          CHECK(vmu, zonetag, "central")
13110       } else if (!strcmp(vmu->mailbox, "00000002")) {
13111          if (0); /* trick to get CHECK to work */
13112          CHECK(vmu, callback, "somecontext")
13113          CHECK(vmu, locale, "de_DE.UTF-8")
13114          CHECK(vmu, zonetag, "european")
13115       }
13116    }
13117    AST_LIST_UNLOCK(&users);
13118 
13119 #undef CHECK
13120 
13121    /* restore config */
13122    load_config(1); /* this might say "Failed to load configuration file." */
13123 
13124 cleanup:
13125    unlink(config_filename);
13126    return res;
13127 }
13128 
13129 #endif /* defined(TEST_FRAMEWORK) */
13130 
13131 static int reload(void)
13132 {
13133    return load_config(1);
13134 }
13135 
13136 static int unload_module(void)
13137 {
13138    int res;
13139 
13140    res = ast_unregister_application(app);
13141    res |= ast_unregister_application(app2);
13142    res |= ast_unregister_application(app3);
13143    res |= ast_unregister_application(app4);
13144    res |= ast_unregister_application(sayname_app);
13145    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13146    res |= ast_manager_unregister("VoicemailUsersList");
13147    res |= ast_data_unregister(NULL);
13148 #ifdef TEST_FRAMEWORK
13149    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13150    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13151    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13152    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13153    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13154 #endif
13155    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13156    ast_uninstall_vm_functions();
13157    ao2_ref(inprocess_container, -1);
13158 
13159    if (poll_thread != AST_PTHREADT_NULL)
13160       stop_poll_thread();
13161 
13162    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13163    ast_unload_realtime("voicemail");
13164    ast_unload_realtime("voicemail_data");
13165 
13166    free_vm_users();
13167    free_vm_zones();
13168    return res;
13169 }
13170 
13171 static int load_module(void)
13172 {
13173    int res;
13174    my_umask = umask(0);
13175    umask(my_umask);
13176 
13177    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13178       return AST_MODULE_LOAD_DECLINE;
13179    }
13180 
13181    /* compute the location of the voicemail spool directory */
13182    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13183    
13184    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13185       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13186    }
13187 
13188    if ((res = load_config(0)))
13189       return res;
13190 
13191    res = ast_register_application_xml(app, vm_exec);
13192    res |= ast_register_application_xml(app2, vm_execmain);
13193    res |= ast_register_application_xml(app3, vm_box_exists);
13194    res |= ast_register_application_xml(app4, vmauthenticate);
13195    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13196    res |= ast_custom_function_register(&mailbox_exists_acf);
13197    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13198 #ifdef TEST_FRAMEWORK
13199    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13200    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13201    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13202    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13203    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13204 #endif
13205 
13206    if (res)
13207       return res;
13208 
13209    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13210    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13211 
13212    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13213    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13214    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13215 
13216    return res;
13217 }
13218 
13219 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13220 {
13221    int cmd = 0;
13222    char destination[80] = "";
13223    int retries = 0;
13224 
13225    if (!num) {
13226       ast_verb(3, "Destination number will be entered manually\n");
13227       while (retries < 3 && cmd != 't') {
13228          destination[1] = '\0';
13229          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13230          if (!cmd)
13231             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13232          if (!cmd)
13233             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13234          if (!cmd) {
13235             cmd = ast_waitfordigit(chan, 6000);
13236             if (cmd)
13237                destination[0] = cmd;
13238          }
13239          if (!cmd) {
13240             retries++;
13241          } else {
13242 
13243             if (cmd < 0)
13244                return 0;
13245             if (cmd == '*') {
13246                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13247                return 0;
13248             }
13249             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13250                retries++;
13251             else
13252                cmd = 't';
13253          }
13254          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13255       }
13256       if (retries >= 3) {
13257          return 0;
13258       }
13259       
13260    } else {
13261       if (option_verbose > 2)
13262          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13263       ast_copy_string(destination, num, sizeof(destination));
13264    }
13265 
13266    if (!ast_strlen_zero(destination)) {
13267       if (destination[strlen(destination) -1 ] == '*')
13268          return 0; 
13269       if (option_verbose > 2)
13270          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13271       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13272       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13273       chan->priority = 0;
13274       return 9;
13275    }
13276    return 0;
13277 }
13278 
13279 /*!
13280  * \brief The advanced options within a message.
13281  * \param chan
13282  * \param vmu 
13283  * \param vms
13284  * \param msg
13285  * \param option
13286  * \param record_gain
13287  *
13288  * Provides handling for the play message envelope, call the person back, or reply to message. 
13289  *
13290  * \return zero on success, -1 on error.
13291  */
13292 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)
13293 {
13294    int res = 0;
13295    char filename[PATH_MAX];
13296    struct ast_config *msg_cfg = NULL;
13297    const char *origtime, *context;
13298    char *name, *num;
13299    int retries = 0;
13300    char *cid;
13301    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13302 
13303    vms->starting = 0; 
13304 
13305    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13306 
13307    /* Retrieve info from VM attribute file */
13308    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13309    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13310    msg_cfg = ast_config_load(filename, config_flags);
13311    DISPOSE(vms->curdir, vms->curmsg);
13312    if (!valid_config(msg_cfg)) {
13313       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13314       return 0;
13315    }
13316 
13317    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13318       ast_config_destroy(msg_cfg);
13319       return 0;
13320    }
13321 
13322    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13323 
13324    context = ast_variable_retrieve(msg_cfg, "message", "context");
13325    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13326       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13327    switch (option) {
13328    case 3: /* Play message envelope */
13329       if (!res)
13330          res = play_message_datetime(chan, vmu, origtime, filename);
13331       if (!res)
13332          res = play_message_callerid(chan, vms, cid, context, 0);
13333 
13334       res = 't';
13335       break;
13336 
13337    case 2:  /* Call back */
13338 
13339       if (ast_strlen_zero(cid))
13340          break;
13341 
13342       ast_callerid_parse(cid, &name, &num);
13343       while ((res > -1) && (res != 't')) {
13344          switch (res) {
13345          case '1':
13346             if (num) {
13347                /* Dial the CID number */
13348                res = dialout(chan, vmu, num, vmu->callback);
13349                if (res) {
13350                   ast_config_destroy(msg_cfg);
13351                   return 9;
13352                }
13353             } else {
13354                res = '2';
13355             }
13356             break;
13357 
13358          case '2':
13359             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13360             if (!ast_strlen_zero(vmu->dialout)) {
13361                res = dialout(chan, vmu, NULL, vmu->dialout);
13362                if (res) {
13363                   ast_config_destroy(msg_cfg);
13364                   return 9;
13365                }
13366             } else {
13367                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13368                res = ast_play_and_wait(chan, "vm-sorry");
13369             }
13370             ast_config_destroy(msg_cfg);
13371             return res;
13372          case '*':
13373             res = 't';
13374             break;
13375          case '3':
13376          case '4':
13377          case '5':
13378          case '6':
13379          case '7':
13380          case '8':
13381          case '9':
13382          case '0':
13383 
13384             res = ast_play_and_wait(chan, "vm-sorry");
13385             retries++;
13386             break;
13387          default:
13388             if (num) {
13389                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13390                res = ast_play_and_wait(chan, "vm-num-i-have");
13391                if (!res)
13392                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13393                if (!res)
13394                   res = ast_play_and_wait(chan, "vm-tocallnum");
13395                /* Only prompt for a caller-specified number if there is a dialout context specified */
13396                if (!ast_strlen_zero(vmu->dialout)) {
13397                   if (!res)
13398                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13399                }
13400             } else {
13401                res = ast_play_and_wait(chan, "vm-nonumber");
13402                if (!ast_strlen_zero(vmu->dialout)) {
13403                   if (!res)
13404                      res = ast_play_and_wait(chan, "vm-toenternumber");
13405                }
13406             }
13407             if (!res) {
13408                res = ast_play_and_wait(chan, "vm-star-cancel");
13409             }
13410             if (!res) {
13411                res = ast_waitfordigit(chan, 6000);
13412             }
13413             if (!res) {
13414                retries++;
13415                if (retries > 3) {
13416                   res = 't';
13417                }
13418             }
13419             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13420             break; 
13421             
13422          }
13423          if (res == 't')
13424             res = 0;
13425          else if (res == '*')
13426             res = -1;
13427       }
13428       break;
13429       
13430    case 1:  /* Reply */
13431       /* Send reply directly to sender */
13432       if (ast_strlen_zero(cid))
13433          break;
13434 
13435       ast_callerid_parse(cid, &name, &num);
13436       if (!num) {
13437          ast_verb(3, "No CID number available, no reply sent\n");
13438          if (!res)
13439             res = ast_play_and_wait(chan, "vm-nonumber");
13440          ast_config_destroy(msg_cfg);
13441          return res;
13442       } else {
13443          struct ast_vm_user vmu2;
13444          if (find_user(&vmu2, vmu->context, num)) {
13445             struct leave_vm_options leave_options;
13446             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13447             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13448 
13449             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13450             
13451             memset(&leave_options, 0, sizeof(leave_options));
13452             leave_options.record_gain = record_gain;
13453             res = leave_voicemail(chan, mailbox, &leave_options);
13454             if (!res)
13455                res = 't';
13456             ast_config_destroy(msg_cfg);
13457             return res;
13458          } else {
13459             /* Sender has no mailbox, can't reply */
13460             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13461             ast_play_and_wait(chan, "vm-nobox");
13462             res = 't';
13463             ast_config_destroy(msg_cfg);
13464             return res;
13465          }
13466       } 
13467       res = 0;
13468 
13469       break;
13470    }
13471 
13472    ast_config_destroy(msg_cfg);
13473 
13474 #ifndef IMAP_STORAGE
13475    if (!res) {
13476       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13477       vms->heard[msg] = 1;
13478       res = wait_file(chan, vms, vms->fn);
13479    }
13480 #endif
13481    return res;
13482 }
13483 
13484 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13485          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13486          signed char record_gain, struct vm_state *vms, char *flag)
13487 {
13488    /* Record message & let caller review or re-record it, or set options if applicable */
13489    int res = 0;
13490    int cmd = 0;
13491    int max_attempts = 3;
13492    int attempts = 0;
13493    int recorded = 0;
13494    int msg_exists = 0;
13495    signed char zero_gain = 0;
13496    char tempfile[PATH_MAX];
13497    char *acceptdtmf = "#";
13498    char *canceldtmf = "";
13499    int canceleddtmf = 0;
13500 
13501    /* Note that urgent and private are for flagging messages as such in the future */
13502 
13503    /* barf if no pointer passed to store duration in */
13504    if (duration == NULL) {
13505       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13506       return -1;
13507    }
13508 
13509    if (!outsidecaller)
13510       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13511    else
13512       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13513 
13514    cmd = '3';  /* Want to start by recording */
13515 
13516    while ((cmd >= 0) && (cmd != 't')) {
13517       switch (cmd) {
13518       case '1':
13519          if (!msg_exists) {
13520             /* In this case, 1 is to record a message */
13521             cmd = '3';
13522             break;
13523          } else {
13524             /* Otherwise 1 is to save the existing message */
13525             ast_verb(3, "Saving message as is\n");
13526             if (!outsidecaller) 
13527                ast_filerename(tempfile, recordfile, NULL);
13528             ast_stream_and_wait(chan, "vm-msgsaved", "");
13529             if (!outsidecaller) {
13530                /* Saves to IMAP server only if imapgreeting=yes */
13531                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13532                DISPOSE(recordfile, -1);
13533             }
13534             cmd = 't';
13535             return res;
13536          }
13537       case '2':
13538          /* Review */
13539          ast_verb(3, "Reviewing the message\n");
13540          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13541          break;
13542       case '3':
13543          msg_exists = 0;
13544          /* Record */
13545          if (recorded == 1) 
13546             ast_verb(3, "Re-recording the message\n");
13547          else  
13548             ast_verb(3, "Recording the message\n");
13549          
13550          if (recorded && outsidecaller) {
13551             cmd = ast_play_and_wait(chan, INTRO);
13552             cmd = ast_play_and_wait(chan, "beep");
13553          }
13554          recorded = 1;
13555          /* 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 */
13556          if (record_gain)
13557             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13558          if (ast_test_flag(vmu, VM_OPERATOR))
13559             canceldtmf = "0";
13560          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13561          if (strchr(canceldtmf, cmd)) {
13562          /* need this flag here to distinguish between pressing '0' during message recording or after */
13563             canceleddtmf = 1;
13564          }
13565          if (record_gain)
13566             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13567          if (cmd == -1) {
13568             /* User has hung up, no options to give */
13569             if (!outsidecaller) {
13570                /* user was recording a greeting and they hung up, so let's delete the recording. */
13571                ast_filedelete(tempfile, NULL);
13572             }     
13573             return cmd;
13574          }
13575          if (cmd == '0') {
13576             break;
13577          } else if (cmd == '*') {
13578             break;
13579 #if 0
13580          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13581             /* Message is too short */
13582             ast_verb(3, "Message too short\n");
13583             cmd = ast_play_and_wait(chan, "vm-tooshort");
13584             cmd = ast_filedelete(tempfile, NULL);
13585             break;
13586          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13587             /* Message is all silence */
13588             ast_verb(3, "Nothing recorded\n");
13589             cmd = ast_filedelete(tempfile, NULL);
13590             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13591             if (!cmd)
13592                cmd = ast_play_and_wait(chan, "vm-speakup");
13593             break;
13594 #endif
13595          } else {
13596             /* If all is well, a message exists */
13597             msg_exists = 1;
13598             cmd = 0;
13599          }
13600          break;
13601       case '4':
13602          if (outsidecaller) {  /* only mark vm messages */
13603             /* Mark Urgent */
13604             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13605                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13606                res = ast_play_and_wait(chan, "vm-marked-urgent");
13607                strcpy(flag, "Urgent");
13608             } else if (flag) {
13609                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13610                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13611                strcpy(flag, "");
13612             } else {
13613                ast_play_and_wait(chan, "vm-sorry");
13614             }
13615             cmd = 0;
13616          } else {
13617             cmd = ast_play_and_wait(chan, "vm-sorry");
13618          }
13619          break;
13620       case '5':
13621       case '6':
13622       case '7':
13623       case '8':
13624       case '9':
13625       case '*':
13626       case '#':
13627          cmd = ast_play_and_wait(chan, "vm-sorry");
13628          break;
13629 #if 0 
13630 /*  XXX Commented out for the moment because of the dangers of deleting
13631     a message while recording (can put the message numbers out of sync) */
13632       case '*':
13633          /* Cancel recording, delete message, offer to take another message*/
13634          cmd = ast_play_and_wait(chan, "vm-deleted");
13635          cmd = ast_filedelete(tempfile, NULL);
13636          if (outsidecaller) {
13637             res = vm_exec(chan, NULL);
13638             return res;
13639          }
13640          else
13641             return 1;
13642 #endif
13643       case '0':
13644          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13645             cmd = ast_play_and_wait(chan, "vm-sorry");
13646             break;
13647          }
13648          if (msg_exists || recorded) {
13649             cmd = ast_play_and_wait(chan, "vm-saveoper");
13650             if (!cmd)
13651                cmd = ast_waitfordigit(chan, 3000);
13652             if (cmd == '1') {
13653                ast_filerename(tempfile, recordfile, NULL);
13654                ast_play_and_wait(chan, "vm-msgsaved");
13655                cmd = '0';
13656             } else if (cmd == '4') {
13657                if (flag) {
13658                   ast_play_and_wait(chan, "vm-marked-urgent");
13659                   strcpy(flag, "Urgent");
13660                }
13661                ast_play_and_wait(chan, "vm-msgsaved");
13662                cmd = '0';
13663             } else {
13664                ast_play_and_wait(chan, "vm-deleted");
13665                DELETE(tempfile, -1, tempfile, vmu);
13666                cmd = '0';
13667             }
13668          }
13669          return cmd;
13670       default:
13671          /* If the caller is an ouside caller, and the review option is enabled,
13672             allow them to review the message, but let the owner of the box review
13673             their OGM's */
13674          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13675             return cmd;
13676          if (msg_exists) {
13677             cmd = ast_play_and_wait(chan, "vm-review");
13678             if (!cmd && outsidecaller) {
13679                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13680                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13681                } else if (flag) {
13682                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13683                }
13684             }
13685          } else {
13686             cmd = ast_play_and_wait(chan, "vm-torerecord");
13687             if (!cmd)
13688                cmd = ast_waitfordigit(chan, 600);
13689          }
13690          
13691          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13692             cmd = ast_play_and_wait(chan, "vm-reachoper");
13693             if (!cmd)
13694                cmd = ast_waitfordigit(chan, 600);
13695          }
13696 #if 0
13697          if (!cmd)
13698             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13699 #endif
13700          if (!cmd)
13701             cmd = ast_waitfordigit(chan, 6000);
13702          if (!cmd) {
13703             attempts++;
13704          }
13705          if (attempts > max_attempts) {
13706             cmd = 't';
13707          }
13708       }
13709    }
13710    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13711       /* Hang up or timeout, so delete the recording. */
13712       ast_filedelete(tempfile, NULL);
13713    }
13714 
13715    if (cmd != 't' && outsidecaller)
13716       ast_play_and_wait(chan, "vm-goodbye");
13717 
13718    return cmd;
13719 }
13720 
13721 /* This is a workaround so that menuselect displays a proper description
13722  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13723  */
13724 
13725 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13726       .load = load_module,
13727       .unload = unload_module,
13728       .reload = reload,
13729       .nonoptreq = "res_adsi,res_smdi",
13730       );

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