Wed Jan 27 20:02:02 2016

Asterisk developer's documentation


app_voicemail.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_odbcstorage" displayname="Voicemail ODBC Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_odbcstorage.c apps/app_directory_odbcstorage.c">
00048    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00049       <depend>generic_odbc</depend>
00050       <depend>ltdl</depend>
00051       <defaultenabled>yes</defaultenabled>
00052       <support_level>core</support_level>
00053    </member>
00054 </category>
00055 <category name="MENUSELECT_OPTS_app_voicemail_imapstorage" displayname="Voicemail IMAP Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_imapstorage.c apps/app_directory_imapstorage.c">
00056    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00057       <depend>imap_tk</depend>
00058       <use>openssl</use>
00059       <defaultenabled>yes</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062 </category>
00063 ***/
00064 
00065 #include "asterisk.h"
00066 
00067 #ifdef IMAP_STORAGE
00068 #include <ctype.h>
00069 #include <signal.h>
00070 #include <pwd.h>
00071 #ifdef USE_SYSTEM_IMAP
00072 #include <imap/c-client.h>
00073 #include <imap/imap4r1.h>
00074 #include <imap/linkage.h>
00075 #elif defined (USE_SYSTEM_CCLIENT)
00076 #include <c-client/c-client.h>
00077 #include <c-client/imap4r1.h>
00078 #include <c-client/linkage.h>
00079 #else
00080 #include "c-client.h"
00081 #include "imap4r1.h"
00082 #include "linkage.h"
00083 #endif
00084 #endif
00085 
00086 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426691 $")
00087 
00088 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00089 #include <sys/time.h>
00090 #include <sys/stat.h>
00091 #include <sys/mman.h>
00092 #include <time.h>
00093 #include <dirent.h>
00094 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00095 #include <sys/wait.h>
00096 #endif
00097 
00098 #include "asterisk/logger.h"
00099 #include "asterisk/lock.h"
00100 #include "asterisk/file.h"
00101 #include "asterisk/channel.h"
00102 #include "asterisk/pbx.h"
00103 #include "asterisk/config.h"
00104 #include "asterisk/say.h"
00105 #include "asterisk/module.h"
00106 #include "asterisk/adsi.h"
00107 #include "asterisk/app.h"
00108 #include "asterisk/manager.h"
00109 #include "asterisk/dsp.h"
00110 #include "asterisk/localtime.h"
00111 #include "asterisk/cli.h"
00112 #include "asterisk/utils.h"
00113 #include "asterisk/stringfields.h"
00114 #include "asterisk/smdi.h"
00115 #include "asterisk/astobj2.h"
00116 #include "asterisk/event.h"
00117 #include "asterisk/taskprocessor.h"
00118 #include "asterisk/test.h"
00119 
00120 #ifdef ODBC_STORAGE
00121 #include "asterisk/res_odbc.h"
00122 #endif
00123 
00124 #ifdef IMAP_STORAGE
00125 #include "asterisk/threadstorage.h"
00126 #endif
00127 
00128 /*** DOCUMENTATION
00129    <application name="VoiceMail" language="en_US">
00130       <synopsis>
00131          Leave a Voicemail message.
00132       </synopsis>
00133       <syntax>
00134          <parameter name="mailboxs" argsep="&amp;" required="true">
00135             <argument name="mailbox1" argsep="@" required="true">
00136                <argument name="mailbox" required="true" />
00137                <argument name="context" />
00138             </argument>
00139             <argument name="mailbox2" argsep="@" multiple="true">
00140                <argument name="mailbox" required="true" />
00141                <argument name="context" />
00142             </argument>
00143          </parameter>
00144          <parameter name="options">
00145             <optionlist>
00146                <option name="b">
00147                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00148                </option>
00149                <option name="d">
00150                   <argument name="c" />
00151                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00152                   if played during the greeting. Context defaults to the current context.</para>
00153                </option>
00154                <option name="g">
00155                   <argument name="#" required="true" />
00156                   <para>Use the specified amount of gain when recording the voicemail
00157                   message. The units are whole-number decibels (dB). Only works on supported
00158                   technologies, which is DAHDI only.</para>
00159                </option>
00160                <option name="s">
00161                   <para>Skip the playback of instructions for leaving a message to the
00162                   calling party.</para>
00163                </option>
00164                <option name="u">
00165                   <para>Play the <literal>unavailable</literal> greeting.</para>
00166                </option>
00167                <option name="U">
00168                   <para>Mark message as <literal>URGENT</literal>.</para>
00169                </option>
00170                <option name="P">
00171                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00172                </option>
00173             </optionlist>
00174          </parameter>
00175       </syntax>
00176       <description>
00177          <para>This application allows the calling party to leave a message for the specified
00178          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00179          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00180          exist.</para>
00181          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00182          <enumlist>
00183             <enum name="0">
00184                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00185             </enum>
00186             <enum name="*">
00187                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00188             </enum>
00189          </enumlist>
00190          <para>This application will set the following channel variable upon completion:</para>
00191          <variablelist>
00192             <variable name="VMSTATUS">
00193                <para>This indicates the status of the execution of the VoiceMail application.</para>
00194                <value name="SUCCESS" />
00195                <value name="USEREXIT" />
00196                <value name="FAILED" />
00197             </variable>
00198          </variablelist>
00199       </description>
00200       <see-also>
00201          <ref type="application">VoiceMailMain</ref>
00202       </see-also>
00203    </application>
00204    <application name="VoiceMailMain" language="en_US">
00205       <synopsis>
00206          Check Voicemail messages.
00207       </synopsis>
00208       <syntax>
00209          <parameter name="mailbox" required="true" argsep="@">
00210             <argument name="mailbox" />
00211             <argument name="context" />
00212          </parameter>
00213          <parameter name="options">
00214             <optionlist>
00215                <option name="p">
00216                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00217                   the mailbox that is entered by the caller.</para>
00218                </option>
00219                <option name="g">
00220                   <argument name="#" required="true" />
00221                   <para>Use the specified amount of gain when recording a voicemail message.
00222                   The units are whole-number decibels (dB).</para>
00223                </option>
00224                <option name="s">
00225                   <para>Skip checking the passcode for the mailbox.</para>
00226                </option>
00227                <option name="a">
00228                   <argument name="folder" required="true" />
00229                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00230                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00231                   <enumlist>
00232                      <enum name="0"><para>INBOX</para></enum>
00233                      <enum name="1"><para>Old</para></enum>
00234                      <enum name="2"><para>Work</para></enum>
00235                      <enum name="3"><para>Family</para></enum>
00236                      <enum name="4"><para>Friends</para></enum>
00237                      <enum name="5"><para>Cust1</para></enum>
00238                      <enum name="6"><para>Cust2</para></enum>
00239                      <enum name="7"><para>Cust3</para></enum>
00240                      <enum name="8"><para>Cust4</para></enum>
00241                      <enum name="9"><para>Cust5</para></enum>
00242                   </enumlist>
00243                </option>
00244             </optionlist>
00245          </parameter>
00246       </syntax>
00247       <description>
00248          <para>This application allows the calling party to check voicemail messages. A specific
00249          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00250          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00251          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00252          <literal>default</literal> context will be used.</para>
00253          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00254          or Password, and the extension exists:</para>
00255          <enumlist>
00256             <enum name="*">
00257                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00258             </enum>
00259          </enumlist>
00260       </description>
00261       <see-also>
00262          <ref type="application">VoiceMail</ref>
00263       </see-also>
00264    </application>
00265    <application name="MailboxExists" language="en_US">
00266       <synopsis>
00267          Check to see if Voicemail mailbox exists.
00268       </synopsis>
00269       <syntax>
00270          <parameter name="mailbox" required="true" argsep="@">
00271             <argument name="mailbox" required="true" />
00272             <argument name="context" />
00273          </parameter>
00274          <parameter name="options">
00275             <para>None options.</para>
00276          </parameter>
00277       </syntax>
00278       <description>
00279          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00280          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00281          will be used.</para>
00282          <para>This application will set the following channel variable upon completion:</para>
00283          <variablelist>
00284             <variable name="VMBOXEXISTSSTATUS">
00285                <para>This will contain the status of the execution of the MailboxExists application.
00286                Possible values include:</para>
00287                <value name="SUCCESS" />
00288                <value name="FAILED" />
00289             </variable>
00290          </variablelist>
00291       </description>
00292    </application>
00293    <application name="VMAuthenticate" language="en_US">
00294       <synopsis>
00295          Authenticate with Voicemail passwords.
00296       </synopsis>
00297       <syntax>
00298          <parameter name="mailbox" required="true" argsep="@">
00299             <argument name="mailbox" />
00300             <argument name="context" />
00301          </parameter>
00302          <parameter name="options">
00303             <optionlist>
00304                <option name="s">
00305                   <para>Skip playing the initial prompts.</para>
00306                </option>
00307             </optionlist>
00308          </parameter>
00309       </syntax>
00310       <description>
00311          <para>This application behaves the same way as the Authenticate application, but the passwords
00312          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00313          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00314          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00315          mailbox.</para>
00316          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00317          or Password, and the extension exists:</para>
00318          <enumlist>
00319             <enum name="*">
00320                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00321             </enum>
00322          </enumlist>
00323       </description>
00324    </application>
00325    <application name="VMSayName" language="en_US">
00326       <synopsis>
00327          Play the name of a voicemail user
00328       </synopsis>
00329       <syntax>
00330          <parameter name="mailbox" required="true" argsep="@">
00331             <argument name="mailbox" />
00332             <argument name="context" />
00333          </parameter>
00334       </syntax>
00335       <description>
00336          <para>This application will say the recorded name of the voicemail user specified as the
00337          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00338       </description>
00339    </application>
00340    <function name="MAILBOX_EXISTS" language="en_US">
00341       <synopsis>
00342          Tell if a mailbox is configured.
00343       </synopsis>
00344       <syntax argsep="@">
00345          <parameter name="mailbox" required="true" />
00346          <parameter name="context" />
00347       </syntax>
00348       <description>
00349          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00350          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00351          context.</para>
00352       </description>
00353    </function>
00354    <manager name="VoicemailUsersList" language="en_US">
00355       <synopsis>
00356          List All Voicemail User Information.
00357       </synopsis>
00358       <syntax>
00359          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00360       </syntax>
00361       <description>
00362       </description>
00363    </manager>
00364  ***/
00365 
00366 #ifdef IMAP_STORAGE
00367 static char imapserver[48];
00368 static char imapport[8];
00369 static char imapflags[128];
00370 static char imapfolder[64];
00371 static char imapparentfolder[64] = "\0";
00372 static char greetingfolder[64];
00373 static char authuser[32];
00374 static char authpassword[42];
00375 static int imapversion = 1;
00376 
00377 static int expungeonhangup = 1;
00378 static int imapgreetings = 0;
00379 static char delimiter = '\0';
00380 
00381 struct vm_state;
00382 struct ast_vm_user;
00383 
00384 AST_THREADSTORAGE(ts_vmstate);
00385 
00386 /* Forward declarations for IMAP */
00387 static int init_mailstream(struct vm_state *vms, int box);
00388 static void write_file(char *filename, char *buffer, unsigned long len);
00389 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00390 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00391 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00392 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00393 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00394 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00395 static void vmstate_insert(struct vm_state *vms);
00396 static void vmstate_delete(struct vm_state *vms);
00397 static void set_update(MAILSTREAM * stream);
00398 static void init_vm_state(struct vm_state *vms);
00399 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00400 static void get_mailbox_delimiter(MAILSTREAM *stream);
00401 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00402 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00403 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);
00404 static void update_messages_by_imapuser(const char *user, unsigned long number);
00405 static int vm_delete(char *file);
00406 
00407 static int imap_remove_file (char *dir, int msgnum);
00408 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00409 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00410 static void check_quota(struct vm_state *vms, char *mailbox);
00411 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00412 struct vmstate {
00413    struct vm_state *vms;
00414    AST_LIST_ENTRY(vmstate) list;
00415 };
00416 
00417 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00418 
00419 #endif
00420 
00421 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00422 
00423 #define COMMAND_TIMEOUT 5000
00424 /* Don't modify these here; set your umask at runtime instead */
00425 #define  VOICEMAIL_DIR_MODE   0777
00426 #define  VOICEMAIL_FILE_MODE  0666
00427 #define  CHUNKSIZE   65536
00428 
00429 #define VOICEMAIL_CONFIG "voicemail.conf"
00430 #define ASTERISK_USERNAME "asterisk"
00431 
00432 /* Define fast-forward, pause, restart, and reverse keys
00433  * while listening to a voicemail message - these are
00434  * strings, not characters */
00435 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00436 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00437 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00438 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00439 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00440 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00441 
00442 /* Default mail command to mail voicemail. Change it with the
00443  * mailcmd= command in voicemail.conf */
00444 #define SENDMAIL "/usr/sbin/sendmail -t"
00445 
00446 #define INTRO "vm-intro"
00447 
00448 #define MAXMSG 100
00449 #define MAXMSGLIMIT 9999
00450 
00451 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00452 
00453 #define BASELINELEN 72
00454 #define BASEMAXINLINE 256
00455 #ifdef IMAP_STORAGE
00456 #define ENDL "\r\n"
00457 #else
00458 #define ENDL "\n"
00459 #endif
00460 
00461 #define MAX_DATETIME_FORMAT   512
00462 #define MAX_NUM_CID_CONTEXTS 10
00463 
00464 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00465 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00466 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00467 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00468 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00469 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00470 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00471 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00472 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00473 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00474 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00475 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00476 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00477 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00478 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00479 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00480 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00481 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00482 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00483 #define ERROR_LOCK_PATH  -100
00484 #define OPERATOR_EXIT     300
00485 
00486 
00487 enum vm_box {
00488    NEW_FOLDER,
00489    OLD_FOLDER,
00490    WORK_FOLDER,
00491    FAMILY_FOLDER,
00492    FRIENDS_FOLDER,
00493    GREETINGS_FOLDER
00494 };
00495 
00496 enum vm_option_flags {
00497    OPT_SILENT =           (1 << 0),
00498    OPT_BUSY_GREETING =    (1 << 1),
00499    OPT_UNAVAIL_GREETING = (1 << 2),
00500    OPT_RECORDGAIN =       (1 << 3),
00501    OPT_PREPEND_MAILBOX =  (1 << 4),
00502    OPT_AUTOPLAY =         (1 << 6),
00503    OPT_DTMFEXIT =         (1 << 7),
00504    OPT_MESSAGE_Urgent =   (1 << 8),
00505    OPT_MESSAGE_PRIORITY = (1 << 9)
00506 };
00507 
00508 enum vm_option_args {
00509    OPT_ARG_RECORDGAIN = 0,
00510    OPT_ARG_PLAYFOLDER = 1,
00511    OPT_ARG_DTMFEXIT   = 2,
00512    /* This *must* be the last value in this enum! */
00513    OPT_ARG_ARRAY_SIZE = 3,
00514 };
00515 
00516 enum vm_passwordlocation {
00517    OPT_PWLOC_VOICEMAILCONF = 0,
00518    OPT_PWLOC_SPOOLDIR      = 1,
00519    OPT_PWLOC_USERSCONF     = 2,
00520 };
00521 
00522 AST_APP_OPTIONS(vm_app_options, {
00523    AST_APP_OPTION('s', OPT_SILENT),
00524    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00525    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00526    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00527    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00528    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00529    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00530    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00531    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00532 });
00533 
00534 static int load_config(int reload);
00535 #ifdef TEST_FRAMEWORK
00536 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00537 #endif
00538 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00539 
00540 /*! \page vmlang Voicemail Language Syntaxes Supported
00541 
00542    \par Syntaxes supported, not really language codes.
00543    \arg \b en    - English
00544    \arg \b de    - German
00545    \arg \b es    - Spanish
00546    \arg \b fr    - French
00547    \arg \b it    - Italian
00548    \arg \b nl    - Dutch
00549    \arg \b pt    - Portuguese
00550    \arg \b pt_BR - Portuguese (Brazil)
00551    \arg \b gr    - Greek
00552    \arg \b no    - Norwegian
00553    \arg \b se    - Swedish
00554    \arg \b tw    - Chinese (Taiwan)
00555    \arg \b ua - Ukrainian
00556 
00557 German requires the following additional soundfile:
00558 \arg \b 1F  einE (feminine)
00559 
00560 Spanish requires the following additional soundfile:
00561 \arg \b 1M      un (masculine)
00562 
00563 Dutch, Portuguese & Spanish require the following additional soundfiles:
00564 \arg \b vm-INBOXs singular of 'new'
00565 \arg \b vm-Olds      singular of 'old/heard/read'
00566 
00567 NB these are plural:
00568 \arg \b vm-INBOX  nieuwe (nl)
00569 \arg \b vm-Old    oude (nl)
00570 
00571 Polish uses:
00572 \arg \b vm-new-a  'new', feminine singular accusative
00573 \arg \b vm-new-e  'new', feminine plural accusative
00574 \arg \b vm-new-ych   'new', feminine plural genitive
00575 \arg \b vm-old-a  'old', feminine singular accusative
00576 \arg \b vm-old-e  'old', feminine plural accusative
00577 \arg \b vm-old-ych   'old', feminine plural genitive
00578 \arg \b digits/1-a   'one', not always same as 'digits/1'
00579 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00580 
00581 Swedish uses:
00582 \arg \b vm-nytt      singular of 'new'
00583 \arg \b vm-nya    plural of 'new'
00584 \arg \b vm-gammalt   singular of 'old'
00585 \arg \b vm-gamla  plural of 'old'
00586 \arg \b digits/ett   'one', not always same as 'digits/1'
00587 
00588 Norwegian uses:
00589 \arg \b vm-ny     singular of 'new'
00590 \arg \b vm-nye    plural of 'new'
00591 \arg \b vm-gammel singular of 'old'
00592 \arg \b vm-gamle  plural of 'old'
00593 
00594 Dutch also uses:
00595 \arg \b nl-om     'at'?
00596 
00597 Spanish also uses:
00598 \arg \b vm-youhaveno
00599 
00600 Italian requires the following additional soundfile:
00601 
00602 For vm_intro_it:
00603 \arg \b vm-nuovo  new
00604 \arg \b vm-nuovi  new plural
00605 \arg \b vm-vecchio   old
00606 \arg \b vm-vecchi old plural
00607 
00608 Chinese (Taiwan) requires the following additional soundfile:
00609 \arg \b vm-tong      A class-word for call (tong1)
00610 \arg \b vm-ri     A class-word for day (ri4)
00611 \arg \b vm-you    You (ni3)
00612 \arg \b vm-haveno   Have no (mei2 you3)
00613 \arg \b vm-have     Have (you3)
00614 \arg \b vm-listen   To listen (yao4 ting1)
00615 
00616 
00617 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00618 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00619 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00620 
00621 */
00622 
00623 struct baseio {
00624    int iocp;
00625    int iolen;
00626    int linelength;
00627    int ateof;
00628    unsigned char iobuf[BASEMAXINLINE];
00629 };
00630 
00631 /*! Structure for linked list of users 
00632  * Use ast_vm_user_destroy() to free one of these structures. */
00633 struct ast_vm_user {
00634    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00635    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00636    char password[80];               /*!< Secret pin code, numbers only */
00637    char fullname[80];               /*!< Full name, for directory app */
00638    char email[80];                  /*!< E-mail address */
00639    char *emailsubject;              /*!< E-mail subject */
00640    char *emailbody;                 /*!< E-mail body */
00641    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00642    char serveremail[80];            /*!< From: Mail address */
00643    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00644    char zonetag[80];                /*!< Time zone */
00645    char locale[20];                 /*!< The locale (for presentation of date/time) */
00646    char callback[80];
00647    char dialout[80];
00648    char uniqueid[80];               /*!< Unique integer identifier */
00649    char exit[80];
00650    char attachfmt[20];              /*!< Attachment format */
00651    unsigned int flags;              /*!< VM_ flags */ 
00652    int saydurationm;
00653    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00654    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00655    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00656    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00657    int passwordlocation;            /*!< Storage location of the password */
00658 #ifdef IMAP_STORAGE
00659    char imapuser[80];               /*!< IMAP server login */
00660    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00661    char imapfolder[64];             /*!< IMAP voicemail folder */
00662    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00663    int imapversion;                 /*!< If configuration changes, use the new values */
00664 #endif
00665    double volgain;                  /*!< Volume gain for voicemails sent via email */
00666    AST_LIST_ENTRY(ast_vm_user) list;
00667 };
00668 
00669 /*! Voicemail time zones */
00670 struct vm_zone {
00671    AST_LIST_ENTRY(vm_zone) list;
00672    char name[80];
00673    char timezone[80];
00674    char msg_format[512];
00675 };
00676 
00677 #define VMSTATE_MAX_MSG_ARRAY 256
00678 
00679 /*! Voicemail mailbox state */
00680 struct vm_state {
00681    char curbox[80];
00682    char username[80];
00683    char context[80];
00684    char curdir[PATH_MAX];
00685    char vmbox[PATH_MAX];
00686    char fn[PATH_MAX];
00687    char intro[PATH_MAX];
00688    int *deleted;
00689    int *heard;
00690    int dh_arraysize; /* used for deleted / heard allocation */
00691    int curmsg;
00692    int lastmsg;
00693    int newmessages;
00694    int oldmessages;
00695    int urgentmessages;
00696    int starting;
00697    int repeats;
00698 #ifdef IMAP_STORAGE
00699    ast_mutex_t lock;
00700    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00701    long *msgArray;
00702    unsigned msg_array_max;
00703    MAILSTREAM *mailstream;
00704    int vmArrayIndex;
00705    char imapuser[80];                   /*!< IMAP server login */
00706    char imapfolder[64];                 /*!< IMAP voicemail folder */
00707    int imapversion;
00708    int interactive;
00709    char introfn[PATH_MAX];              /*!< Name of prepended file */
00710    unsigned int quota_limit;
00711    unsigned int quota_usage;
00712    struct vm_state *persist_vms;
00713 #endif
00714 };
00715 
00716 #ifdef ODBC_STORAGE
00717 static char odbc_database[80];
00718 static char odbc_table[80];
00719 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00720 #define DISPOSE(a,b) remove_file(a,b)
00721 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00722 #define EXISTS(a,b,c,d) (message_exists(a,b))
00723 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00724 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00725 #define DELETE(a,b,c,d) (delete_file(a,b))
00726 #else
00727 #ifdef IMAP_STORAGE
00728 #define DISPOSE(a,b) (imap_remove_file(a,b))
00729 #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))
00730 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00731 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00732 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00733 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00734 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00735 #else
00736 #define RETRIEVE(a,b,c,d)
00737 #define DISPOSE(a,b)
00738 #define STORE(a,b,c,d,e,f,g,h,i,j)
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_plain_file(g,h)); 
00742 #define DELETE(a,b,c,d) (vm_delete(c))
00743 #endif
00744 #endif
00745 
00746 static char VM_SPOOL_DIR[PATH_MAX];
00747 
00748 static char ext_pass_cmd[128];
00749 static char ext_pass_check_cmd[128];
00750 
00751 static int my_umask;
00752 
00753 #define PWDCHANGE_INTERNAL (1 << 1)
00754 #define PWDCHANGE_EXTERNAL (1 << 2)
00755 static int pwdchange = PWDCHANGE_INTERNAL;
00756 
00757 #ifdef ODBC_STORAGE
00758 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00759 #else
00760 # ifdef IMAP_STORAGE
00761 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00762 # else
00763 # define tdesc "Comedian Mail (Voicemail System)"
00764 # endif
00765 #endif
00766 
00767 static char userscontext[AST_MAX_EXTENSION] = "default";
00768 
00769 static char *addesc = "Comedian Mail";
00770 
00771 /* Leave a message */
00772 static char *app = "VoiceMail";
00773 
00774 /* Check mail, control, etc */
00775 static char *app2 = "VoiceMailMain";
00776 
00777 static char *app3 = "MailboxExists";
00778 static char *app4 = "VMAuthenticate";
00779 
00780 static char *sayname_app = "VMSayName";
00781 
00782 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00783 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00784 static char zonetag[80];
00785 static char locale[20];
00786 static int maxsilence;
00787 static int maxmsg;
00788 static int maxdeletedmsg;
00789 static int silencethreshold = 128;
00790 static char serveremail[80];
00791 static char mailcmd[160];  /* Configurable mail cmd */
00792 static char externnotify[160]; 
00793 static struct ast_smdi_interface *smdi_iface = NULL;
00794 static char vmfmts[80];
00795 static double volgain;
00796 static int vmminsecs;
00797 static int vmmaxsecs;
00798 static int maxgreet;
00799 static int skipms;
00800 static int maxlogins;
00801 static int minpassword;
00802 static int passwordlocation;
00803 
00804 /*! Poll mailboxes for changes since there is something external to
00805  *  app_voicemail that may change them. */
00806 static unsigned int poll_mailboxes;
00807 
00808 /*! Polling frequency */
00809 static unsigned int poll_freq;
00810 /*! By default, poll every 30 seconds */
00811 #define DEFAULT_POLL_FREQ 30
00812 
00813 AST_MUTEX_DEFINE_STATIC(poll_lock);
00814 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00815 static pthread_t poll_thread = AST_PTHREADT_NULL;
00816 static unsigned char poll_thread_run;
00817 
00818 /*! Subscription to ... MWI event subscriptions */
00819 static struct ast_event_sub *mwi_sub_sub;
00820 /*! Subscription to ... MWI event un-subscriptions */
00821 static struct ast_event_sub *mwi_unsub_sub;
00822 
00823 /*!
00824  * \brief An MWI subscription
00825  *
00826  * This is so we can keep track of which mailboxes are subscribed to.
00827  * This way, we know which mailboxes to poll when the pollmailboxes
00828  * option is being used.
00829  */
00830 struct mwi_sub {
00831    AST_RWLIST_ENTRY(mwi_sub) entry;
00832    int old_urgent;
00833    int old_new;
00834    int old_old;
00835    uint32_t uniqueid;
00836    char mailbox[1];
00837 };
00838 
00839 struct mwi_sub_task {
00840    const char *mailbox;
00841    const char *context;
00842    uint32_t uniqueid;
00843 };
00844 
00845 static struct ast_taskprocessor *mwi_subscription_tps;
00846 
00847 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00848 
00849 /* custom audio control prompts for voicemail playback */
00850 static char listen_control_forward_key[12];
00851 static char listen_control_reverse_key[12];
00852 static char listen_control_pause_key[12];
00853 static char listen_control_restart_key[12];
00854 static char listen_control_stop_key[12];
00855 
00856 /* custom password sounds */
00857 static char vm_password[80] = "vm-password";
00858 static char vm_newpassword[80] = "vm-newpassword";
00859 static char vm_passchanged[80] = "vm-passchanged";
00860 static char vm_reenterpassword[80] = "vm-reenterpassword";
00861 static char vm_mismatch[80] = "vm-mismatch";
00862 static char vm_invalid_password[80] = "vm-invalid-password";
00863 static char vm_pls_try_again[80] = "vm-pls-try-again";
00864 
00865 /*
00866  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00867  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00868  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00869  * app.c's __ast_play_and_record function
00870  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00871  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00872  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00873  * more effort than either of the other two.
00874  */
00875 static char vm_prepend_timeout[80] = "vm-then-pound";
00876 
00877 static struct ast_flags globalflags = {0};
00878 
00879 static int saydurationminfo;
00880 
00881 static char dialcontext[AST_MAX_CONTEXT] = "";
00882 static char callcontext[AST_MAX_CONTEXT] = "";
00883 static char exitcontext[AST_MAX_CONTEXT] = "";
00884 
00885 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00886 
00887 
00888 static char *emailbody = NULL;
00889 static char *emailsubject = NULL;
00890 static char *pagerbody = NULL;
00891 static char *pagersubject = NULL;
00892 static char fromstring[100];
00893 static char pagerfromstring[100];
00894 static char charset[32] = "ISO-8859-1";
00895 
00896 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00897 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00898 static int adsiver = 1;
00899 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00900 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00901 
00902 /* Forward declarations - generic */
00903 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00904 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);
00905 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00906 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00907          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00908          signed char record_gain, struct vm_state *vms, char *flag);
00909 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00910 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00911 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);
00912 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);
00913 static void apply_options(struct ast_vm_user *vmu, const char *options);
00914 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);
00915 static int is_valid_dtmf(const char *key);
00916 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00917 static int write_password_to_file(const char *secretfn, const char *password);
00918 static const char *substitute_escapes(const char *value);
00919 static void free_user(struct ast_vm_user *vmu);
00920 
00921 struct ao2_container *inprocess_container;
00922 
00923 struct inprocess {
00924    int count;
00925    char *context;
00926    char mailbox[0];
00927 };
00928 
00929 static int inprocess_hash_fn(const void *obj, const int flags)
00930 {
00931    const struct inprocess *i = obj;
00932    return atoi(i->mailbox);
00933 }
00934 
00935 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00936 {
00937    struct inprocess *i = obj, *j = arg;
00938    if (strcmp(i->mailbox, j->mailbox)) {
00939       return 0;
00940    }
00941    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00942 }
00943 
00944 static int inprocess_count(const char *context, const char *mailbox, int delta)
00945 {
00946    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00947    arg->context = arg->mailbox + strlen(mailbox) + 1;
00948    strcpy(arg->mailbox, mailbox); /* SAFE */
00949    strcpy(arg->context, context); /* SAFE */
00950    ao2_lock(inprocess_container);
00951    if ((i = ao2_find(inprocess_container, arg, 0))) {
00952       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00953       ao2_unlock(inprocess_container);
00954       ao2_ref(i, -1);
00955       return ret;
00956    }
00957    if (delta < 0) {
00958       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00959    }
00960    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00961       ao2_unlock(inprocess_container);
00962       return 0;
00963    }
00964    i->context = i->mailbox + strlen(mailbox) + 1;
00965    strcpy(i->mailbox, mailbox); /* SAFE */
00966    strcpy(i->context, context); /* SAFE */
00967    i->count = delta;
00968    ao2_link(inprocess_container, i);
00969    ao2_unlock(inprocess_container);
00970    ao2_ref(i, -1);
00971    return 0;
00972 }
00973 
00974 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00975 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00976 #endif
00977 
00978 /*!
00979  * \brief Strips control and non 7-bit clean characters from input string.
00980  *
00981  * \note To map control and none 7-bit characters to a 7-bit clean characters
00982  *  please use ast_str_encode_mine().
00983  */
00984 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00985 {
00986    char *bufptr = buf;
00987    for (; *input; input++) {
00988       if (*input < 32) {
00989          continue;
00990       }
00991       *bufptr++ = *input;
00992       if (bufptr == buf + buflen - 1) {
00993          break;
00994       }
00995    }
00996    *bufptr = '\0';
00997    return buf;
00998 }
00999 
01000 
01001 /*!
01002  * \brief Sets default voicemail system options to a voicemail user.
01003  *
01004  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01005  * - all the globalflags
01006  * - the saydurationminfo
01007  * - the callcontext
01008  * - the dialcontext
01009  * - the exitcontext
01010  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01011  * - volume gain
01012  * - emailsubject, emailbody set to NULL
01013  */
01014 static void populate_defaults(struct ast_vm_user *vmu)
01015 {
01016    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01017    vmu->passwordlocation = passwordlocation;
01018    if (saydurationminfo) {
01019       vmu->saydurationm = saydurationminfo;
01020    }
01021    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01022    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01023    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01024    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01025    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01026    if (vmminsecs) {
01027       vmu->minsecs = vmminsecs;
01028    }
01029    if (vmmaxsecs) {
01030       vmu->maxsecs = vmmaxsecs;
01031    }
01032    if (maxmsg) {
01033       vmu->maxmsg = maxmsg;
01034    }
01035    if (maxdeletedmsg) {
01036       vmu->maxdeletedmsg = maxdeletedmsg;
01037    }
01038    vmu->volgain = volgain;
01039    ast_free(vmu->emailsubject);
01040    vmu->emailsubject = NULL;
01041    ast_free(vmu->emailbody);
01042    vmu->emailbody = NULL;
01043 #ifdef IMAP_STORAGE
01044    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01045 #endif
01046 }
01047 
01048 /*!
01049  * \brief Sets a a specific property value.
01050  * \param vmu The voicemail user object to work with.
01051  * \param var The name of the property to be set.
01052  * \param value The value to be set to the property.
01053  * 
01054  * The property name must be one of the understood properties. See the source for details.
01055  */
01056 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01057 {
01058    int x;
01059    if (!strcasecmp(var, "attach")) {
01060       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01061    } else if (!strcasecmp(var, "attachfmt")) {
01062       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01063    } else if (!strcasecmp(var, "serveremail")) {
01064       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01065    } else if (!strcasecmp(var, "emailbody")) {
01066       vmu->emailbody = ast_strdup(substitute_escapes(value));
01067    } else if (!strcasecmp(var, "emailsubject")) {
01068       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01069    } else if (!strcasecmp(var, "language")) {
01070       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01071    } else if (!strcasecmp(var, "tz")) {
01072       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01073    } else if (!strcasecmp(var, "locale")) {
01074       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01075 #ifdef IMAP_STORAGE
01076    } else if (!strcasecmp(var, "imapuser")) {
01077       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01078       vmu->imapversion = imapversion;
01079    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01080       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01081       vmu->imapversion = imapversion;
01082    } else if (!strcasecmp(var, "imapfolder")) {
01083       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01084    } else if (!strcasecmp(var, "imapvmshareid")) {
01085       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01086       vmu->imapversion = imapversion;
01087 #endif
01088    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01089       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01090    } else if (!strcasecmp(var, "saycid")){
01091       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01092    } else if (!strcasecmp(var, "sendvoicemail")){
01093       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01094    } else if (!strcasecmp(var, "review")){
01095       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01096    } else if (!strcasecmp(var, "tempgreetwarn")){
01097       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01098    } else if (!strcasecmp(var, "messagewrap")){
01099       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01100    } else if (!strcasecmp(var, "operator")) {
01101       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01102    } else if (!strcasecmp(var, "envelope")){
01103       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01104    } else if (!strcasecmp(var, "moveheard")){
01105       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01106    } else if (!strcasecmp(var, "sayduration")){
01107       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01108    } else if (!strcasecmp(var, "saydurationm")){
01109       if (sscanf(value, "%30d", &x) == 1) {
01110          vmu->saydurationm = x;
01111       } else {
01112          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01113       }
01114    } else if (!strcasecmp(var, "forcename")){
01115       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01116    } else if (!strcasecmp(var, "forcegreetings")){
01117       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01118    } else if (!strcasecmp(var, "callback")) {
01119       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01120    } else if (!strcasecmp(var, "dialout")) {
01121       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01122    } else if (!strcasecmp(var, "exitcontext")) {
01123       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01124    } else if (!strcasecmp(var, "minsecs")) {
01125       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01126          vmu->minsecs = x;
01127       } else {
01128          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01129          vmu->minsecs = vmminsecs;
01130       }
01131    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01132       vmu->maxsecs = atoi(value);
01133       if (vmu->maxsecs <= 0) {
01134          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01135          vmu->maxsecs = vmmaxsecs;
01136       } else {
01137          vmu->maxsecs = atoi(value);
01138       }
01139       if (!strcasecmp(var, "maxmessage"))
01140          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01141    } else if (!strcasecmp(var, "maxmsg")) {
01142       vmu->maxmsg = atoi(value);
01143       /* Accept maxmsg=0 (Greetings only voicemail) */
01144       if (vmu->maxmsg < 0) {
01145          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01146          vmu->maxmsg = MAXMSG;
01147       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01148          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01149          vmu->maxmsg = MAXMSGLIMIT;
01150       }
01151    } else if (!strcasecmp(var, "nextaftercmd")) {
01152       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01153    } else if (!strcasecmp(var, "backupdeleted")) {
01154       if (sscanf(value, "%30d", &x) == 1)
01155          vmu->maxdeletedmsg = x;
01156       else if (ast_true(value))
01157          vmu->maxdeletedmsg = MAXMSG;
01158       else
01159          vmu->maxdeletedmsg = 0;
01160 
01161       if (vmu->maxdeletedmsg < 0) {
01162          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01163          vmu->maxdeletedmsg = MAXMSG;
01164       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01165          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01166          vmu->maxdeletedmsg = MAXMSGLIMIT;
01167       }
01168    } else if (!strcasecmp(var, "volgain")) {
01169       sscanf(value, "%30lf", &vmu->volgain);
01170    } else if (!strcasecmp(var, "passwordlocation")) {
01171       if (!strcasecmp(value, "spooldir")) {
01172          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01173       } else {
01174          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01175       }
01176    } else if (!strcasecmp(var, "options")) {
01177       apply_options(vmu, value);
01178    }
01179 }
01180 
01181 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01182 {
01183    int fds[2], pid = 0;
01184 
01185    memset(buf, 0, len);
01186 
01187    if (pipe(fds)) {
01188       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01189    } else {
01190       /* good to go*/
01191       pid = ast_safe_fork(0);
01192 
01193       if (pid < 0) {
01194          /* ok maybe not */
01195          close(fds[0]);
01196          close(fds[1]);
01197          snprintf(buf, len, "FAILURE: Fork failed");
01198       } else if (pid) {
01199          /* parent */
01200          close(fds[1]);
01201          if (read(fds[0], buf, len) < 0) {
01202             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01203          }
01204          close(fds[0]);
01205       } else {
01206          /*  child */
01207          AST_DECLARE_APP_ARGS(arg,
01208             AST_APP_ARG(v)[20];
01209          );
01210          char *mycmd = ast_strdupa(command);
01211 
01212          close(fds[0]);
01213          dup2(fds[1], STDOUT_FILENO);
01214          close(fds[1]);
01215          ast_close_fds_above_n(STDOUT_FILENO);
01216 
01217          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01218 
01219          execv(arg.v[0], arg.v); 
01220          printf("FAILURE: %s", strerror(errno));
01221          _exit(0);
01222       }
01223    }
01224    return buf;
01225 }
01226 
01227 /*!
01228  * \brief Check that password meets minimum required length
01229  * \param vmu The voicemail user to change the password for.
01230  * \param password The password string to check
01231  *
01232  * \return zero on ok, 1 on not ok.
01233  */
01234 static int check_password(struct ast_vm_user *vmu, char *password)
01235 {
01236    /* check minimum length */
01237    if (strlen(password) < minpassword)
01238       return 1;
01239    /* check that password does not contain '*' character */
01240    if (!ast_strlen_zero(password) && password[0] == '*')
01241       return 1;
01242    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01243       char cmd[255], buf[255];
01244 
01245       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01246 
01247       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01248       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01249          ast_debug(5, "Result: %s\n", buf);
01250          if (!strncasecmp(buf, "VALID", 5)) {
01251             ast_debug(3, "Passed password check: '%s'\n", buf);
01252             return 0;
01253          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01254             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01255             return 0;
01256          } else {
01257             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01258             return 1;
01259          }
01260       }
01261    }
01262    return 0;
01263 }
01264 
01265 /*! 
01266  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01267  * \param vmu The voicemail user to change the password for.
01268  * \param password The new value to be set to the password for this user.
01269  * 
01270  * This only works if there is a realtime engine configured.
01271  * This is called from the (top level) vm_change_password.
01272  *
01273  * \return zero on success, -1 on error.
01274  */
01275 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01276 {
01277    int res = -1;
01278    if (!strcmp(vmu->password, password)) {
01279       /* No change (but an update would return 0 rows updated, so we opt out here) */
01280       return 0;
01281    }
01282 
01283    if (strlen(password) > 10) {
01284       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01285    }
01286    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01287       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01288       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01289       res = 0;
01290    }
01291    return res;
01292 }
01293 
01294 /*!
01295  * \brief Destructively Parse options and apply.
01296  */
01297 static void apply_options(struct ast_vm_user *vmu, const char *options)
01298 {  
01299    char *stringp;
01300    char *s;
01301    char *var, *value;
01302    stringp = ast_strdupa(options);
01303    while ((s = strsep(&stringp, "|"))) {
01304       value = s;
01305       if ((var = strsep(&value, "=")) && value) {
01306          apply_option(vmu, var, value);
01307       }
01308    }  
01309 }
01310 
01311 /*!
01312  * \brief Loads the options specific to a voicemail user.
01313  * 
01314  * This is called when a vm_user structure is being set up, such as from load_options.
01315  */
01316 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01317 {
01318    for (; var; var = var->next) {
01319       if (!strcasecmp(var->name, "vmsecret")) {
01320          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01321       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01322          if (ast_strlen_zero(retval->password)) {
01323             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01324                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01325                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01326             } else {
01327                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01328             }
01329          }
01330       } else if (!strcasecmp(var->name, "uniqueid")) {
01331          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01332       } else if (!strcasecmp(var->name, "pager")) {
01333          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01334       } else if (!strcasecmp(var->name, "email")) {
01335          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01336       } else if (!strcasecmp(var->name, "fullname")) {
01337          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01338       } else if (!strcasecmp(var->name, "context")) {
01339          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01340       } else if (!strcasecmp(var->name, "emailsubject")) {
01341          ast_free(retval->emailsubject);
01342          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01343       } else if (!strcasecmp(var->name, "emailbody")) {
01344          ast_free(retval->emailbody);
01345          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01346 #ifdef IMAP_STORAGE
01347       } else if (!strcasecmp(var->name, "imapuser")) {
01348          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01349          retval->imapversion = imapversion;
01350       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01351          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01352          retval->imapversion = imapversion;
01353       } else if (!strcasecmp(var->name, "imapfolder")) {
01354          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01355       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01356          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01357          retval->imapversion = imapversion;
01358 #endif
01359       } else
01360          apply_option(retval, var->name, var->value);
01361    }
01362 }
01363 
01364 /*!
01365  * \brief Determines if a DTMF key entered is valid.
01366  * \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.
01367  *
01368  * Tests the character entered against the set of valid DTMF characters. 
01369  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01370  */
01371 static int is_valid_dtmf(const char *key)
01372 {
01373    int i;
01374    char *local_key = ast_strdupa(key);
01375 
01376    for (i = 0; i < strlen(key); ++i) {
01377       if (!strchr(VALID_DTMF, *local_key)) {
01378          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01379          return 0;
01380       }
01381       local_key++;
01382    }
01383    return 1;
01384 }
01385 
01386 /*!
01387  * \brief Finds a voicemail user from the realtime engine.
01388  * \param ivm
01389  * \param context
01390  * \param mailbox
01391  *
01392  * 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.
01393  *
01394  * \return The ast_vm_user structure for the user that was found.
01395  */
01396 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01397 {
01398    struct ast_variable *var;
01399    struct ast_vm_user *retval;
01400 
01401    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01402       if (ivm) {
01403          memset(retval, 0, sizeof(*retval));
01404       }
01405       populate_defaults(retval);
01406       if (!ivm) {
01407          ast_set_flag(retval, VM_ALLOCED);
01408       }
01409       if (mailbox) {
01410          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01411       }
01412       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01413          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01414       } else {
01415          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01416       }
01417       if (var) {
01418          apply_options_full(retval, var);
01419          ast_variables_destroy(var);
01420       } else { 
01421          if (!ivm) 
01422             free_user(retval);
01423          retval = NULL;
01424       }  
01425    } 
01426    return retval;
01427 }
01428 
01429 /*!
01430  * \brief Finds a voicemail user from the users file or the realtime engine.
01431  * \param ivm
01432  * \param context
01433  * \param mailbox
01434  * 
01435  * \return The ast_vm_user structure for the user that was found.
01436  */
01437 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01438 {
01439    /* This function could be made to generate one from a database, too */
01440    struct ast_vm_user *vmu = NULL, *cur;
01441    AST_LIST_LOCK(&users);
01442 
01443    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01444       context = "default";
01445 
01446    AST_LIST_TRAVERSE(&users, cur, list) {
01447 #ifdef IMAP_STORAGE
01448       if (cur->imapversion != imapversion) {
01449          continue;
01450       }
01451 #endif
01452       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01453          break;
01454       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01455          break;
01456    }
01457    if (cur) {
01458       /* Make a copy, so that on a reload, we have no race */
01459       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01460          *vmu = *cur;
01461          if (!ivm) {
01462             vmu->emailbody = ast_strdup(cur->emailbody);
01463             vmu->emailsubject = ast_strdup(cur->emailsubject);
01464          }
01465          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01466          AST_LIST_NEXT(vmu, list) = NULL;
01467       }
01468    } else
01469       vmu = find_user_realtime(ivm, context, mailbox);
01470    AST_LIST_UNLOCK(&users);
01471    return vmu;
01472 }
01473 
01474 /*!
01475  * \brief Resets a user password to a specified password.
01476  * \param context
01477  * \param mailbox
01478  * \param newpass
01479  *
01480  * This does the actual change password work, called by the vm_change_password() function.
01481  *
01482  * \return zero on success, -1 on error.
01483  */
01484 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01485 {
01486    /* This function could be made to generate one from a database, too */
01487    struct ast_vm_user *cur;
01488    int res = -1;
01489    AST_LIST_LOCK(&users);
01490    AST_LIST_TRAVERSE(&users, cur, list) {
01491       if ((!context || !strcasecmp(context, cur->context)) &&
01492          (!strcasecmp(mailbox, cur->mailbox)))
01493             break;
01494    }
01495    if (cur) {
01496       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01497       res = 0;
01498    }
01499    AST_LIST_UNLOCK(&users);
01500    return res;
01501 }
01502 
01503 /*!
01504  * \brief Check if configuration file is valid
01505  */
01506 static inline int valid_config(const struct ast_config *cfg)
01507 {
01508    return cfg && cfg != CONFIG_STATUS_FILEINVALID;
01509 }
01510 
01511 /*! 
01512  * \brief The handler for the change password option.
01513  * \param vmu The voicemail user to work with.
01514  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01515  * 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.
01516  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01517  */
01518 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01519 {
01520    struct ast_config   *cfg = NULL;
01521    struct ast_variable *var = NULL;
01522    struct ast_category *cat = NULL;
01523    char *category = NULL, *value = NULL, *new = NULL;
01524    const char *tmp = NULL;
01525    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01526    char secretfn[PATH_MAX] = "";
01527    int found = 0;
01528 
01529    if (!change_password_realtime(vmu, newpassword))
01530       return;
01531 
01532    /* check if we should store the secret in the spool directory next to the messages */
01533    switch (vmu->passwordlocation) {
01534    case OPT_PWLOC_SPOOLDIR:
01535       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01536       if (write_password_to_file(secretfn, newpassword) == 0) {
01537          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01538          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01539          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01540          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01541          break;
01542       } else {
01543          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01544       }
01545       /* Fall-through */
01546    case OPT_PWLOC_VOICEMAILCONF:
01547       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
01548          while ((category = ast_category_browse(cfg, category))) {
01549             if (!strcasecmp(category, vmu->context)) {
01550                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01551                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01552                   break;
01553                }
01554                value = strstr(tmp, ",");
01555                if (!value) {
01556                   new = ast_alloca(strlen(newpassword)+1);
01557                   sprintf(new, "%s", newpassword);
01558                } else {
01559                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01560                   sprintf(new, "%s%s", newpassword, value);
01561                }
01562                if (!(cat = ast_category_get(cfg, category))) {
01563                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01564                   break;
01565                }
01566                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01567                found = 1;
01568             }
01569          }
01570          /* save the results */
01571          if (found) {
01572             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01573             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01574             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01575             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01576             ast_config_destroy(cfg);
01577             break;
01578          }
01579 
01580          ast_config_destroy(cfg);
01581       }
01582       /* Fall-through */
01583    case OPT_PWLOC_USERSCONF:
01584       /* check users.conf and update the password stored for the mailbox */
01585       /* if no vmsecret entry exists create one. */
01586       if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
01587          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01588          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01589             ast_debug(4, "users.conf: %s\n", category);
01590             if (!strcasecmp(category, vmu->mailbox)) {
01591                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01592                   ast_debug(3, "looks like we need to make vmsecret!\n");
01593                   var = ast_variable_new("vmsecret", newpassword, "");
01594                } else {
01595                   var = NULL;
01596                }
01597                new = ast_alloca(strlen(newpassword) + 1);
01598                sprintf(new, "%s", newpassword);
01599                if (!(cat = ast_category_get(cfg, category))) {
01600                   ast_debug(4, "failed to get category!\n");
01601                   ast_free(var);
01602                   break;
01603                }
01604                if (!var) {
01605                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01606                } else {
01607                   ast_variable_append(cat, var);
01608                }
01609                found = 1;
01610                break;
01611             }
01612          }
01613          /* save the results and clean things up */
01614          if (found) {
01615             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01616             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01617             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01618             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01619          }
01620 
01621          ast_config_destroy(cfg);
01622       }
01623    }
01624 }
01625 
01626 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01627 {
01628    char buf[255];
01629    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01630    ast_debug(1, "External password: %s\n",buf);
01631    if (!ast_safe_system(buf)) {
01632       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01633       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01634       /* Reset the password in memory, too */
01635       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01636    }
01637 }
01638 
01639 /*! 
01640  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01641  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01642  * \param len The length of the path string that was written out.
01643  * \param context
01644  * \param ext 
01645  * \param folder 
01646  * 
01647  * The path is constructed as 
01648  *    VM_SPOOL_DIRcontext/ext/folder
01649  *
01650  * \return zero on success, -1 on error.
01651  */
01652 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01653 {
01654    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01655 }
01656 
01657 /*! 
01658  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01659  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01660  * \param len The length of the path string that was written out.
01661  * \param dir 
01662  * \param num 
01663  * 
01664  * The path is constructed as 
01665  *    VM_SPOOL_DIRcontext/ext/folder
01666  *
01667  * \return zero on success, -1 on error.
01668  */
01669 static int make_file(char *dest, const int len, const char *dir, const int num)
01670 {
01671    return snprintf(dest, len, "%s/msg%04d", dir, num);
01672 }
01673 
01674 /* same as mkstemp, but return a FILE * */
01675 static FILE *vm_mkftemp(char *template)
01676 {
01677    FILE *p = NULL;
01678    int pfd = mkstemp(template);
01679    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01680    if (pfd > -1) {
01681       p = fdopen(pfd, "w+");
01682       if (!p) {
01683          close(pfd);
01684          pfd = -1;
01685       }
01686    }
01687    return p;
01688 }
01689 
01690 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01691  * \param dest    String. base directory.
01692  * \param len     Length of dest.
01693  * \param context String. Ignored if is null or empty string.
01694  * \param ext     String. Ignored if is null or empty string.
01695  * \param folder  String. Ignored if is null or empty string. 
01696  * \return -1 on failure, 0 on success.
01697  */
01698 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01699 {
01700    mode_t   mode = VOICEMAIL_DIR_MODE;
01701    int res;
01702 
01703    make_dir(dest, len, context, ext, folder);
01704    if ((res = ast_mkdir(dest, mode))) {
01705       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01706       return -1;
01707    }
01708    return 0;
01709 }
01710 
01711 static const char * const mailbox_folders[] = {
01712 #ifdef IMAP_STORAGE
01713    imapfolder,
01714 #else
01715    "INBOX",
01716 #endif
01717    "Old",
01718    "Work",
01719    "Family",
01720    "Friends",
01721    "Cust1",
01722    "Cust2",
01723    "Cust3",
01724    "Cust4",
01725    "Cust5",
01726    "Deleted",
01727    "Urgent",
01728 };
01729 
01730 static const char *mbox(struct ast_vm_user *vmu, int id)
01731 {
01732 #ifdef IMAP_STORAGE
01733    if (vmu && id == 0) {
01734       return vmu->imapfolder;
01735    }
01736 #endif
01737    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01738 }
01739 
01740 static int get_folder_by_name(const char *name)
01741 {
01742    size_t i;
01743 
01744    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01745       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01746          return i;
01747       }
01748    }
01749 
01750    return -1;
01751 }
01752 
01753 static void free_user(struct ast_vm_user *vmu)
01754 {
01755    if (ast_test_flag(vmu, VM_ALLOCED)) {
01756 
01757       ast_free(vmu->emailbody);
01758       vmu->emailbody = NULL;
01759 
01760       ast_free(vmu->emailsubject);
01761       vmu->emailsubject = NULL;
01762 
01763       ast_free(vmu);
01764    }
01765 }
01766 
01767 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01768 
01769    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01770 
01771    /* remove old allocation */
01772    if (vms->deleted) {
01773       ast_free(vms->deleted);
01774       vms->deleted = NULL;
01775    }
01776    if (vms->heard) {
01777       ast_free(vms->heard);
01778       vms->heard = NULL;
01779    }
01780    vms->dh_arraysize = 0;
01781 
01782    if (arraysize > 0) {
01783       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01784          return -1;
01785       }
01786       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01787          ast_free(vms->deleted);
01788          vms->deleted = NULL;
01789          return -1;
01790       }
01791       vms->dh_arraysize = arraysize;
01792    }
01793 
01794    return 0;
01795 }
01796 
01797 /* All IMAP-specific functions should go in this block. This
01798  * keeps them from being spread out all over the code */
01799 #ifdef IMAP_STORAGE
01800 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01801 {
01802    char arg[10];
01803    struct vm_state *vms;
01804    unsigned long messageNum;
01805 
01806    /* If greetings aren't stored in IMAP, just delete the file */
01807    if (msgnum < 0 && !imapgreetings) {
01808       ast_filedelete(file, NULL);
01809       return;
01810    }
01811 
01812    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01813       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);
01814       return;
01815    }
01816 
01817    if (msgnum < 0) {
01818       imap_delete_old_greeting(file, vms);
01819       return;
01820    }
01821 
01822    /* find real message number based on msgnum */
01823    /* this may be an index into vms->msgArray based on the msgnum. */
01824    messageNum = vms->msgArray[msgnum];
01825    if (messageNum == 0) {
01826       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01827       return;
01828    }
01829    if (option_debug > 2)
01830       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01831    /* delete message */
01832    snprintf (arg, sizeof(arg), "%lu", messageNum);
01833    ast_mutex_lock(&vms->lock);
01834    mail_setflag (vms->mailstream, arg, "\\DELETED");
01835    mail_expunge(vms->mailstream);
01836    ast_mutex_unlock(&vms->lock);
01837 }
01838 
01839 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01840 {
01841    struct vm_state *vms_p;
01842    char *file, *filename;
01843    char *attachment;
01844    int i;
01845    BODY *body;
01846 
01847    /* This function is only used for retrieval of IMAP greetings
01848     * regular messages are not retrieved this way, nor are greetings
01849     * if they are stored locally*/
01850    if (msgnum > -1 || !imapgreetings) {
01851       return 0;
01852    } else {
01853       file = strrchr(ast_strdupa(dir), '/');
01854       if (file)
01855          *file++ = '\0';
01856       else {
01857          ast_debug (1, "Failed to procure file name from directory passed.\n");
01858          return -1;
01859       }
01860    }
01861 
01862    /* check if someone is accessing this box right now... */
01863    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01864       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01865       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01866       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01867       * that's all we need to do.
01868       */
01869       if (!(vms_p = create_vm_state_from_user(vmu))) {
01870          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01871          return -1;
01872       }
01873    }
01874 
01875    /* Greetings will never have a prepended message */
01876    *vms_p->introfn = '\0';
01877 
01878    ast_mutex_lock(&vms_p->lock);
01879    init_mailstream(vms_p, GREETINGS_FOLDER);
01880    if (!vms_p->mailstream) {
01881       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01882       ast_mutex_unlock(&vms_p->lock);
01883       return -1;
01884    }
01885 
01886    /*XXX Yuck, this could probably be done a lot better */
01887    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01888       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01889       /* We have the body, now we extract the file name of the first attachment. */
01890       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01891          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01892       } else {
01893          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01894          ast_mutex_unlock(&vms_p->lock);
01895          return -1;
01896       }
01897       filename = strsep(&attachment, ".");
01898       if (!strcmp(filename, file)) {
01899          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01900          vms_p->msgArray[vms_p->curmsg] = i + 1;
01901          save_body(body, vms_p, "2", attachment, 0);
01902          ast_mutex_unlock(&vms_p->lock);
01903          return 0;
01904       }
01905    }
01906    ast_mutex_unlock(&vms_p->lock);
01907 
01908    return -1;
01909 }
01910 
01911 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01912 {
01913    BODY *body;
01914    char *header_content;
01915    char *attachedfilefmt;
01916    char buf[80];
01917    struct vm_state *vms;
01918    char text_file[PATH_MAX];
01919    FILE *text_file_ptr;
01920    int res = 0;
01921    struct ast_vm_user *vmu;
01922 
01923    if (!(vmu = find_user(NULL, context, mailbox))) {
01924       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01925       return -1;
01926    }
01927    
01928    if (msgnum < 0) {
01929       if (imapgreetings) {
01930          res = imap_retrieve_greeting(dir, msgnum, vmu);
01931          goto exit;
01932       } else {
01933          res = 0;
01934          goto exit;
01935       }
01936    }
01937 
01938    /* Before anything can happen, we need a vm_state so that we can
01939     * actually access the imap server through the vms->mailstream
01940     */
01941    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01942       /* This should not happen. If it does, then I guess we'd
01943        * need to create the vm_state, extract which mailbox to
01944        * open, and then set up the msgArray so that the correct
01945        * IMAP message could be accessed. If I have seen correctly
01946        * though, the vms should be obtainable from the vmstates list
01947        * and should have its msgArray properly set up.
01948        */
01949       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01950       res = -1;
01951       goto exit;
01952    }
01953    
01954    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01955    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01956 
01957    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01958    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01959       res = 0;
01960       goto exit;
01961    }
01962 
01963    if (option_debug > 2)
01964       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01965    if (vms->msgArray[msgnum] == 0) {
01966       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01967       res = -1;
01968       goto exit;
01969    }
01970 
01971    /* This will only work for new messages... */
01972    ast_mutex_lock(&vms->lock);
01973    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01974    ast_mutex_unlock(&vms->lock);
01975    /* empty string means no valid header */
01976    if (ast_strlen_zero(header_content)) {
01977       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01978       res = -1;
01979       goto exit;
01980    }
01981 
01982    ast_mutex_lock(&vms->lock);
01983    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01984    ast_mutex_unlock(&vms->lock);
01985 
01986    /* We have the body, now we extract the file name of the first attachment. */
01987    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01988       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01989    } else {
01990       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01991       res = -1;
01992       goto exit;
01993    }
01994    
01995    /* Find the format of the attached file */
01996 
01997    strsep(&attachedfilefmt, ".");
01998    if (!attachedfilefmt) {
01999       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
02000       res = -1;
02001       goto exit;
02002    }
02003    
02004    save_body(body, vms, "2", attachedfilefmt, 0);
02005    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02006       *vms->introfn = '\0';
02007    }
02008 
02009    /* Get info from headers!! */
02010    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02011 
02012    if (!(text_file_ptr = fopen(text_file, "w"))) {
02013       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02014    }
02015 
02016    fprintf(text_file_ptr, "%s\n", "[message]");
02017 
02018    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02019    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02020    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02021    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02022    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02023    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02024    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02025    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02026    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02027    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02028    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02029    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02030    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02031    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02032    fclose(text_file_ptr);
02033 
02034 exit:
02035    free_user(vmu);
02036    return res;
02037 }
02038 
02039 static int folder_int(const char *folder)
02040 {
02041    /*assume a NULL folder means INBOX*/
02042    if (!folder) {
02043       return 0;
02044    }
02045    if (!strcasecmp(folder, imapfolder)) {
02046       return 0;
02047    } else if (!strcasecmp(folder, "Old")) {
02048       return 1;
02049    } else if (!strcasecmp(folder, "Work")) {
02050       return 2;
02051    } else if (!strcasecmp(folder, "Family")) {
02052       return 3;
02053    } else if (!strcasecmp(folder, "Friends")) {
02054       return 4;
02055    } else if (!strcasecmp(folder, "Cust1")) {
02056       return 5;
02057    } else if (!strcasecmp(folder, "Cust2")) {
02058       return 6;
02059    } else if (!strcasecmp(folder, "Cust3")) {
02060       return 7;
02061    } else if (!strcasecmp(folder, "Cust4")) {
02062       return 8;
02063    } else if (!strcasecmp(folder, "Cust5")) {
02064       return 9;
02065    } else if (!strcasecmp(folder, "Urgent")) {
02066       return 11;
02067    } else { /*assume they meant INBOX if folder is not found otherwise*/
02068       return 0;
02069    }
02070 }
02071 
02072 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02073 {
02074    SEARCHPGM *pgm;
02075    SEARCHHEADER *hdr;
02076 
02077    struct ast_vm_user *vmu, vmus;
02078    struct vm_state *vms_p;
02079    int ret = 0;
02080    int fold = folder_int(folder);
02081    int urgent = 0;
02082    
02083    /* If URGENT, then look at INBOX */
02084    if (fold == 11) {
02085       fold = NEW_FOLDER;
02086       urgent = 1;
02087    }
02088 
02089    if (ast_strlen_zero(mailbox))
02090       return 0;
02091 
02092    /* We have to get the user before we can open the stream! */
02093    vmu = find_user(&vmus, context, mailbox);
02094    if (!vmu) {
02095       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02096       return -1;
02097    } else {
02098       /* No IMAP account available */
02099       if (vmu->imapuser[0] == '\0') {
02100          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02101          return -1;
02102       }
02103    }
02104    
02105    /* No IMAP account available */
02106    if (vmu->imapuser[0] == '\0') {
02107       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02108       free_user(vmu);
02109       return -1;
02110    }
02111    ast_assert(msgnum < vms->msg_array_max);
02112 
02113    /* check if someone is accessing this box right now... */
02114    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02115    if (!vms_p) {
02116       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02117    }
02118    if (vms_p) {
02119       ast_debug(3, "Returning before search - user is logged in\n");
02120       if (fold == 0) { /* INBOX */
02121          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02122       }
02123       if (fold == 1) { /* Old messages */
02124          return vms_p->oldmessages;
02125       }
02126    }
02127 
02128    /* add one if not there... */
02129    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02130    if (!vms_p) {
02131       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02132    }
02133 
02134    if (!vms_p) {
02135       vms_p = create_vm_state_from_user(vmu);
02136    }
02137    ret = init_mailstream(vms_p, fold);
02138    if (!vms_p->mailstream) {
02139       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02140       return -1;
02141    }
02142    if (ret == 0) {
02143       ast_mutex_lock(&vms_p->lock);
02144       pgm = mail_newsearchpgm ();
02145       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02146       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02147       pgm->header = hdr;
02148       if (fold != OLD_FOLDER) {
02149          pgm->unseen = 1;
02150          pgm->seen = 0;
02151       }
02152       /* In the special case where fold is 1 (old messages) we have to do things a bit
02153        * differently. Old messages are stored in the INBOX but are marked as "seen"
02154        */
02155       else {
02156          pgm->unseen = 0;
02157          pgm->seen = 1;
02158       }
02159       /* look for urgent messages */
02160       if (fold == NEW_FOLDER) {
02161          if (urgent) {
02162             pgm->flagged = 1;
02163             pgm->unflagged = 0;
02164          } else {
02165             pgm->flagged = 0;
02166             pgm->unflagged = 1;
02167          }
02168       }
02169       pgm->undeleted = 1;
02170       pgm->deleted = 0;
02171 
02172       vms_p->vmArrayIndex = 0;
02173       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02174       if (fold == 0 && urgent == 0)
02175          vms_p->newmessages = vms_p->vmArrayIndex;
02176       if (fold == 1)
02177          vms_p->oldmessages = vms_p->vmArrayIndex;
02178       if (fold == 0 && urgent == 1)
02179          vms_p->urgentmessages = vms_p->vmArrayIndex;
02180       /*Freeing the searchpgm also frees the searchhdr*/
02181       mail_free_searchpgm(&pgm);
02182       ast_mutex_unlock(&vms_p->lock);
02183       vms_p->updated = 0;
02184       return vms_p->vmArrayIndex;
02185    } else {
02186       ast_mutex_lock(&vms_p->lock);
02187       mail_ping(vms_p->mailstream);
02188       ast_mutex_unlock(&vms_p->lock);
02189    }
02190    return 0;
02191 }
02192 
02193 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02194 {
02195    /* Check if mailbox is full */
02196    check_quota(vms, vmu->imapfolder);
02197    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02198       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02199       ast_play_and_wait(chan, "vm-mailboxfull");
02200       return -1;
02201    }
02202    
02203    /* Check if we have exceeded maxmsg */
02204    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));
02205    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02206       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02207       ast_play_and_wait(chan, "vm-mailboxfull");
02208       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02209       return -1;
02210    }
02211 
02212    return 0;
02213 }
02214 
02215 /*!
02216  * \brief Gets the number of messages that exist in a mailbox folder.
02217  * \param context
02218  * \param mailbox
02219  * \param folder
02220  * 
02221  * This method is used when IMAP backend is used.
02222  * \return The number of messages in this mailbox folder (zero or more).
02223  */
02224 static int messagecount(const char *context, const char *mailbox, const char *folder)
02225 {
02226    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02227       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02228    } else {
02229       return __messagecount(context, mailbox, folder);
02230    }
02231 }
02232 
02233 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)
02234 {
02235    char *myserveremail = serveremail;
02236    char fn[PATH_MAX];
02237    char introfn[PATH_MAX];
02238    char mailbox[256];
02239    char *stringp;
02240    FILE *p = NULL;
02241    char tmp[80] = "/tmp/astmail-XXXXXX";
02242    long len;
02243    void *buf;
02244    int tempcopy = 0;
02245    STRING str;
02246    int ret; /* for better error checking */
02247    char *imap_flags = NIL;
02248    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02249    int box = NEW_FOLDER;
02250 
02251    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02252    if (msgnum < 0) {
02253       if(!imapgreetings) {
02254          return 0;
02255       } else {
02256          box = GREETINGS_FOLDER;
02257       }
02258    }
02259    
02260    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02261       return -1;
02262    }
02263 
02264    /* Set urgent flag for IMAP message */
02265    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02266       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02267       imap_flags = "\\FLAGGED";
02268    }
02269    
02270    /* Attach only the first format */
02271    fmt = ast_strdupa(fmt);
02272    stringp = fmt;
02273    strsep(&stringp, "|");
02274 
02275    if (!ast_strlen_zero(vmu->serveremail))
02276       myserveremail = vmu->serveremail;
02277 
02278    if (msgnum > -1)
02279       make_file(fn, sizeof(fn), dir, msgnum);
02280    else
02281       ast_copy_string (fn, dir, sizeof(fn));
02282 
02283    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02284    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02285       *introfn = '\0';
02286    }
02287    
02288    if (ast_strlen_zero(vmu->email)) {
02289       /* We need the vmu->email to be set when we call make_email_file, but
02290        * if we keep it set, a duplicate e-mail will be created. So at the end
02291        * of this function, we will revert back to an empty string if tempcopy
02292        * is 1.
02293        */
02294       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02295       tempcopy = 1;
02296    }
02297 
02298    if (!strcmp(fmt, "wav49"))
02299       fmt = "WAV";
02300    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02301 
02302    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02303       command hangs. */
02304    if (!(p = vm_mkftemp(tmp))) {
02305       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02306       if (tempcopy)
02307          *(vmu->email) = '\0';
02308       return -1;
02309    }
02310 
02311    if (msgnum < 0 && imapgreetings) {
02312       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02313          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02314          return -1;
02315       }
02316       imap_delete_old_greeting(fn, vms);
02317    }
02318 
02319    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02320       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02321       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02322       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02323    /* read mail file to memory */
02324    len = ftell(p);
02325    rewind(p);
02326    if (!(buf = ast_malloc(len + 1))) {
02327       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02328       fclose(p);
02329       if (tempcopy)
02330          *(vmu->email) = '\0';
02331       return -1;
02332    }
02333    if (fread(buf, len, 1, p) < len) {
02334       if (ferror(p)) {
02335          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02336          return -1;
02337       }
02338    }
02339    ((char *) buf)[len] = '\0';
02340    INIT(&str, mail_string, buf, len);
02341    ret = init_mailstream(vms, box);
02342    if (ret == 0) {
02343       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02344       ast_mutex_lock(&vms->lock);
02345       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02346          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02347       ast_mutex_unlock(&vms->lock);
02348       fclose(p);
02349       unlink(tmp);
02350       ast_free(buf);
02351    } else {
02352       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02353       fclose(p);
02354       unlink(tmp);
02355       ast_free(buf);
02356       return -1;
02357    }
02358    ast_debug(3, "%s stored\n", fn);
02359    
02360    if (tempcopy)
02361       *(vmu->email) = '\0';
02362    inprocess_count(vmu->mailbox, vmu->context, -1);
02363    return 0;
02364 
02365 }
02366 
02367 /*!
02368  * \brief Gets the number of messages that exist in the inbox folder.
02369  * \param mailbox_context
02370  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02371  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02372  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02373  * 
02374  * This method is used when IMAP backend is used.
02375  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02376  *
02377  * \return zero on success, -1 on error.
02378  */
02379 
02380 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02381 {
02382    char tmp[PATH_MAX] = "";
02383    char *mailboxnc;
02384    char *context;
02385    char *mb;
02386    char *cur;
02387    if (newmsgs)
02388       *newmsgs = 0;
02389    if (oldmsgs)
02390       *oldmsgs = 0;
02391    if (urgentmsgs)
02392       *urgentmsgs = 0;
02393 
02394    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02395    /* If no mailbox, return immediately */
02396    if (ast_strlen_zero(mailbox_context))
02397       return 0;
02398    
02399    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02400    context = strchr(tmp, '@');
02401    if (strchr(mailbox_context, ',')) {
02402       int tmpnew, tmpold, tmpurgent;
02403       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02404       mb = tmp;
02405       while ((cur = strsep(&mb, ", "))) {
02406          if (!ast_strlen_zero(cur)) {
02407             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02408                return -1;
02409             else {
02410                if (newmsgs)
02411                   *newmsgs += tmpnew; 
02412                if (oldmsgs)
02413                   *oldmsgs += tmpold;
02414                if (urgentmsgs)
02415                   *urgentmsgs += tmpurgent;
02416             }
02417          }
02418       }
02419       return 0;
02420    }
02421    if (context) {
02422       *context = '\0';
02423       mailboxnc = tmp;
02424       context++;
02425    } else {
02426       context = "default";
02427       mailboxnc = (char *) mailbox_context;
02428    }
02429 
02430    if (newmsgs) {
02431       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02432       if (!vmu) {
02433          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02434          return -1;
02435       }
02436       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02437          free_user(vmu);
02438          return -1;
02439       }
02440       free_user(vmu);
02441    }
02442    if (oldmsgs) {
02443       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02444          return -1;
02445       }
02446    }
02447    if (urgentmsgs) {
02448       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02449          return -1;
02450       }
02451    }
02452    return 0;
02453 }
02454 
02455 /** 
02456  * \brief Determines if the given folder has messages.
02457  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02458  * \param folder the folder to look in
02459  *
02460  * This function is used when the mailbox is stored in an IMAP back end.
02461  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02462  * \return 1 if the folder has one or more messages. zero otherwise.
02463  */
02464 
02465 static int has_voicemail(const char *mailbox, const char *folder)
02466 {
02467    char tmp[256], *tmp2, *box, *context;
02468    ast_copy_string(tmp, mailbox, sizeof(tmp));
02469    tmp2 = tmp;
02470    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02471       while ((box = strsep(&tmp2, ",&"))) {
02472          if (!ast_strlen_zero(box)) {
02473             if (has_voicemail(box, folder)) {
02474                return 1;
02475             }
02476          }
02477       }
02478    }
02479    if ((context = strchr(tmp, '@'))) {
02480       *context++ = '\0';
02481    } else {
02482       context = "default";
02483    }
02484    return __messagecount(context, tmp, folder) ? 1 : 0;
02485 }
02486 
02487 /*!
02488  * \brief Copies a message from one mailbox to another.
02489  * \param chan
02490  * \param vmu
02491  * \param imbox
02492  * \param msgnum
02493  * \param duration
02494  * \param recip
02495  * \param fmt
02496  * \param dir
02497  *
02498  * This works with IMAP storage based mailboxes.
02499  *
02500  * \return zero on success, -1 on error.
02501  */
02502 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)
02503 {
02504    struct vm_state *sendvms = NULL, *destvms = NULL;
02505    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02506    if (msgnum >= recip->maxmsg) {
02507       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02508       return -1;
02509    }
02510    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02511       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02512       return -1;
02513    }
02514    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02515       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02516       return -1;
02517    }
02518    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02519    ast_mutex_lock(&sendvms->lock);
02520    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02521       ast_mutex_unlock(&sendvms->lock);
02522       return 0;
02523    }
02524    ast_mutex_unlock(&sendvms->lock);
02525    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02526    return -1;
02527 }
02528 
02529 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02530 {
02531    char tmp[256], *t = tmp;
02532    size_t left = sizeof(tmp);
02533    
02534    if (box == OLD_FOLDER) {
02535       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02536    } else {
02537       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02538    }
02539 
02540    if (box == NEW_FOLDER) {
02541       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02542    } else {
02543       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02544    }
02545 
02546    /* Build up server information */
02547    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02548 
02549    /* Add authentication user if present */
02550    if (!ast_strlen_zero(authuser))
02551       ast_build_string(&t, &left, "/authuser=%s", authuser);
02552 
02553    /* Add flags if present */
02554    if (!ast_strlen_zero(imapflags))
02555       ast_build_string(&t, &left, "/%s", imapflags);
02556 
02557    /* End with username */
02558 #if 1
02559    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02560 #else
02561    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02562 #endif
02563    if (box == NEW_FOLDER || box == OLD_FOLDER)
02564       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02565    else if (box == GREETINGS_FOLDER)
02566       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02567    else {   /* Other folders such as Friends, Family, etc... */
02568       if (!ast_strlen_zero(imapparentfolder)) {
02569          /* imapparentfolder would typically be set to INBOX */
02570          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02571       } else {
02572          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02573       }
02574    }
02575 }
02576 
02577 static int init_mailstream(struct vm_state *vms, int box)
02578 {
02579    MAILSTREAM *stream = NIL;
02580    long debug;
02581    char tmp[256];
02582    
02583    if (!vms) {
02584       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02585       return -1;
02586    }
02587    if (option_debug > 2)
02588       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02589    if (vms->mailstream == NIL || !vms->mailstream) {
02590       if (option_debug)
02591          ast_log(LOG_DEBUG, "mailstream not set.\n");
02592    } else {
02593       stream = vms->mailstream;
02594    }
02595    /* debug = T;  user wants protocol telemetry? */
02596    debug = NIL;  /* NO protocol telemetry? */
02597 
02598    if (delimiter == '\0') {      /* did not probe the server yet */
02599       char *cp;
02600 #ifdef USE_SYSTEM_IMAP
02601 #include <imap/linkage.c>
02602 #elif defined(USE_SYSTEM_CCLIENT)
02603 #include <c-client/linkage.c>
02604 #else
02605 #include "linkage.c"
02606 #endif
02607       /* Connect to INBOX first to get folders delimiter */
02608       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02609       ast_mutex_lock(&vms->lock);
02610       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02611       ast_mutex_unlock(&vms->lock);
02612       if (stream == NIL) {
02613          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02614          return -1;
02615       }
02616       get_mailbox_delimiter(stream);
02617       /* update delimiter in imapfolder */
02618       for (cp = vms->imapfolder; *cp; cp++)
02619          if (*cp == '/')
02620             *cp = delimiter;
02621    }
02622    /* Now connect to the target folder */
02623    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02624    if (option_debug > 2)
02625       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02626    ast_mutex_lock(&vms->lock);
02627    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02628    ast_mutex_unlock(&vms->lock);
02629    if (vms->mailstream == NIL) {
02630       return -1;
02631    } else {
02632       return 0;
02633    }
02634 }
02635 
02636 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02637 {
02638    SEARCHPGM *pgm;
02639    SEARCHHEADER *hdr;
02640    int ret, urgent = 0;
02641 
02642    /* If Urgent, then look at INBOX */
02643    if (box == 11) {
02644       box = NEW_FOLDER;
02645       urgent = 1;
02646    }
02647 
02648    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02649    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02650    vms->imapversion = vmu->imapversion;
02651    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02652 
02653    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02654       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02655       return -1;
02656    }
02657    
02658    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02659    
02660    /* Check Quota */
02661    if  (box == 0)  {
02662       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02663       check_quota(vms, (char *) mbox(vmu, box));
02664    }
02665 
02666    ast_mutex_lock(&vms->lock);
02667    pgm = mail_newsearchpgm();
02668 
02669    /* Check IMAP folder for Asterisk messages only... */
02670    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02671    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02672    pgm->header = hdr;
02673    pgm->deleted = 0;
02674    pgm->undeleted = 1;
02675 
02676    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02677    if (box == NEW_FOLDER && urgent == 1) {
02678       pgm->unseen = 1;
02679       pgm->seen = 0;
02680       pgm->flagged = 1;
02681       pgm->unflagged = 0;
02682    } else if (box == NEW_FOLDER && urgent == 0) {
02683       pgm->unseen = 1;
02684       pgm->seen = 0;
02685       pgm->flagged = 0;
02686       pgm->unflagged = 1;
02687    } else if (box == OLD_FOLDER) {
02688       pgm->seen = 1;
02689       pgm->unseen = 0;
02690    }
02691 
02692    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02693 
02694    vms->vmArrayIndex = 0;
02695    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02696    vms->lastmsg = vms->vmArrayIndex - 1;
02697    mail_free_searchpgm(&pgm);
02698    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02699     * ensure to allocate enough space to account for all of them. Warn if old messages
02700     * have not been checked first as that is required.
02701     */
02702    if (box == 0 && !vms->dh_arraysize) {
02703       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02704    }
02705    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02706       ast_mutex_unlock(&vms->lock);
02707       return -1;
02708    }
02709 
02710    ast_mutex_unlock(&vms->lock);
02711    return 0;
02712 }
02713 
02714 static void write_file(char *filename, char *buffer, unsigned long len)
02715 {
02716    FILE *output;
02717 
02718    output = fopen (filename, "w");
02719    if (fwrite(buffer, len, 1, output) != 1) {
02720       if (ferror(output)) {
02721          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02722       }
02723    }
02724    fclose (output);
02725 }
02726 
02727 static void update_messages_by_imapuser(const char *user, unsigned long number)
02728 {
02729    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02730 
02731    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02732       return;
02733    }
02734 
02735    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02736 
02737    /* Ensure we have room for the next message. */
02738    if (vms->vmArrayIndex >= vms->msg_array_max) {
02739       long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
02740       if (!new_mem) {
02741          return;
02742       }
02743       vms->msgArray = new_mem;
02744       vms->msg_array_max *= 2;
02745    }
02746 
02747    vms->msgArray[vms->vmArrayIndex++] = number;
02748 }
02749 
02750 void mm_searched(MAILSTREAM *stream, unsigned long number)
02751 {
02752    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02753 
02754    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02755       return;
02756 
02757    update_messages_by_imapuser(user, number);
02758 }
02759 
02760 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02761 {
02762    struct ast_variable *var;
02763    struct ast_vm_user *vmu;
02764 
02765    vmu = ast_calloc(1, sizeof *vmu);
02766    if (!vmu)
02767       return NULL;
02768 
02769    populate_defaults(vmu);
02770    ast_set_flag(vmu, VM_ALLOCED);
02771 
02772    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02773    if (var) {
02774       apply_options_full(vmu, var);
02775       ast_variables_destroy(var);
02776       return vmu;
02777    } else {
02778       ast_free(vmu);
02779       return NULL;
02780    }
02781 }
02782 
02783 /* Interfaces to C-client */
02784 
02785 void mm_exists(MAILSTREAM * stream, unsigned long number)
02786 {
02787    /* mail_ping will callback here if new mail! */
02788    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02789    if (number == 0) return;
02790    set_update(stream);
02791 }
02792 
02793 
02794 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02795 {
02796    /* mail_ping will callback here if expunged mail! */
02797    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02798    if (number == 0) return;
02799    set_update(stream);
02800 }
02801 
02802 
02803 void mm_flags(MAILSTREAM * stream, unsigned long number)
02804 {
02805    /* mail_ping will callback here if read mail! */
02806    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02807    if (number == 0) return;
02808    set_update(stream);
02809 }
02810 
02811 
02812 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02813 {
02814    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02815    mm_log (string, errflg);
02816 }
02817 
02818 
02819 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02820 {
02821    if (delimiter == '\0') {
02822       delimiter = delim;
02823    }
02824 
02825    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02826    if (attributes & LATT_NOINFERIORS)
02827       ast_debug(5, "no inferiors\n");
02828    if (attributes & LATT_NOSELECT)
02829       ast_debug(5, "no select\n");
02830    if (attributes & LATT_MARKED)
02831       ast_debug(5, "marked\n");
02832    if (attributes & LATT_UNMARKED)
02833       ast_debug(5, "unmarked\n");
02834 }
02835 
02836 
02837 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02838 {
02839    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02840    if (attributes & LATT_NOINFERIORS)
02841       ast_debug(5, "no inferiors\n");
02842    if (attributes & LATT_NOSELECT)
02843       ast_debug(5, "no select\n");
02844    if (attributes & LATT_MARKED)
02845       ast_debug(5, "marked\n");
02846    if (attributes & LATT_UNMARKED)
02847       ast_debug(5, "unmarked\n");
02848 }
02849 
02850 
02851 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02852 {
02853    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02854    if (status->flags & SA_MESSAGES)
02855       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02856    if (status->flags & SA_RECENT)
02857       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02858    if (status->flags & SA_UNSEEN)
02859       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02860    if (status->flags & SA_UIDVALIDITY)
02861       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02862    if (status->flags & SA_UIDNEXT)
02863       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02864    ast_log(AST_LOG_NOTICE, "\n");
02865 }
02866 
02867 
02868 void mm_log(char *string, long errflg)
02869 {
02870    switch ((short) errflg) {
02871       case NIL:
02872          ast_debug(1, "IMAP Info: %s\n", string);
02873          break;
02874       case PARSE:
02875       case WARN:
02876          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02877          break;
02878       case ERROR:
02879          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02880          break;
02881    }
02882 }
02883 
02884 
02885 void mm_dlog(char *string)
02886 {
02887    ast_log(AST_LOG_NOTICE, "%s\n", string);
02888 }
02889 
02890 
02891 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02892 {
02893    struct ast_vm_user *vmu;
02894 
02895    ast_debug(4, "Entering callback mm_login\n");
02896 
02897    ast_copy_string(user, mb->user, MAILTMPLEN);
02898 
02899    /* We should only do this when necessary */
02900    if (!ast_strlen_zero(authpassword)) {
02901       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02902    } else {
02903       AST_LIST_TRAVERSE(&users, vmu, list) {
02904          if (!strcasecmp(mb->user, vmu->imapuser)) {
02905             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02906             break;
02907          }
02908       }
02909       if (!vmu) {
02910          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02911             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02912             free_user(vmu);
02913          }
02914       }
02915    }
02916 }
02917 
02918 
02919 void mm_critical(MAILSTREAM * stream)
02920 {
02921 }
02922 
02923 
02924 void mm_nocritical(MAILSTREAM * stream)
02925 {
02926 }
02927 
02928 
02929 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02930 {
02931    kill (getpid (), SIGSTOP);
02932    return NIL;
02933 }
02934 
02935 
02936 void mm_fatal(char *string)
02937 {
02938    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02939 }
02940 
02941 /* C-client callback to handle quota */
02942 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02943 {
02944    struct vm_state *vms;
02945    char *mailbox = stream->mailbox, *user;
02946    char buf[1024] = "";
02947    unsigned long usage = 0, limit = 0;
02948    
02949    while (pquota) {
02950       usage = pquota->usage;
02951       limit = pquota->limit;
02952       pquota = pquota->next;
02953    }
02954    
02955    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)))) {
02956       ast_log(AST_LOG_ERROR, "No state found.\n");
02957       return;
02958    }
02959 
02960    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02961 
02962    vms->quota_usage = usage;
02963    vms->quota_limit = limit;
02964 }
02965 
02966 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02967 {
02968    char *start, *eol_pnt;
02969    int taglen;
02970 
02971    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02972       return NULL;
02973 
02974    taglen = strlen(tag) + 1;
02975    if (taglen < 1)
02976       return NULL;
02977 
02978    if (!(start = strstr(header, tag)))
02979       return NULL;
02980 
02981    /* Since we can be called multiple times we should clear our buffer */
02982    memset(buf, 0, len);
02983 
02984    ast_copy_string(buf, start+taglen, len);
02985    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02986       *eol_pnt = '\0';
02987    return buf;
02988 }
02989 
02990 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02991 {
02992    char *start, *quote, *eol_pnt;
02993 
02994    if (ast_strlen_zero(mailbox))
02995       return NULL;
02996 
02997    if (!(start = strstr(mailbox, "/user=")))
02998       return NULL;
02999 
03000    ast_copy_string(buf, start+6, len);
03001 
03002    if (!(quote = strchr(buf, '\"'))) {
03003       if (!(eol_pnt = strchr(buf, '/')))
03004          eol_pnt = strchr(buf,'}');
03005       *eol_pnt = '\0';
03006       return buf;
03007    } else {
03008       eol_pnt = strchr(buf+1,'\"');
03009       *eol_pnt = '\0';
03010       return buf+1;
03011    }
03012 }
03013 
03014 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
03015 {
03016    struct vm_state *vms_p;
03017 
03018    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03019    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03020       return vms_p;
03021    }
03022    if (option_debug > 4)
03023       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
03024    /* XXX: Is this correctly freed always? */
03025    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03026       return NULL;
03027    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03028    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03029    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03030    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03031    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03032    vms_p->imapversion = vmu->imapversion;
03033    if (option_debug > 4)
03034       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03035    vms_p->updated = 1;
03036    /* set mailbox to INBOX! */
03037    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03038    init_vm_state(vms_p);
03039    vmstate_insert(vms_p);
03040    return vms_p;
03041 }
03042 
03043 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03044 {
03045    struct vmstate *vlist = NULL;
03046 
03047    if (interactive) {
03048       struct vm_state *vms;
03049       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03050       vms = pthread_getspecific(ts_vmstate.key);
03051       return vms;
03052    }
03053 
03054    AST_LIST_LOCK(&vmstates);
03055    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03056       if (!vlist->vms) {
03057          ast_debug(3, "error: vms is NULL for %s\n", user);
03058          continue;
03059       }
03060       if (vlist->vms->imapversion != imapversion) {
03061          continue;
03062       }
03063       if (!vlist->vms->imapuser) {
03064          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03065          continue;
03066       }
03067 
03068       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03069          AST_LIST_UNLOCK(&vmstates);
03070          return vlist->vms;
03071       }
03072    }
03073    AST_LIST_UNLOCK(&vmstates);
03074 
03075    ast_debug(3, "%s not found in vmstates\n", user);
03076 
03077    return NULL;
03078 }
03079 
03080 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03081 {
03082 
03083    struct vmstate *vlist = NULL;
03084    const char *local_context = S_OR(context, "default");
03085 
03086    if (interactive) {
03087       struct vm_state *vms;
03088       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03089       vms = pthread_getspecific(ts_vmstate.key);
03090       return vms;
03091    }
03092 
03093    AST_LIST_LOCK(&vmstates);
03094    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03095       if (!vlist->vms) {
03096          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03097          continue;
03098       }
03099       if (vlist->vms->imapversion != imapversion) {
03100          continue;
03101       }
03102       if (!vlist->vms->username || !vlist->vms->context) {
03103          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03104          continue;
03105       }
03106 
03107       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);
03108       
03109       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03110          ast_debug(3, "Found it!\n");
03111          AST_LIST_UNLOCK(&vmstates);
03112          return vlist->vms;
03113       }
03114    }
03115    AST_LIST_UNLOCK(&vmstates);
03116 
03117    ast_debug(3, "%s not found in vmstates\n", mailbox);
03118 
03119    return NULL;
03120 }
03121 
03122 static void vmstate_insert(struct vm_state *vms) 
03123 {
03124    struct vmstate *v;
03125    struct vm_state *altvms;
03126 
03127    /* If interactive, it probably already exists, and we should
03128       use the one we already have since it is more up to date.
03129       We can compare the username to find the duplicate */
03130    if (vms->interactive == 1) {
03131       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03132       if (altvms) {  
03133          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03134          vms->newmessages = altvms->newmessages;
03135          vms->oldmessages = altvms->oldmessages;
03136          vms->vmArrayIndex = altvms->vmArrayIndex;
03137          /* XXX: no msgArray copying? */
03138          vms->lastmsg = altvms->lastmsg;
03139          vms->curmsg = altvms->curmsg;
03140          /* get a pointer to the persistent store */
03141          vms->persist_vms = altvms;
03142          /* Reuse the mailstream? */
03143 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03144          vms->mailstream = altvms->mailstream;
03145 #else
03146          vms->mailstream = NIL;
03147 #endif
03148       }
03149       return;
03150    }
03151 
03152    if (!(v = ast_calloc(1, sizeof(*v))))
03153       return;
03154    
03155    v->vms = vms;
03156 
03157    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03158 
03159    AST_LIST_LOCK(&vmstates);
03160    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03161    AST_LIST_UNLOCK(&vmstates);
03162 }
03163 
03164 static void vmstate_delete(struct vm_state *vms) 
03165 {
03166    struct vmstate *vc = NULL;
03167    struct vm_state *altvms = NULL;
03168 
03169    /* If interactive, we should copy pertinent info
03170       back to the persistent state (to make update immediate) */
03171    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03172       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03173       altvms->newmessages = vms->newmessages;
03174       altvms->oldmessages = vms->oldmessages;
03175       altvms->updated = 1;
03176       vms->mailstream = mail_close(vms->mailstream);
03177 
03178       /* Interactive states are not stored within the persistent list */
03179       return;
03180    }
03181    
03182    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03183    
03184    AST_LIST_LOCK(&vmstates);
03185    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03186       if (vc->vms == vms) {
03187          AST_LIST_REMOVE_CURRENT(list);
03188          break;
03189       }
03190    }
03191    AST_LIST_TRAVERSE_SAFE_END
03192    AST_LIST_UNLOCK(&vmstates);
03193    
03194    if (vc) {
03195       ast_mutex_destroy(&vc->vms->lock);
03196       ast_free(vc->vms->msgArray);
03197       vc->vms->msgArray = NULL;
03198       vc->vms->msg_array_max = 0;
03199       /* XXX: is no one supposed to free vms itself? */
03200       ast_free(vc);
03201    } else {
03202       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03203    }
03204 }
03205 
03206 static void set_update(MAILSTREAM * stream) 
03207 {
03208    struct vm_state *vms;
03209    char *mailbox = stream->mailbox, *user;
03210    char buf[1024] = "";
03211 
03212    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03213       if (user && option_debug > 2)
03214          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03215       return;
03216    }
03217 
03218    ast_debug(3, "User %s mailbox set for update.\n", user);
03219 
03220    vms->updated = 1; /* Set updated flag since mailbox changed */
03221 }
03222 
03223 static void init_vm_state(struct vm_state *vms) 
03224 {
03225    vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
03226    vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
03227    if (!vms->msgArray) {
03228       /* Out of mem? This can't be good. */
03229       vms->msg_array_max = 0;
03230    }
03231    vms->vmArrayIndex = 0;
03232    ast_mutex_init(&vms->lock);
03233 }
03234 
03235 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03236 {
03237    char *body_content;
03238    char *body_decoded;
03239    char *fn = is_intro ? vms->introfn : vms->fn;
03240    unsigned long len;
03241    unsigned long newlen;
03242    char filename[256];
03243    
03244    if (!body || body == NIL)
03245       return -1;
03246 
03247    ast_mutex_lock(&vms->lock);
03248    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03249    ast_mutex_unlock(&vms->lock);
03250    if (body_content != NIL) {
03251       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03252       /* ast_debug(1,body_content); */
03253       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03254       /* If the body of the file is empty, return an error */
03255       if (!newlen) {
03256          return -1;
03257       }
03258       write_file(filename, (char *) body_decoded, newlen);
03259    } else {
03260       ast_debug(5, "Body of message is NULL.\n");
03261       return -1;
03262    }
03263    return 0;
03264 }
03265 
03266 /*! 
03267  * \brief Get delimiter via mm_list callback 
03268  * \param stream
03269  *
03270  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03271  */
03272 /* MUTEX should already be held */
03273 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03274    char tmp[50];
03275    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03276    mail_list(stream, tmp, "*");
03277 }
03278 
03279 /*! 
03280  * \brief Check Quota for user 
03281  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03282  * \param mailbox the mailbox to check the quota for.
03283  *
03284  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03285  */
03286 static void check_quota(struct vm_state *vms, char *mailbox) {
03287    ast_mutex_lock(&vms->lock);
03288    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03289    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03290    if (vms && vms->mailstream != NULL) {
03291       imap_getquotaroot(vms->mailstream, mailbox);
03292    } else {
03293       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03294    }
03295    ast_mutex_unlock(&vms->lock);
03296 }
03297 
03298 #endif /* IMAP_STORAGE */
03299 
03300 /*! \brief Lock file path
03301  * only return failure if ast_lock_path returns 'timeout',
03302  * not if the path does not exist or any other reason
03303  */
03304 static int vm_lock_path(const char *path)
03305 {
03306    switch (ast_lock_path(path)) {
03307    case AST_LOCK_TIMEOUT:
03308       return -1;
03309    default:
03310       return 0;
03311    }
03312 }
03313 
03314 
03315 #ifdef ODBC_STORAGE
03316 struct generic_prepare_struct {
03317    char *sql;
03318    int argc;
03319    char **argv;
03320 };
03321 
03322 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03323 {
03324    struct generic_prepare_struct *gps = data;
03325    int res, i;
03326    SQLHSTMT stmt;
03327 
03328    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03329    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03330       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03331       return NULL;
03332    }
03333    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03334    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03335       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03336       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03337       return NULL;
03338    }
03339    for (i = 0; i < gps->argc; i++)
03340       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03341 
03342    return stmt;
03343 }
03344 
03345 /*!
03346  * \brief Retrieves a file from an ODBC data store.
03347  * \param dir the path to the file to be retreived.
03348  * \param msgnum the message number, such as within a mailbox folder.
03349  * 
03350  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03351  * 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.
03352  *
03353  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03354  * The output is the message information file with the name msgnum and the extension .txt
03355  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03356  * 
03357  * \return 0 on success, -1 on error.
03358  */
03359 static int retrieve_file(char *dir, int msgnum)
03360 {
03361    int x = 0;
03362    int res;
03363    int fd = -1;
03364    size_t fdlen = 0;
03365    void *fdm = MAP_FAILED;
03366    SQLSMALLINT colcount = 0;
03367    SQLHSTMT stmt;
03368    char sql[PATH_MAX];
03369    char fmt[80]="";
03370    char *c;
03371    char coltitle[256];
03372    SQLSMALLINT collen;
03373    SQLSMALLINT datatype;
03374    SQLSMALLINT decimaldigits;
03375    SQLSMALLINT nullable;
03376    SQLULEN colsize;
03377    SQLLEN colsize2;
03378    FILE *f = NULL;
03379    char rowdata[80];
03380    char fn[PATH_MAX];
03381    char full_fn[PATH_MAX];
03382    char msgnums[80];
03383    char *argv[] = { dir, msgnums };
03384    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03385 
03386    struct odbc_obj *obj;
03387    obj = ast_odbc_request_obj(odbc_database, 0);
03388    if (obj) {
03389       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03390       c = strchr(fmt, '|');
03391       if (c)
03392          *c = '\0';
03393       if (!strcasecmp(fmt, "wav49"))
03394          strcpy(fmt, "WAV");
03395       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03396       if (msgnum > -1)
03397          make_file(fn, sizeof(fn), dir, msgnum);
03398       else
03399          ast_copy_string(fn, dir, sizeof(fn));
03400 
03401       /* Create the information file */
03402       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03403       
03404       if (!(f = fopen(full_fn, "w+"))) {
03405          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03406          goto yuck;
03407       }
03408       
03409       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03410       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03411       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03412       if (!stmt) {
03413          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03414          ast_odbc_release_obj(obj);
03415          goto yuck;
03416       }
03417       res = SQLFetch(stmt);
03418       if (res == SQL_NO_DATA) {
03419          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03420          ast_odbc_release_obj(obj);
03421          goto yuck;
03422       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03423          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03424          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03425          ast_odbc_release_obj(obj);
03426          goto yuck;
03427       }
03428       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03429       if (fd < 0) {
03430          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03431          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03432          ast_odbc_release_obj(obj);
03433          goto yuck;
03434       }
03435       res = SQLNumResultCols(stmt, &colcount);
03436       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03437          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03438          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03439          ast_odbc_release_obj(obj);
03440          goto yuck;
03441       }
03442       if (f) 
03443          fprintf(f, "[message]\n");
03444       for (x = 0; x < colcount; x++) {
03445          rowdata[0] = '\0';
03446          colsize = 0;
03447          collen = sizeof(coltitle);
03448          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03449                   &datatype, &colsize, &decimaldigits, &nullable);
03450          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03451             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03452             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03453             ast_odbc_release_obj(obj);
03454             goto yuck;
03455          }
03456          if (!strcasecmp(coltitle, "recording")) {
03457             off_t offset;
03458             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03459             fdlen = colsize2;
03460             if (fd > -1) {
03461                char tmp[1]="";
03462                lseek(fd, fdlen - 1, SEEK_SET);
03463                if (write(fd, tmp, 1) != 1) {
03464                   close(fd);
03465                   fd = -1;
03466                   continue;
03467                }
03468                /* Read out in small chunks */
03469                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03470                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03471                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03472                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03473                      ast_odbc_release_obj(obj);
03474                      goto yuck;
03475                   } else {
03476                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03477                      munmap(fdm, CHUNKSIZE);
03478                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03479                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03480                         unlink(full_fn);
03481                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03482                         ast_odbc_release_obj(obj);
03483                         goto yuck;
03484                      }
03485                   }
03486                }
03487                if (truncate(full_fn, fdlen) < 0) {
03488                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03489                }
03490             }
03491          } else {
03492             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03493             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03494                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03495                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03496                ast_odbc_release_obj(obj);
03497                goto yuck;
03498             }
03499             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03500                fprintf(f, "%s=%s\n", coltitle, rowdata);
03501          }
03502       }
03503       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03504       ast_odbc_release_obj(obj);
03505    } else
03506       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03507 yuck:
03508    if (f)
03509       fclose(f);
03510    if (fd > -1)
03511       close(fd);
03512    return x - 1;
03513 }
03514 
03515 /*!
03516  * \brief Determines the highest message number in use for a given user and mailbox folder.
03517  * \param vmu 
03518  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03519  *
03520  * This method is used when mailboxes are stored in an ODBC back end.
03521  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03522  *
03523  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03524 
03525  */
03526 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03527 {
03528    int x = 0;
03529    int res;
03530    SQLHSTMT stmt;
03531    char sql[PATH_MAX];
03532    char rowdata[20];
03533    char *argv[] = { dir };
03534    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03535 
03536    struct odbc_obj *obj;
03537    obj = ast_odbc_request_obj(odbc_database, 0);
03538    if (obj) {
03539       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03540 
03541       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03542       if (!stmt) {
03543          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03544          ast_odbc_release_obj(obj);
03545          goto yuck;
03546       }
03547       res = SQLFetch(stmt);
03548       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03549          if (res == SQL_NO_DATA) {
03550             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03551          } else {
03552             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03553          }
03554 
03555          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03556          ast_odbc_release_obj(obj);
03557          goto yuck;
03558       }
03559       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03560       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03561          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03562          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03563          ast_odbc_release_obj(obj);
03564          goto yuck;
03565       }
03566       if (sscanf(rowdata, "%30d", &x) != 1)
03567          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03568       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03569       ast_odbc_release_obj(obj);
03570       return x;
03571    } else
03572       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03573 yuck:
03574    return x - 1;
03575 }
03576 
03577 /*!
03578  * \brief Determines if the specified message exists.
03579  * \param dir the folder the mailbox folder to look for messages. 
03580  * \param msgnum the message index to query for.
03581  *
03582  * This method is used when mailboxes are stored in an ODBC back end.
03583  *
03584  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03585  */
03586 static int message_exists(char *dir, int msgnum)
03587 {
03588    int x = 0;
03589    int res;
03590    SQLHSTMT stmt;
03591    char sql[PATH_MAX];
03592    char rowdata[20];
03593    char msgnums[20];
03594    char *argv[] = { dir, msgnums };
03595    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03596 
03597    struct odbc_obj *obj;
03598    obj = ast_odbc_request_obj(odbc_database, 0);
03599    if (obj) {
03600       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03601       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03602       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03603       if (!stmt) {
03604          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03605          ast_odbc_release_obj(obj);
03606          goto yuck;
03607       }
03608       res = SQLFetch(stmt);
03609       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03610          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03611          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03612          ast_odbc_release_obj(obj);
03613          goto yuck;
03614       }
03615       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03616       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03617          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03618          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03619          ast_odbc_release_obj(obj);
03620          goto yuck;
03621       }
03622       if (sscanf(rowdata, "%30d", &x) != 1)
03623          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03624       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03625       ast_odbc_release_obj(obj);
03626    } else
03627       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03628 yuck:
03629    return x;
03630 }
03631 
03632 /*!
03633  * \brief returns the number of messages found.
03634  * \param vmu
03635  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03636  *
03637  * This method is used when mailboxes are stored in an ODBC back end.
03638  *
03639  * \return The count of messages being zero or more, less than zero on error.
03640  */
03641 static int count_messages(struct ast_vm_user *vmu, char *dir)
03642 {
03643    int x = 0;
03644    int res;
03645    SQLHSTMT stmt;
03646    char sql[PATH_MAX];
03647    char rowdata[20];
03648    char *argv[] = { dir };
03649    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03650 
03651    struct odbc_obj *obj;
03652    obj = ast_odbc_request_obj(odbc_database, 0);
03653    if (obj) {
03654       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03655       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03656       if (!stmt) {
03657          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03658          ast_odbc_release_obj(obj);
03659          goto yuck;
03660       }
03661       res = SQLFetch(stmt);
03662       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03663          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03664          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03665          ast_odbc_release_obj(obj);
03666          goto yuck;
03667       }
03668       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03669       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03670          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03671          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03672          ast_odbc_release_obj(obj);
03673          goto yuck;
03674       }
03675       if (sscanf(rowdata, "%30d", &x) != 1)
03676          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03677       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03678       ast_odbc_release_obj(obj);
03679       return x;
03680    } else
03681       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03682 yuck:
03683    return x - 1;
03684 
03685 }
03686 
03687 /*!
03688  * \brief Deletes a message from the mailbox folder.
03689  * \param sdir The mailbox folder to work in.
03690  * \param smsg The message index to be deleted.
03691  *
03692  * This method is used when mailboxes are stored in an ODBC back end.
03693  * The specified message is directly deleted from the database 'voicemessages' table.
03694  * 
03695  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03696  */
03697 static void delete_file(const char *sdir, int smsg)
03698 {
03699    SQLHSTMT stmt;
03700    char sql[PATH_MAX];
03701    char msgnums[20];
03702    char *argv[] = { NULL, msgnums };
03703    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03704    struct odbc_obj *obj;
03705 
03706    argv[0] = ast_strdupa(sdir);
03707 
03708    obj = ast_odbc_request_obj(odbc_database, 0);
03709    if (obj) {
03710       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03711       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03712       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03713       if (!stmt)
03714          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03715       else
03716          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03717       ast_odbc_release_obj(obj);
03718    } else
03719       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03720    return;  
03721 }
03722 
03723 /*!
03724  * \brief Copies a voicemail from one mailbox to another.
03725  * \param sdir the folder for which to look for the message to be copied.
03726  * \param smsg the index of the message to be copied.
03727  * \param ddir the destination folder to copy the message into.
03728  * \param dmsg the index to be used for the copied message.
03729  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03730  * \param dmailboxcontext The context for the destination user.
03731  *
03732  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03733  */
03734 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03735 {
03736    SQLHSTMT stmt;
03737    char sql[512];
03738    char msgnums[20];
03739    char msgnumd[20];
03740    struct odbc_obj *obj;
03741    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03742    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03743 
03744    delete_file(ddir, dmsg);
03745    obj = ast_odbc_request_obj(odbc_database, 0);
03746    if (obj) {
03747       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03748       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03749       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);
03750       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03751       if (!stmt)
03752          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03753       else
03754          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03755       ast_odbc_release_obj(obj);
03756    } else
03757       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03758    return;  
03759 }
03760 
03761 struct insert_data {
03762    char *sql;
03763    const char *dir;
03764    const char *msgnums;
03765    void *data;
03766    SQLLEN datalen;
03767    SQLLEN indlen;
03768    const char *context;
03769    const char *macrocontext;
03770    const char *callerid;
03771    const char *origtime;
03772    const char *duration;
03773    const char *mailboxuser;
03774    const char *mailboxcontext;
03775    const char *category;
03776    const char *flag;
03777 };
03778 
03779 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03780 {
03781    struct insert_data *data = vdata;
03782    int res;
03783    SQLHSTMT stmt;
03784 
03785    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03786    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03787       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03788       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03789       return NULL;
03790    }
03791 
03792    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03793    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03794    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03795    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03796    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03797    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03798    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03799    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03800    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03801    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03802    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03803    if (!ast_strlen_zero(data->category)) {
03804       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03805    }
03806    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03807    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03808       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03809       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03810       return NULL;
03811    }
03812 
03813    return stmt;
03814 }
03815 
03816 /*!
03817  * \brief Stores a voicemail into the database.
03818  * \param dir the folder the mailbox folder to store the message.
03819  * \param mailboxuser the user owning the mailbox folder.
03820  * \param mailboxcontext
03821  * \param msgnum the message index for the message to be stored.
03822  *
03823  * This method is used when mailboxes are stored in an ODBC back end.
03824  * The message sound file and information file is looked up on the file system. 
03825  * A SQL query is invoked to store the message into the (MySQL) database.
03826  *
03827  * \return the zero on success -1 on error.
03828  */
03829 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03830 {
03831    int res = 0;
03832    int fd = -1;
03833    void *fdm = MAP_FAILED;
03834    off_t fdlen = -1;
03835    SQLHSTMT stmt;
03836    char sql[PATH_MAX];
03837    char msgnums[20];
03838    char fn[PATH_MAX];
03839    char full_fn[PATH_MAX];
03840    char fmt[80]="";
03841    char *c;
03842    struct ast_config *cfg = NULL;
03843    struct odbc_obj *obj;
03844    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03845       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03846    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03847 
03848    delete_file(dir, msgnum);
03849    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03850       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03851       return -1;
03852    }
03853 
03854    do {
03855       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03856       c = strchr(fmt, '|');
03857       if (c)
03858          *c = '\0';
03859       if (!strcasecmp(fmt, "wav49"))
03860          strcpy(fmt, "WAV");
03861       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03862       if (msgnum > -1)
03863          make_file(fn, sizeof(fn), dir, msgnum);
03864       else
03865          ast_copy_string(fn, dir, sizeof(fn));
03866       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03867       cfg = ast_config_load(full_fn, config_flags);
03868       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03869       fd = open(full_fn, O_RDWR);
03870       if (fd < 0) {
03871          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03872          res = -1;
03873          break;
03874       }
03875       if (valid_config(cfg)) {
03876          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03877             idata.context = "";
03878          }
03879          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03880             idata.macrocontext = "";
03881          }
03882          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03883             idata.callerid = "";
03884          }
03885          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03886             idata.origtime = "";
03887          }
03888          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03889             idata.duration = "";
03890          }
03891          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03892             idata.category = "";
03893          }
03894          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03895             idata.flag = "";
03896          }
03897       }
03898       fdlen = lseek(fd, 0, SEEK_END);
03899       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03900          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03901          res = -1;
03902          break;
03903       }
03904       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03905       if (fdm == MAP_FAILED) {
03906          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03907          res = -1;
03908          break;
03909       } 
03910       idata.data = fdm;
03911       idata.datalen = idata.indlen = fdlen;
03912 
03913       if (!ast_strlen_zero(idata.category)) 
03914          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03915       else
03916          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03917 
03918       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03919          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03920       } else {
03921          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03922          res = -1;
03923       }
03924    } while (0);
03925    if (obj) {
03926       ast_odbc_release_obj(obj);
03927    }
03928    if (valid_config(cfg))
03929       ast_config_destroy(cfg);
03930    if (fdm != MAP_FAILED)
03931       munmap(fdm, fdlen);
03932    if (fd > -1)
03933       close(fd);
03934    return res;
03935 }
03936 
03937 /*!
03938  * \brief Renames a message in a mailbox folder.
03939  * \param sdir The folder of the message to be renamed.
03940  * \param smsg The index of the message to be renamed.
03941  * \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.
03942  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03943  * \param ddir The destination folder for the message to be renamed into
03944  * \param dmsg The destination message for the message to be renamed.
03945  *
03946  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03947  * 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.
03948  * 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.
03949  */
03950 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03951 {
03952    SQLHSTMT stmt;
03953    char sql[PATH_MAX];
03954    char msgnums[20];
03955    char msgnumd[20];
03956    struct odbc_obj *obj;
03957    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03958    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03959 
03960    delete_file(ddir, dmsg);
03961    obj = ast_odbc_request_obj(odbc_database, 0);
03962    if (obj) {
03963       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03964       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03965       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03966       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03967       if (!stmt)
03968          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03969       else
03970          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03971       ast_odbc_release_obj(obj);
03972    } else
03973       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03974    return;  
03975 }
03976 
03977 /*!
03978  * \brief Removes a voicemail message file.
03979  * \param dir the path to the message file.
03980  * \param msgnum the unique number for the message within the mailbox.
03981  *
03982  * Removes the message content file and the information file.
03983  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03984  * Typical use is to clean up after a RETRIEVE operation. 
03985  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03986  * \return zero on success, -1 on error.
03987  */
03988 static int remove_file(char *dir, int msgnum)
03989 {
03990    char fn[PATH_MAX];
03991    char full_fn[PATH_MAX];
03992    char msgnums[80];
03993    
03994    if (msgnum > -1) {
03995       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03996       make_file(fn, sizeof(fn), dir, msgnum);
03997    } else
03998       ast_copy_string(fn, dir, sizeof(fn));
03999    ast_filedelete(fn, NULL);  
04000    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
04001    unlink(full_fn);
04002    return 0;
04003 }
04004 #else
04005 #ifndef IMAP_STORAGE
04006 /*!
04007  * \brief Find all .txt files - even if they are not in sequence from 0000.
04008  * \param vmu
04009  * \param dir
04010  *
04011  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04012  *
04013  * \return the count of messages, zero or more.
04014  */
04015 static int count_messages(struct ast_vm_user *vmu, char *dir)
04016 {
04017 
04018    int vmcount = 0;
04019    DIR *vmdir = NULL;
04020    struct dirent *vment = NULL;
04021 
04022    if (vm_lock_path(dir))
04023       return ERROR_LOCK_PATH;
04024 
04025    if ((vmdir = opendir(dir))) {
04026       while ((vment = readdir(vmdir))) {
04027          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04028             vmcount++;
04029          }
04030       }
04031       closedir(vmdir);
04032    }
04033    ast_unlock_path(dir);
04034    
04035    return vmcount;
04036 }
04037 
04038 /*!
04039  * \brief Renames a message in a mailbox folder.
04040  * \param sfn The path to the mailbox information and data file to be renamed.
04041  * \param dfn The path for where the message data and information files will be renamed to.
04042  *
04043  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04044  */
04045 static void rename_file(char *sfn, char *dfn)
04046 {
04047    char stxt[PATH_MAX];
04048    char dtxt[PATH_MAX];
04049    ast_filerename(sfn, dfn, NULL);
04050    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04051    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04052    if (ast_check_realtime("voicemail_data")) {
04053       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04054    }
04055    rename(stxt, dtxt);
04056 }
04057 
04058 /*! 
04059  * \brief Determines the highest message number in use for a given user and mailbox folder.
04060  * \param vmu 
04061  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04062  *
04063  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04064  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04065  *
04066  * \note Should always be called with a lock already set on dir.
04067  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04068  */
04069 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04070 {
04071    int x;
04072    unsigned char map[MAXMSGLIMIT] = "";
04073    DIR *msgdir;
04074    struct dirent *msgdirent;
04075    int msgdirint;
04076    char extension[4];
04077    int stopcount = 0;
04078 
04079    /* Reading the entire directory into a file map scales better than
04080     * doing a stat repeatedly on a predicted sequence.  I suspect this
04081     * is partially due to stat(2) internally doing a readdir(2) itself to
04082     * find each file. */
04083    if (!(msgdir = opendir(dir))) {
04084       return -1;
04085    }
04086 
04087    while ((msgdirent = readdir(msgdir))) {
04088       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04089          map[msgdirint] = 1;
04090          stopcount++;
04091          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04092       }
04093    }
04094    closedir(msgdir);
04095 
04096    for (x = 0; x < vmu->maxmsg; x++) {
04097       if (map[x] == 1) {
04098          stopcount--;
04099       } else if (map[x] == 0 && !stopcount) {
04100          break;
04101       }
04102    }
04103 
04104    return x - 1;
04105 }
04106 
04107 #endif /* #ifndef IMAP_STORAGE */
04108 #endif /* #else of #ifdef ODBC_STORAGE */
04109 #ifndef IMAP_STORAGE
04110 /*!
04111  * \brief Utility function to copy a file.
04112  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04113  * \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.
04114  *
04115  * 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.
04116  * The copy operation copies up to 4096 bytes at once.
04117  *
04118  * \return zero on success, -1 on error.
04119  */
04120 static int copy(char *infile, char *outfile)
04121 {
04122    int ifd;
04123    int ofd;
04124    int res;
04125    int len;
04126    char buf[4096];
04127 
04128 #ifdef HARDLINK_WHEN_POSSIBLE
04129    /* Hard link if possible; saves disk space & is faster */
04130    if (link(infile, outfile)) {
04131 #endif
04132       if ((ifd = open(infile, O_RDONLY)) < 0) {
04133          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04134          return -1;
04135       }
04136       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04137          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04138          close(ifd);
04139          return -1;
04140       }
04141       do {
04142          len = read(ifd, buf, sizeof(buf));
04143          if (len < 0) {
04144             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04145             close(ifd);
04146             close(ofd);
04147             unlink(outfile);
04148          } else if (len) {
04149             res = write(ofd, buf, len);
04150             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04151                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04152                close(ifd);
04153                close(ofd);
04154                unlink(outfile);
04155             }
04156          }
04157       } while (len);
04158       close(ifd);
04159       close(ofd);
04160       return 0;
04161 #ifdef HARDLINK_WHEN_POSSIBLE
04162    } else {
04163       /* Hard link succeeded */
04164       return 0;
04165    }
04166 #endif
04167 }
04168 
04169 /*!
04170  * \brief Copies a voicemail information (envelope) file.
04171  * \param frompath
04172  * \param topath 
04173  *
04174  * Every voicemail has the data (.wav) file, and the information file.
04175  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04176  * This is used by the COPY macro when not using IMAP storage.
04177  */
04178 static void copy_plain_file(char *frompath, char *topath)
04179 {
04180    char frompath2[PATH_MAX], topath2[PATH_MAX];
04181    struct ast_variable *tmp,*var = NULL;
04182    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04183    ast_filecopy(frompath, topath, NULL);
04184    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04185    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04186    if (ast_check_realtime("voicemail_data")) {
04187       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04188       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04189       for (tmp = var; tmp; tmp = tmp->next) {
04190          if (!strcasecmp(tmp->name, "origmailbox")) {
04191             origmailbox = tmp->value;
04192          } else if (!strcasecmp(tmp->name, "context")) {
04193             context = tmp->value;
04194          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04195             macrocontext = tmp->value;
04196          } else if (!strcasecmp(tmp->name, "exten")) {
04197             exten = tmp->value;
04198          } else if (!strcasecmp(tmp->name, "priority")) {
04199             priority = tmp->value;
04200          } else if (!strcasecmp(tmp->name, "callerchan")) {
04201             callerchan = tmp->value;
04202          } else if (!strcasecmp(tmp->name, "callerid")) {
04203             callerid = tmp->value;
04204          } else if (!strcasecmp(tmp->name, "origdate")) {
04205             origdate = tmp->value;
04206          } else if (!strcasecmp(tmp->name, "origtime")) {
04207             origtime = tmp->value;
04208          } else if (!strcasecmp(tmp->name, "category")) {
04209             category = tmp->value;
04210          } else if (!strcasecmp(tmp->name, "duration")) {
04211             duration = tmp->value;
04212          }
04213       }
04214       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);
04215    }
04216    copy(frompath2, topath2);
04217    ast_variables_destroy(var);
04218 }
04219 #endif
04220 
04221 /*! 
04222  * \brief Removes the voicemail sound and information file.
04223  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04224  *
04225  * This is used by the DELETE macro when voicemails are stored on the file system.
04226  *
04227  * \return zero on success, -1 on error.
04228  */
04229 static int vm_delete(char *file)
04230 {
04231    char *txt;
04232    int txtsize = 0;
04233 
04234    txtsize = (strlen(file) + 5)*sizeof(char);
04235    txt = ast_alloca(txtsize);
04236    /* Sprintf here would safe because we alloca'd exactly the right length,
04237     * but trying to eliminate all sprintf's anyhow
04238     */
04239    if (ast_check_realtime("voicemail_data")) {
04240       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04241    }
04242    snprintf(txt, txtsize, "%s.txt", file);
04243    unlink(txt);
04244    return ast_filedelete(file, NULL);
04245 }
04246 
04247 /*!
04248  * \brief utility used by inchar(), for base_encode()
04249  */
04250 static int inbuf(struct baseio *bio, FILE *fi)
04251 {
04252    int l;
04253 
04254    if (bio->ateof)
04255       return 0;
04256 
04257    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04258       if (ferror(fi))
04259          return -1;
04260 
04261       bio->ateof = 1;
04262       return 0;
04263    }
04264 
04265    bio->iolen = l;
04266    bio->iocp = 0;
04267 
04268    return 1;
04269 }
04270 
04271 /*!
04272  * \brief utility used by base_encode()
04273  */
04274 static int inchar(struct baseio *bio, FILE *fi)
04275 {
04276    if (bio->iocp>=bio->iolen) {
04277       if (!inbuf(bio, fi))
04278          return EOF;
04279    }
04280 
04281    return bio->iobuf[bio->iocp++];
04282 }
04283 
04284 /*!
04285  * \brief utility used by base_encode()
04286  */
04287 static int ochar(struct baseio *bio, int c, FILE *so)
04288 {
04289    if (bio->linelength >= BASELINELEN) {
04290       if (fputs(ENDL, so) == EOF) {
04291          return -1;
04292       }
04293 
04294       bio->linelength = 0;
04295    }
04296 
04297    if (putc(((unsigned char) c), so) == EOF) {
04298       return -1;
04299    }
04300 
04301    bio->linelength++;
04302 
04303    return 1;
04304 }
04305 
04306 /*!
04307  * \brief Performs a base 64 encode algorithm on the contents of a File
04308  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04309  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04310  *
04311  * 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 ?
04312  *
04313  * \return zero on success, -1 on error.
04314  */
04315 static int base_encode(char *filename, FILE *so)
04316 {
04317    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04318       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04319       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04320       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04321    int i, hiteof = 0;
04322    FILE *fi;
04323    struct baseio bio;
04324 
04325    memset(&bio, 0, sizeof(bio));
04326    bio.iocp = BASEMAXINLINE;
04327 
04328    if (!(fi = fopen(filename, "rb"))) {
04329       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04330       return -1;
04331    }
04332 
04333    while (!hiteof){
04334       unsigned char igroup[3], ogroup[4];
04335       int c, n;
04336 
04337       memset(igroup, 0, sizeof(igroup));
04338 
04339       for (n = 0; n < 3; n++) {
04340          if ((c = inchar(&bio, fi)) == EOF) {
04341             hiteof = 1;
04342             break;
04343          }
04344 
04345          igroup[n] = (unsigned char) c;
04346       }
04347 
04348       if (n > 0) {
04349          ogroup[0]= dtable[igroup[0] >> 2];
04350          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04351          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04352          ogroup[3]= dtable[igroup[2] & 0x3F];
04353 
04354          if (n < 3) {
04355             ogroup[3] = '=';
04356 
04357             if (n < 2)
04358                ogroup[2] = '=';
04359          }
04360 
04361          for (i = 0; i < 4; i++)
04362             ochar(&bio, ogroup[i], so);
04363       }
04364    }
04365 
04366    fclose(fi);
04367    
04368    if (fputs(ENDL, so) == EOF) {
04369       return 0;
04370    }
04371 
04372    return 1;
04373 }
04374 
04375 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)
04376 {
04377    char callerid[256];
04378    char num[12];
04379    char fromdir[256], fromfile[256];
04380    struct ast_config *msg_cfg;
04381    const char *origcallerid, *origtime;
04382    char origcidname[80], origcidnum[80], origdate[80];
04383    int inttime;
04384    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04385 
04386    /* Prepare variables for substitution in email body and subject */
04387    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04388    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04389    snprintf(num, sizeof(num), "%d", msgnum);
04390    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04391    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04392    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04393    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04394       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04395    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04396    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04397    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04398    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04399    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04400 
04401    /* Retrieve info from VM attribute file */
04402    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04403    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04404    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04405       strcat(fromfile, ".txt");
04406    }
04407    if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
04408       if (option_debug > 0) {
04409          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04410       }
04411       return;
04412    }
04413 
04414    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04415       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04416       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04417       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04418       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04419    }
04420 
04421    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04422       struct timeval tv = { inttime, };
04423       struct ast_tm tm;
04424       ast_localtime(&tv, &tm, NULL);
04425       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04426       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04427    }
04428    ast_config_destroy(msg_cfg);
04429 }
04430 
04431 /*!
04432  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04433  * \param from The string to work with.
04434  * \param buf The buffer into which to write the modified quoted string.
04435  * \param maxlen Always zero, but see \see ast_str
04436  * 
04437  * \return The destination string with quotes wrapped on it (the to field).
04438  */
04439 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04440 {
04441    const char *ptr;
04442 
04443    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04444    ast_str_set(buf, maxlen, "\"");
04445    for (ptr = from; *ptr; ptr++) {
04446       if (*ptr == '"' || *ptr == '\\') {
04447          ast_str_append(buf, maxlen, "\\%c", *ptr);
04448       } else {
04449          ast_str_append(buf, maxlen, "%c", *ptr);
04450       }
04451    }
04452    ast_str_append(buf, maxlen, "\"");
04453 
04454    return ast_str_buffer(*buf);
04455 }
04456 
04457 /*! \brief
04458  * fill in *tm for current time according to the proper timezone, if any.
04459  * \return tm so it can be used as a function argument.
04460  */
04461 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04462 {
04463    const struct vm_zone *z = NULL;
04464    struct timeval t = ast_tvnow();
04465 
04466    /* Does this user have a timezone specified? */
04467    if (!ast_strlen_zero(vmu->zonetag)) {
04468       /* Find the zone in the list */
04469       AST_LIST_LOCK(&zones);
04470       AST_LIST_TRAVERSE(&zones, z, list) {
04471          if (!strcmp(z->name, vmu->zonetag))
04472             break;
04473       }
04474       AST_LIST_UNLOCK(&zones);
04475    }
04476    ast_localtime(&t, tm, z ? z->timezone : NULL);
04477    return tm;
04478 }
04479 
04480 /*!\brief Check if the string would need encoding within the MIME standard, to
04481  * avoid confusing certain mail software that expects messages to be 7-bit
04482  * clean.
04483  */
04484 static int check_mime(const char *str)
04485 {
04486    for (; *str; str++) {
04487       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04488          return 1;
04489       }
04490    }
04491    return 0;
04492 }
04493 
04494 /*!\brief Encode a string according to the MIME rules for encoding strings
04495  * that are not 7-bit clean or contain control characters.
04496  *
04497  * Additionally, if the encoded string would exceed the MIME limit of 76
04498  * characters per line, then the encoding will be broken up into multiple
04499  * sections, separated by a space character, in order to facilitate
04500  * breaking up the associated header across multiple lines.
04501  *
04502  * \param end An expandable buffer for holding the result
04503  * \param maxlen Always zero, but see \see ast_str
04504  * \param start A string to be encoded
04505  * \param preamble The length of the first line already used for this string,
04506  * to ensure that each line maintains a maximum length of 76 chars.
04507  * \param postamble the length of any additional characters appended to the
04508  * line, used to ensure proper field wrapping.
04509  * \retval The encoded string.
04510  */
04511 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04512 {
04513    struct ast_str *tmp = ast_str_alloca(80);
04514    int first_section = 1;
04515 
04516    ast_str_reset(*end);
04517    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04518    for (; *start; start++) {
04519       int need_encoding = 0;
04520       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04521          need_encoding = 1;
04522       }
04523       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04524          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04525          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04526          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04527          /* Start new line */
04528          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04529          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04530          first_section = 0;
04531       }
04532       if (need_encoding && *start == ' ') {
04533          ast_str_append(&tmp, -1, "_");
04534       } else if (need_encoding) {
04535          ast_str_append(&tmp, -1, "=%hhX", *start);
04536       } else {
04537          ast_str_append(&tmp, -1, "%c", *start);
04538       }
04539    }
04540    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04541    return ast_str_buffer(*end);
04542 }
04543 
04544 /*!
04545  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04546  * \param p The output file to generate the email contents into.
04547  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04548  * \param vmu The voicemail user who is sending the voicemail.
04549  * \param msgnum The message index in the mailbox folder.
04550  * \param context 
04551  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04552  * \param fromfolder
04553  * \param cidnum The caller ID number.
04554  * \param cidname The caller ID name.
04555  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04556  * \param attach2 
04557  * \param format The message sound file format. i.e. .wav
04558  * \param duration The time of the message content, in seconds.
04559  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04560  * \param chan
04561  * \param category
04562  * \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.
04563  * \param flag
04564  *
04565  * 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.
04566  */
04567 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)
04568 {
04569    char date[256];
04570    char host[MAXHOSTNAMELEN] = "";
04571    char who[256];
04572    char bound[256];
04573    char dur[256];
04574    struct ast_tm tm;
04575    char enc_cidnum[256] = "", enc_cidname[256] = "";
04576    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04577    char *greeting_attachment; 
04578    char filename[256];
04579 
04580    if (!str1 || !str2) {
04581       ast_free(str1);
04582       ast_free(str2);
04583       return;
04584    }
04585 
04586    if (cidnum) {
04587       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04588    }
04589    if (cidname) {
04590       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04591    }
04592    gethostname(host, sizeof(host) - 1);
04593 
04594    if (strchr(srcemail, '@')) {
04595       ast_copy_string(who, srcemail, sizeof(who));
04596    } else {
04597       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04598    }
04599 
04600    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04601    if (greeting_attachment) {
04602       *greeting_attachment++ = '\0';
04603    }
04604 
04605    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04606    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04607    fprintf(p, "Date: %s" ENDL, date);
04608 
04609    /* Set date format for voicemail mail */
04610    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04611 
04612    if (!ast_strlen_zero(fromstring)) {
04613       struct ast_channel *ast;
04614       if ((ast = ast_dummy_channel_alloc())) {
04615          char *ptr;
04616          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04617          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04618 
04619          if (check_mime(ast_str_buffer(str1))) {
04620             int first_line = 1;
04621             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04622             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04623                *ptr = '\0';
04624                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04625                first_line = 0;
04626                /* Substring is smaller, so this will never grow */
04627                ast_str_set(&str2, 0, "%s", ptr + 1);
04628             }
04629             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04630          } else {
04631             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04632          }
04633          ast = ast_channel_unref(ast);
04634       } else {
04635          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04636       }
04637    } else {
04638       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04639    }
04640 
04641    if (check_mime(vmu->fullname)) {
04642       int first_line = 1;
04643       char *ptr;
04644       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04645       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04646          *ptr = '\0';
04647          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04648          first_line = 0;
04649          /* Substring is smaller, so this will never grow */
04650          ast_str_set(&str2, 0, "%s", ptr + 1);
04651       }
04652       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04653    } else {
04654       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04655    }
04656 
04657    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04658       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04659       struct ast_channel *ast;
04660       if ((ast = ast_dummy_channel_alloc())) {
04661          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04662          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04663          if (check_mime(ast_str_buffer(str1))) {
04664             int first_line = 1;
04665             char *ptr;
04666             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04667             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04668                *ptr = '\0';
04669                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04670                first_line = 0;
04671                /* Substring is smaller, so this will never grow */
04672                ast_str_set(&str2, 0, "%s", ptr + 1);
04673             }
04674             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04675          } else {
04676             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04677          }
04678          ast = ast_channel_unref(ast);
04679       } else {
04680          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04681       }
04682    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04683       if (ast_strlen_zero(flag)) {
04684          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04685       } else {
04686          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04687       }
04688    } else {
04689       if (ast_strlen_zero(flag)) {
04690          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04691       } else {
04692          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04693       }
04694    }
04695 
04696    fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
04697       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04698    if (imap) {
04699       /* additional information needed for IMAP searching */
04700       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04701       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04702       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04703       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04704 #ifdef IMAP_STORAGE
04705       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04706 #else
04707       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04708 #endif
04709       /* flag added for Urgent */
04710       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04711       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04712       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04713       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04714       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04715       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04716       if (!ast_strlen_zero(category)) {
04717          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04718       } else {
04719          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04720       }
04721       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04722       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04723       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04724    }
04725    if (!ast_strlen_zero(cidnum)) {
04726       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04727    }
04728    if (!ast_strlen_zero(cidname)) {
04729       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04730    }
04731    fprintf(p, "MIME-Version: 1.0" ENDL);
04732    if (attach_user_voicemail) {
04733       /* Something unique. */
04734       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
04735          (int) getpid(), (unsigned int) ast_random());
04736 
04737       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04738       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04739       fprintf(p, "--%s" ENDL, bound);
04740    }
04741    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04742    if (emailbody || vmu->emailbody) {
04743       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04744       struct ast_channel *ast;
04745       if ((ast = ast_dummy_channel_alloc())) {
04746          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04747          ast_str_substitute_variables(&str1, 0, ast, e_body);
04748 #ifdef IMAP_STORAGE
04749             {
04750                /* Convert body to native line terminators for IMAP backend */
04751                char *line = ast_str_buffer(str1), *next;
04752                do {
04753                   /* Terminate line before outputting it to the file */
04754                   if ((next = strchr(line, '\n'))) {
04755                      *next++ = '\0';
04756                   }
04757                   fprintf(p, "%s" ENDL, line);
04758                   line = next;
04759                } while (!ast_strlen_zero(line));
04760             }
04761 #else
04762          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04763 #endif
04764          ast = ast_channel_unref(ast);
04765       } else {
04766          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04767       }
04768    } else if (msgnum > -1) {
04769       if (strcmp(vmu->mailbox, mailbox)) {
04770          /* Forwarded type */
04771          struct ast_config *msg_cfg;
04772          const char *v;
04773          int inttime;
04774          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04775          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04776          /* Retrieve info from VM attribute file */
04777          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04778          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04779          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04780             strcat(fromfile, ".txt");
04781          }
04782          if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
04783             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04784                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04785             }
04786 
04787             /* You might be tempted to do origdate, except that a) it's in the wrong
04788              * format, and b) it's missing for IMAP recordings. */
04789             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04790                struct timeval tv = { inttime, };
04791                struct ast_tm tm;
04792                ast_localtime(&tv, &tm, NULL);
04793                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04794             }
04795             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04796                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04797                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04798                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04799                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04800                date, origcallerid, origdate);
04801             ast_config_destroy(msg_cfg);
04802          } else {
04803             goto plain_message;
04804          }
04805       } else {
04806 plain_message:
04807          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04808             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04809             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04810             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04811             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04812       }
04813    } else {
04814       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04815             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04816    }
04817 
04818    if (imap || attach_user_voicemail) {
04819       if (!ast_strlen_zero(attach2)) {
04820          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04821          ast_debug(5, "creating second attachment filename %s\n", filename);
04822          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04823          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04824          ast_debug(5, "creating attachment filename %s\n", filename);
04825          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04826       } else {
04827          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04828          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04829          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04830       }
04831    }
04832    ast_free(str1);
04833    ast_free(str2);
04834 }
04835 
04836 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)
04837 {
04838    char tmpdir[256], newtmp[256];
04839    char fname[256];
04840    char tmpcmd[256];
04841    int tmpfd = -1;
04842    int soxstatus = 0;
04843 
04844    /* Eww. We want formats to tell us their own MIME type */
04845    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04846 
04847    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04848       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04849       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04850       tmpfd = mkstemp(newtmp);
04851       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04852       ast_debug(3, "newtmp: %s\n", newtmp);
04853       if (tmpfd > -1) {
04854          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04855          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04856             attach = newtmp;
04857             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04858          } else {
04859             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04860                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04861             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04862          }
04863       }
04864    }
04865    fprintf(p, "--%s" ENDL, bound);
04866    if (msgnum > -1)
04867       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04868    else
04869       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04870    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04871    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04872    if (msgnum > -1)
04873       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04874    else
04875       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04876    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04877    base_encode(fname, p);
04878    if (last)
04879       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04880    if (tmpfd > -1) {
04881       if (soxstatus == 0) {
04882          unlink(fname);
04883       }
04884       close(tmpfd);
04885       unlink(newtmp);
04886    }
04887    return 0;
04888 }
04889 
04890 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)
04891 {
04892    FILE *p = NULL;
04893    char tmp[80] = "/tmp/astmail-XXXXXX";
04894    char tmp2[256];
04895    char *stringp;
04896 
04897    if (vmu && ast_strlen_zero(vmu->email)) {
04898       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04899       return(0);
04900    }
04901 
04902    /* Mail only the first format */
04903    format = ast_strdupa(format);
04904    stringp = format;
04905    strsep(&stringp, "|");
04906 
04907    if (!strcmp(format, "wav49"))
04908       format = "WAV";
04909    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));
04910    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04911       command hangs */
04912    if ((p = vm_mkftemp(tmp)) == NULL) {
04913       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04914       return -1;
04915    } else {
04916       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04917       fclose(p);
04918       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04919       ast_safe_system(tmp2);
04920       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04921    }
04922    return 0;
04923 }
04924 
04925 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)
04926 {
04927    char enc_cidnum[256], enc_cidname[256];
04928    char date[256];
04929    char host[MAXHOSTNAMELEN] = "";
04930    char who[256];
04931    char dur[PATH_MAX];
04932    char tmp[80] = "/tmp/astmail-XXXXXX";
04933    char tmp2[PATH_MAX];
04934    struct ast_tm tm;
04935    FILE *p;
04936    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04937 
04938    if (!str1 || !str2) {
04939       ast_free(str1);
04940       ast_free(str2);
04941       return -1;
04942    }
04943 
04944    if (cidnum) {
04945       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04946    }
04947    if (cidname) {
04948       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04949    }
04950 
04951    if ((p = vm_mkftemp(tmp)) == NULL) {
04952       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04953       ast_free(str1);
04954       ast_free(str2);
04955       return -1;
04956    }
04957    gethostname(host, sizeof(host)-1);
04958    if (strchr(srcemail, '@')) {
04959       ast_copy_string(who, srcemail, sizeof(who));
04960    } else {
04961       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04962    }
04963    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04964    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04965    fprintf(p, "Date: %s\n", date);
04966 
04967    /* Reformat for custom pager format */
04968    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04969 
04970    if (!ast_strlen_zero(pagerfromstring)) {
04971       struct ast_channel *ast;
04972       if ((ast = ast_dummy_channel_alloc())) {
04973          char *ptr;
04974          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04975          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04976 
04977          if (check_mime(ast_str_buffer(str1))) {
04978             int first_line = 1;
04979             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04980             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04981                *ptr = '\0';
04982                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04983                first_line = 0;
04984                /* Substring is smaller, so this will never grow */
04985                ast_str_set(&str2, 0, "%s", ptr + 1);
04986             }
04987             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04988          } else {
04989             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04990          }
04991          ast = ast_channel_unref(ast);
04992       } else {
04993          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04994       }
04995    } else {
04996       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04997    }
04998 
04999    if (check_mime(vmu->fullname)) {
05000       int first_line = 1;
05001       char *ptr;
05002       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
05003       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05004          *ptr = '\0';
05005          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
05006          first_line = 0;
05007          /* Substring is smaller, so this will never grow */
05008          ast_str_set(&str2, 0, "%s", ptr + 1);
05009       }
05010       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
05011    } else {
05012       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
05013    }
05014 
05015    if (!ast_strlen_zero(pagersubject)) {
05016       struct ast_channel *ast;
05017       if ((ast = ast_dummy_channel_alloc())) {
05018          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05019          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05020          if (check_mime(ast_str_buffer(str1))) {
05021             int first_line = 1;
05022             char *ptr;
05023             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05024             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05025                *ptr = '\0';
05026                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05027                first_line = 0;
05028                /* Substring is smaller, so this will never grow */
05029                ast_str_set(&str2, 0, "%s", ptr + 1);
05030             }
05031             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05032          } else {
05033             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05034          }
05035          ast = ast_channel_unref(ast);
05036       } else {
05037          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05038       }
05039    } else {
05040       if (ast_strlen_zero(flag)) {
05041          fprintf(p, "Subject: New VM\n\n");
05042       } else {
05043          fprintf(p, "Subject: New %s VM\n\n", flag);
05044       }
05045    }
05046 
05047    if (pagerbody) {
05048       struct ast_channel *ast;
05049       if ((ast = ast_dummy_channel_alloc())) {
05050          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05051          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05052          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05053          ast = ast_channel_unref(ast);
05054       } else {
05055          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05056       }
05057    } else {
05058       fprintf(p, "New %s long %s msg in box %s\n"
05059             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05060    }
05061 
05062    fclose(p);
05063    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05064    ast_safe_system(tmp2);
05065    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05066    ast_free(str1);
05067    ast_free(str2);
05068    return 0;
05069 }
05070 
05071 /*!
05072  * \brief Gets the current date and time, as formatted string.
05073  * \param s The buffer to hold the output formatted date.
05074  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05075  * 
05076  * The date format string used is "%a %b %e %r UTC %Y".
05077  * 
05078  * \return zero on success, -1 on error.
05079  */
05080 static int get_date(char *s, int len)
05081 {
05082    struct ast_tm tm;
05083    struct timeval t = ast_tvnow();
05084    
05085    ast_localtime(&t, &tm, "UTC");
05086 
05087    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05088 }
05089 
05090 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05091 {
05092    int res;
05093    char fn[PATH_MAX];
05094    char dest[PATH_MAX];
05095 
05096    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05097 
05098    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05099       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05100       return -1;
05101    }
05102 
05103    RETRIEVE(fn, -1, ext, context);
05104    if (ast_fileexists(fn, NULL, NULL) > 0) {
05105       res = ast_stream_and_wait(chan, fn, ecodes);
05106       if (res) {
05107          DISPOSE(fn, -1);
05108          return res;
05109       }
05110    } else {
05111       /* Dispose just in case */
05112       DISPOSE(fn, -1);
05113       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05114       if (res)
05115          return res;
05116       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05117       if (res)
05118          return res;
05119    }
05120    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05121    return res;
05122 }
05123 
05124 static void free_zone(struct vm_zone *z)
05125 {
05126    ast_free(z);
05127 }
05128 
05129 #ifdef ODBC_STORAGE
05130 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05131 {
05132    int x = -1;
05133    int res;
05134    SQLHSTMT stmt = NULL;
05135    char sql[PATH_MAX];
05136    char rowdata[20];
05137    char tmp[PATH_MAX] = "";
05138    struct odbc_obj *obj = NULL;
05139    char *context;
05140    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05141 
05142    if (newmsgs)
05143       *newmsgs = 0;
05144    if (oldmsgs)
05145       *oldmsgs = 0;
05146    if (urgentmsgs)
05147       *urgentmsgs = 0;
05148 
05149    /* If no mailbox, return immediately */
05150    if (ast_strlen_zero(mailbox))
05151       return 0;
05152 
05153    ast_copy_string(tmp, mailbox, sizeof(tmp));
05154 
05155    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05156       int u, n, o;
05157       char *next, *remaining = tmp;
05158       while ((next = strsep(&remaining, " ,"))) {
05159          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05160             return -1;
05161          }
05162          if (urgentmsgs) {
05163             *urgentmsgs += u;
05164          }
05165          if (newmsgs) {
05166             *newmsgs += n;
05167          }
05168          if (oldmsgs) {
05169             *oldmsgs += o;
05170          }
05171       }
05172       return 0;
05173    }
05174 
05175    context = strchr(tmp, '@');
05176    if (context) {
05177       *context = '\0';
05178       context++;
05179    } else
05180       context = "default";
05181 
05182    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05183       do {
05184          if (newmsgs) {
05185             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05186             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05187                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05188                break;
05189             }
05190             res = SQLFetch(stmt);
05191             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05192                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05193                break;
05194             }
05195             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05196             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05197                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05198                break;
05199             }
05200             *newmsgs = atoi(rowdata);
05201             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05202          }
05203 
05204          if (oldmsgs) {
05205             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05206             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05207                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05208                break;
05209             }
05210             res = SQLFetch(stmt);
05211             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05212                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05213                break;
05214             }
05215             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05216             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05217                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05218                break;
05219             }
05220             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05221             *oldmsgs = atoi(rowdata);
05222          }
05223 
05224          if (urgentmsgs) {
05225             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05226             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05227                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05228                break;
05229             }
05230             res = SQLFetch(stmt);
05231             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05232                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05233                break;
05234             }
05235             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05236             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05237                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05238                break;
05239             }
05240             *urgentmsgs = atoi(rowdata);
05241          }
05242 
05243          x = 0;
05244       } while (0);
05245    } else {
05246       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05247    }
05248 
05249    if (stmt) {
05250       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05251    }
05252    if (obj) {
05253       ast_odbc_release_obj(obj);
05254    }
05255    return x;
05256 }
05257 
05258 /*!
05259  * \brief Gets the number of messages that exist in a mailbox folder.
05260  * \param context
05261  * \param mailbox
05262  * \param folder
05263  * 
05264  * This method is used when ODBC backend is used.
05265  * \return The number of messages in this mailbox folder (zero or more).
05266  */
05267 static int messagecount(const char *context, const char *mailbox, const char *folder)
05268 {
05269    struct odbc_obj *obj = NULL;
05270    int nummsgs = 0;
05271    int res;
05272    SQLHSTMT stmt = NULL;
05273    char sql[PATH_MAX];
05274    char rowdata[20];
05275    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05276    if (!folder)
05277       folder = "INBOX";
05278    /* If no mailbox, return immediately */
05279    if (ast_strlen_zero(mailbox))
05280       return 0;
05281 
05282    obj = ast_odbc_request_obj(odbc_database, 0);
05283    if (obj) {
05284       if (!strcmp(folder, "INBOX")) {
05285          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);
05286       } else {
05287          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05288       }
05289       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05290       if (!stmt) {
05291          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05292          goto yuck;
05293       }
05294       res = SQLFetch(stmt);
05295       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05296          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05297          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05298          goto yuck;
05299       }
05300       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05301       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05302          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05303          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05304          goto yuck;
05305       }
05306       nummsgs = atoi(rowdata);
05307       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05308    } else
05309       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05310 
05311 yuck:
05312    if (obj)
05313       ast_odbc_release_obj(obj);
05314    return nummsgs;
05315 }
05316 
05317 /** 
05318  * \brief Determines if the given folder has messages.
05319  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05320  * 
05321  * This function is used when the mailbox is stored in an ODBC back end.
05322  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05323  * \return 1 if the folder has one or more messages. zero otherwise.
05324  */
05325 static int has_voicemail(const char *mailbox, const char *folder)
05326 {
05327    char tmp[256], *tmp2 = tmp, *box, *context;
05328    ast_copy_string(tmp, mailbox, sizeof(tmp));
05329    while ((context = box = strsep(&tmp2, ",&"))) {
05330       strsep(&context, "@");
05331       if (ast_strlen_zero(context))
05332          context = "default";
05333       if (messagecount(context, box, folder))
05334          return 1;
05335    }
05336    return 0;
05337 }
05338 #endif
05339 #ifndef IMAP_STORAGE
05340 /*! 
05341  * \brief Copies a message from one mailbox to another.
05342  * \param chan
05343  * \param vmu
05344  * \param imbox
05345  * \param msgnum
05346  * \param duration
05347  * \param recip
05348  * \param fmt
05349  * \param dir
05350  * \param flag
05351  *
05352  * This is only used by file storage based mailboxes.
05353  *
05354  * \return zero on success, -1 on error.
05355  */
05356 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)
05357 {
05358    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05359    const char *frombox = mbox(vmu, imbox);
05360    const char *userfolder;
05361    int recipmsgnum;
05362    int res = 0;
05363 
05364    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05365 
05366    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05367       userfolder = "Urgent";
05368    } else {
05369       userfolder = "INBOX";
05370    }
05371 
05372    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05373 
05374    if (!dir)
05375       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05376    else
05377       ast_copy_string(fromdir, dir, sizeof(fromdir));
05378 
05379    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05380    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05381 
05382    if (vm_lock_path(todir))
05383       return ERROR_LOCK_PATH;
05384 
05385    recipmsgnum = last_message_index(recip, todir) + 1;
05386    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05387       make_file(topath, sizeof(topath), todir, recipmsgnum);
05388 #ifndef ODBC_STORAGE
05389       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05390          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05391       } else {
05392 #endif
05393          /* If we are prepending a message for ODBC, then the message already
05394           * exists in the database, but we want to force copying from the
05395           * filesystem (since only the FS contains the prepend). */
05396          copy_plain_file(frompath, topath);
05397          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05398          vm_delete(topath);
05399 #ifndef ODBC_STORAGE
05400       }
05401 #endif
05402    } else {
05403       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05404       res = -1;
05405    }
05406    ast_unlock_path(todir);
05407    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05408       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05409       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05410       flag);
05411    
05412    return res;
05413 }
05414 #endif
05415 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05416 
05417 static int messagecount(const char *context, const char *mailbox, const char *folder)
05418 {
05419    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05420 }
05421 
05422 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05423 {
05424    DIR *dir;
05425    struct dirent *de;
05426    char fn[256];
05427    int ret = 0;
05428 
05429    /* If no mailbox, return immediately */
05430    if (ast_strlen_zero(mailbox))
05431       return 0;
05432 
05433    if (ast_strlen_zero(folder))
05434       folder = "INBOX";
05435    if (ast_strlen_zero(context))
05436       context = "default";
05437 
05438    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05439 
05440    if (!(dir = opendir(fn)))
05441       return 0;
05442 
05443    while ((de = readdir(dir))) {
05444       if (!strncasecmp(de->d_name, "msg", 3)) {
05445          if (shortcircuit) {
05446             ret = 1;
05447             break;
05448          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05449             ret++;
05450          }
05451       }
05452    }
05453 
05454    closedir(dir);
05455 
05456    return ret;
05457 }
05458 
05459 /** 
05460  * \brief Determines if the given folder has messages.
05461  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05462  * \param folder the folder to look in
05463  *
05464  * This function is used when the mailbox is stored in a filesystem back end.
05465  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05466  * \return 1 if the folder has one or more messages. zero otherwise.
05467  */
05468 static int has_voicemail(const char *mailbox, const char *folder)
05469 {
05470    char tmp[256], *tmp2 = tmp, *box, *context;
05471    ast_copy_string(tmp, mailbox, sizeof(tmp));
05472    if (ast_strlen_zero(folder)) {
05473       folder = "INBOX";
05474    }
05475    while ((box = strsep(&tmp2, ",&"))) {
05476       if ((context = strchr(box, '@')))
05477          *context++ = '\0';
05478       else
05479          context = "default";
05480       if (__has_voicemail(context, box, folder, 1))
05481          return 1;
05482       /* If we are checking INBOX, we should check Urgent as well */
05483       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05484          return 1;
05485       }
05486    }
05487    return 0;
05488 }
05489 
05490 
05491 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05492 {
05493    char tmp[256];
05494    char *context;
05495 
05496    /* If no mailbox, return immediately */
05497    if (ast_strlen_zero(mailbox))
05498       return 0;
05499 
05500    if (newmsgs)
05501       *newmsgs = 0;
05502    if (oldmsgs)
05503       *oldmsgs = 0;
05504    if (urgentmsgs)
05505       *urgentmsgs = 0;
05506 
05507    if (strchr(mailbox, ',')) {
05508       int tmpnew, tmpold, tmpurgent;
05509       char *mb, *cur;
05510 
05511       ast_copy_string(tmp, mailbox, sizeof(tmp));
05512       mb = tmp;
05513       while ((cur = strsep(&mb, ", "))) {
05514          if (!ast_strlen_zero(cur)) {
05515             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05516                return -1;
05517             else {
05518                if (newmsgs)
05519                   *newmsgs += tmpnew; 
05520                if (oldmsgs)
05521                   *oldmsgs += tmpold;
05522                if (urgentmsgs)
05523                   *urgentmsgs += tmpurgent;
05524             }
05525          }
05526       }
05527       return 0;
05528    }
05529 
05530    ast_copy_string(tmp, mailbox, sizeof(tmp));
05531    
05532    if ((context = strchr(tmp, '@')))
05533       *context++ = '\0';
05534    else
05535       context = "default";
05536 
05537    if (newmsgs)
05538       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05539    if (oldmsgs)
05540       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05541    if (urgentmsgs)
05542       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05543 
05544    return 0;
05545 }
05546 
05547 #endif
05548 
05549 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05550 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05551 {
05552    int urgentmsgs = 0;
05553    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05554    if (newmsgs) {
05555       *newmsgs += urgentmsgs;
05556    }
05557    return res;
05558 }
05559 
05560 static void run_externnotify(char *context, char *extension, const char *flag)
05561 {
05562    char arguments[255];
05563    char ext_context[256] = "";
05564    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05565    struct ast_smdi_mwi_message *mwi_msg;
05566 
05567    if (!ast_strlen_zero(context))
05568       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05569    else
05570       ast_copy_string(ext_context, extension, sizeof(ext_context));
05571 
05572    if (smdi_iface) {
05573       if (ast_app_has_voicemail(ext_context, NULL)) 
05574          ast_smdi_mwi_set(smdi_iface, extension);
05575       else
05576          ast_smdi_mwi_unset(smdi_iface, extension);
05577 
05578       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05579          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05580          if (!strncmp(mwi_msg->cause, "INV", 3))
05581             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05582          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05583             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05584          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05585          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05586       } else {
05587          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05588       }
05589    }
05590 
05591    if (!ast_strlen_zero(externnotify)) {
05592       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05593          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05594       } else {
05595          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
05596             externnotify, S_OR(context, "\"\""),
05597             extension, newvoicemails,
05598             oldvoicemails, urgentvoicemails);
05599          ast_debug(1, "Executing %s\n", arguments);
05600          ast_safe_system(arguments);
05601       }
05602    }
05603 }
05604 
05605 /*!
05606  * \brief Variables used for saving a voicemail.
05607  *
05608  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05609  */
05610 struct leave_vm_options {
05611    unsigned int flags;
05612    signed char record_gain;
05613    char *exitcontext;
05614 };
05615 
05616 /*!
05617  * \brief Prompts the user and records a voicemail to a mailbox.
05618  * \param chan
05619  * \param ext
05620  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05621  * 
05622  * 
05623  * 
05624  * \return zero on success, -1 on error.
05625  */
05626 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05627 {
05628 #ifdef IMAP_STORAGE
05629    int newmsgs, oldmsgs;
05630 #else
05631    char urgdir[PATH_MAX];
05632 #endif
05633    char txtfile[PATH_MAX];
05634    char tmptxtfile[PATH_MAX];
05635    struct vm_state *vms = NULL;
05636    char callerid[256];
05637    FILE *txt;
05638    char date[256];
05639    int txtdes;
05640    int res = 0;
05641    int msgnum;
05642    int duration = 0;
05643    int sound_duration = 0;
05644    int ausemacro = 0;
05645    int ousemacro = 0;
05646    int ouseexten = 0;
05647    char tmpdur[16];
05648    char priority[16];
05649    char origtime[16];
05650    char dir[PATH_MAX];
05651    char tmpdir[PATH_MAX];
05652    char fn[PATH_MAX];
05653    char prefile[PATH_MAX] = "";
05654    char tempfile[PATH_MAX] = "";
05655    char ext_context[256] = "";
05656    char fmt[80];
05657    char *context;
05658    char ecodes[17] = "#";
05659    struct ast_str *tmp = ast_str_create(16);
05660    char *tmpptr;
05661    struct ast_vm_user *vmu;
05662    struct ast_vm_user svm;
05663    const char *category = NULL;
05664    const char *code;
05665    const char *alldtmf = "0123456789ABCD*#";
05666    char flag[80];
05667 
05668    if (!tmp) {
05669       return -1;
05670    }
05671 
05672    ast_str_set(&tmp, 0, "%s", ext);
05673    ext = ast_str_buffer(tmp);
05674    if ((context = strchr(ext, '@'))) {
05675       *context++ = '\0';
05676       tmpptr = strchr(context, '&');
05677    } else {
05678       tmpptr = strchr(ext, '&');
05679    }
05680 
05681    if (tmpptr)
05682       *tmpptr++ = '\0';
05683 
05684    ast_channel_lock(chan);
05685    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05686       category = ast_strdupa(category);
05687    }
05688    ast_channel_unlock(chan);
05689 
05690    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05691       ast_copy_string(flag, "Urgent", sizeof(flag));
05692    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05693       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05694    } else {
05695       flag[0] = '\0';
05696    }
05697 
05698    ast_debug(3, "Before find_user\n");
05699    if (!(vmu = find_user(&svm, context, ext))) {
05700       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05701       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05702       ast_free(tmp);
05703       return res;
05704    }
05705    /* Setup pre-file if appropriate */
05706    if (strcmp(vmu->context, "default"))
05707       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05708    else
05709       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05710 
05711    /* Set the path to the prefile. Will be one of 
05712       VM_SPOOL_DIRcontext/ext/busy
05713       VM_SPOOL_DIRcontext/ext/unavail
05714       Depending on the flag set in options.
05715    */
05716    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05717       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05718    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05719       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05720    }
05721    /* Set the path to the tmpfile as
05722       VM_SPOOL_DIR/context/ext/temp
05723       and attempt to create the folder structure.
05724    */
05725    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05726    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05727       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05728       ast_free(tmp);
05729       return -1;
05730    }
05731    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05732    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05733       ast_copy_string(prefile, tempfile, sizeof(prefile));
05734 
05735    DISPOSE(tempfile, -1);
05736    /* It's easier just to try to make it than to check for its existence */
05737 #ifndef IMAP_STORAGE
05738    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05739 #else
05740    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05741    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05742       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05743    }
05744 #endif
05745 
05746    /* Check current or macro-calling context for special extensions */
05747    if (ast_test_flag(vmu, VM_OPERATOR)) {
05748       if (!ast_strlen_zero(vmu->exit)) {
05749          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05750             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05751             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05752             ouseexten = 1;
05753          }
05754       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05755          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05756          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05757          ouseexten = 1;
05758       } else if (!ast_strlen_zero(chan->macrocontext)
05759          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05760             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05761          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05762          ousemacro = 1;
05763       }
05764    }
05765 
05766    if (!ast_strlen_zero(vmu->exit)) {
05767       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05768          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05769          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05770       }
05771    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05772       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05773       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05774    } else if (!ast_strlen_zero(chan->macrocontext)
05775       && ast_exists_extension(chan, chan->macrocontext, "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       ausemacro = 1;
05779    }
05780 
05781    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05782       for (code = alldtmf; *code; code++) {
05783          char e[2] = "";
05784          e[0] = *code;
05785          if (strchr(ecodes, e[0]) == NULL
05786             && ast_canmatch_extension(chan,
05787                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05788                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05789             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05790          }
05791       }
05792    }
05793 
05794    /* Play the beginning intro if desired */
05795    if (!ast_strlen_zero(prefile)) {
05796 #ifdef ODBC_STORAGE
05797       int success = 
05798 #endif
05799          RETRIEVE(prefile, -1, ext, context);
05800       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05801          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05802             res = ast_waitstream(chan, ecodes);
05803 #ifdef ODBC_STORAGE
05804          if (success == -1) {
05805             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05806             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05807             store_file(prefile, vmu->mailbox, vmu->context, -1);
05808          }
05809 #endif
05810       } else {
05811          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05812          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05813       }
05814       DISPOSE(prefile, -1);
05815       if (res < 0) {
05816          ast_debug(1, "Hang up during prefile playback\n");
05817          free_user(vmu);
05818          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05819          ast_free(tmp);
05820          return -1;
05821       }
05822    }
05823    if (res == '#') {
05824       /* On a '#' we skip the instructions */
05825       ast_set_flag(options, OPT_SILENT);
05826       res = 0;
05827    }
05828    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05829    if (vmu->maxmsg == 0) {
05830       if (option_debug > 2)
05831          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05832       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05833       goto leave_vm_out;
05834    }
05835    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05836       res = ast_stream_and_wait(chan, INTRO, ecodes);
05837       if (res == '#') {
05838          ast_set_flag(options, OPT_SILENT);
05839          res = 0;
05840       }
05841    }
05842    if (res > 0)
05843       ast_stopstream(chan);
05844    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05845     other than the operator -- an automated attendant or mailbox login for example */
05846    if (res == '*') {
05847       chan->exten[0] = 'a';
05848       chan->exten[1] = '\0';
05849       if (!ast_strlen_zero(vmu->exit)) {
05850          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05851       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05852          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05853       }
05854       chan->priority = 0;
05855       free_user(vmu);
05856       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05857       ast_free(tmp);
05858       return 0;
05859    }
05860 
05861    /* Check for a '0' here */
05862    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05863    transfer:
05864       if (ouseexten || ousemacro) {
05865          chan->exten[0] = 'o';
05866          chan->exten[1] = '\0';
05867          if (!ast_strlen_zero(vmu->exit)) {
05868             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05869          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05870             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05871          }
05872          ast_play_and_wait(chan, "transfer");
05873          chan->priority = 0;
05874          free_user(vmu);
05875          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05876       }
05877       ast_free(tmp);
05878       return OPERATOR_EXIT;
05879    }
05880 
05881    /* Allow all other digits to exit Voicemail and return to the dialplan */
05882    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05883       if (!ast_strlen_zero(options->exitcontext)) {
05884          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05885       }
05886       free_user(vmu);
05887       ast_free(tmp);
05888       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05889       return res;
05890    }
05891 
05892    if (res < 0) {
05893       free_user(vmu);
05894       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05895       ast_free(tmp);
05896       return -1;
05897    }
05898    /* The meat of recording the message...  All the announcements and beeps have been played*/
05899    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05900    if (!ast_strlen_zero(fmt)) {
05901       msgnum = 0;
05902 
05903 #ifdef IMAP_STORAGE
05904       /* Is ext a mailbox? */
05905       /* must open stream for this user to get info! */
05906       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05907       if (res < 0) {
05908          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05909          ast_free(tmp);
05910          return -1;
05911       }
05912       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05913       /* It is possible under certain circumstances that inboxcount did not
05914        * create a vm_state when it was needed. This is a catchall which will
05915        * rarely be used.
05916        */
05917          if (!(vms = create_vm_state_from_user(vmu))) {
05918             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05919             ast_free(tmp);
05920             return -1;
05921          }
05922       }
05923       vms->newmessages++;
05924       
05925       /* here is a big difference! We add one to it later */
05926       msgnum = newmsgs + oldmsgs;
05927       ast_debug(3, "Messagecount set to %d\n", msgnum);
05928       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05929       /* set variable for compatibility */
05930       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05931 
05932       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05933          goto leave_vm_out;
05934       }
05935 #else
05936       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05937          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05938          if (!res)
05939             res = ast_waitstream(chan, "");
05940          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05941          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05942          inprocess_count(vmu->mailbox, vmu->context, -1);
05943          goto leave_vm_out;
05944       }
05945 
05946 #endif
05947       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05948       txtdes = mkstemp(tmptxtfile);
05949       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05950       if (txtdes < 0) {
05951          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05952          if (!res)
05953             res = ast_waitstream(chan, "");
05954          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05955          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05956          inprocess_count(vmu->mailbox, vmu->context, -1);
05957          goto leave_vm_out;
05958       }
05959 
05960       /* Now play the beep once we have the message number for our next message. */
05961       if (res >= 0) {
05962          /* Unless we're *really* silent, try to send the beep */
05963          res = ast_stream_and_wait(chan, "beep", "");
05964       }
05965             
05966       /* Store information in real-time storage */
05967       if (ast_check_realtime("voicemail_data")) {
05968          snprintf(priority, sizeof(priority), "%d", chan->priority);
05969          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05970          get_date(date, sizeof(date));
05971          ast_callerid_merge(callerid, sizeof(callerid),
05972             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05973             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05974             "Unknown");
05975          ast_store_realtime("voicemail_data",
05976             "origmailbox", ext,
05977             "context", chan->context,
05978             "macrocontext", chan->macrocontext,
05979             "exten", chan->exten,
05980             "priority", priority,
05981             "callerchan", chan->name,
05982             "callerid", callerid,
05983             "origdate", date,
05984             "origtime", origtime,
05985             "category", S_OR(category, ""),
05986             "filename", tmptxtfile,
05987             SENTINEL);
05988       }
05989 
05990       /* Store information */
05991       txt = fdopen(txtdes, "w+");
05992       if (txt) {
05993          get_date(date, sizeof(date));
05994          ast_callerid_merge(callerid, sizeof(callerid),
05995             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05996             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05997             "Unknown");
05998          fprintf(txt, 
05999             ";\n"
06000             "; Message Information file\n"
06001             ";\n"
06002             "[message]\n"
06003             "origmailbox=%s\n"
06004             "context=%s\n"
06005             "macrocontext=%s\n"
06006             "exten=%s\n"
06007             "rdnis=%s\n"
06008             "priority=%d\n"
06009             "callerchan=%s\n"
06010             "callerid=%s\n"
06011             "origdate=%s\n"
06012             "origtime=%ld\n"
06013             "category=%s\n",
06014             ext,
06015             chan->context,
06016             chan->macrocontext, 
06017             chan->exten,
06018             S_COR(chan->redirecting.from.number.valid,
06019                chan->redirecting.from.number.str, "unknown"),
06020             chan->priority,
06021             chan->name,
06022             callerid,
06023             date, (long) time(NULL),
06024             category ? category : "");
06025       } else {
06026          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06027          inprocess_count(vmu->mailbox, vmu->context, -1);
06028          if (ast_check_realtime("voicemail_data")) {
06029             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06030          }
06031          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
06032          goto leave_vm_out;
06033       }
06034       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06035 
06036       if (txt) {
06037          fprintf(txt, "flag=%s\n", flag);
06038          if (sound_duration < vmu->minsecs) {
06039             fclose(txt);
06040             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06041             ast_filedelete(tmptxtfile, NULL);
06042             unlink(tmptxtfile);
06043             if (ast_check_realtime("voicemail_data")) {
06044                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06045             }
06046             inprocess_count(vmu->mailbox, vmu->context, -1);
06047          } else {
06048             fprintf(txt, "duration=%d\n", duration);
06049             fclose(txt);
06050             if (vm_lock_path(dir)) {
06051                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06052                /* Delete files */
06053                ast_filedelete(tmptxtfile, NULL);
06054                unlink(tmptxtfile);
06055                inprocess_count(vmu->mailbox, vmu->context, -1);
06056             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06057                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06058                unlink(tmptxtfile);
06059                ast_unlock_path(dir);
06060                inprocess_count(vmu->mailbox, vmu->context, -1);
06061                if (ast_check_realtime("voicemail_data")) {
06062                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06063                }
06064             } else {
06065 #ifndef IMAP_STORAGE
06066                msgnum = last_message_index(vmu, dir) + 1;
06067 #endif
06068                make_file(fn, sizeof(fn), dir, msgnum);
06069 
06070                /* assign a variable with the name of the voicemail file */ 
06071 #ifndef IMAP_STORAGE
06072                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06073 #else
06074                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06075 #endif
06076 
06077                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06078                ast_filerename(tmptxtfile, fn, NULL);
06079                rename(tmptxtfile, txtfile);
06080                inprocess_count(vmu->mailbox, vmu->context, -1);
06081 
06082                /* Properly set permissions on voicemail text descriptor file.
06083                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06084                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06085                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06086 
06087                ast_unlock_path(dir);
06088                if (ast_check_realtime("voicemail_data")) {
06089                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06090                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06091                }
06092                /* We must store the file first, before copying the message, because
06093                 * ODBC storage does the entire copy with SQL.
06094                 */
06095                if (ast_fileexists(fn, NULL, NULL) > 0) {
06096                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06097                }
06098 
06099                /* Are there to be more recipients of this message? */
06100                while (tmpptr) {
06101                   struct ast_vm_user recipu, *recip;
06102                   char *exten, *cntx;
06103 
06104                   exten = strsep(&tmpptr, "&");
06105                   cntx = strchr(exten, '@');
06106                   if (cntx) {
06107                      *cntx = '\0';
06108                      cntx++;
06109                   }
06110                   if ((recip = find_user(&recipu, cntx, exten))) {
06111                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06112                      free_user(recip);
06113                   }
06114                }
06115 #ifndef IMAP_STORAGE
06116                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06117                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06118                   char sfn[PATH_MAX];
06119                   char dfn[PATH_MAX];
06120                   int x;
06121                   /* It's easier just to try to make it than to check for its existence */
06122                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06123                   x = last_message_index(vmu, urgdir) + 1;
06124                   make_file(sfn, sizeof(sfn), dir, msgnum);
06125                   make_file(dfn, sizeof(dfn), urgdir, x);
06126                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06127                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06128                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06129                   ast_copy_string(fn, dfn, sizeof(fn));
06130                   msgnum = x;
06131                }
06132 #endif
06133                /* Notification needs to happen after the copy, though. */
06134                if (ast_fileexists(fn, NULL, NULL)) {
06135 #ifdef IMAP_STORAGE
06136                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06137                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06138                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06139                      flag);
06140 #else
06141                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06142                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06143                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06144                      flag);
06145 #endif
06146                }
06147 
06148                /* Disposal needs to happen after the optional move and copy */
06149                if (ast_fileexists(fn, NULL, NULL)) {
06150                   DISPOSE(dir, msgnum);
06151                }
06152             }
06153          }
06154       } else {
06155          inprocess_count(vmu->mailbox, vmu->context, -1);
06156       }
06157       if (res == '0') {
06158          goto transfer;
06159       } else if (res > 0 && res != 't')
06160          res = 0;
06161 
06162       if (sound_duration < vmu->minsecs)
06163          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06164          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06165       else
06166          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06167    } else
06168       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06169 leave_vm_out:
06170    free_user(vmu);
06171 
06172 #ifdef IMAP_STORAGE
06173    /* expunge message - use UID Expunge if supported on IMAP server*/
06174    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06175    if (expungeonhangup == 1) {
06176       ast_mutex_lock(&vms->lock);
06177 #ifdef HAVE_IMAP_TK2006
06178       if (LEVELUIDPLUS (vms->mailstream)) {
06179          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06180       } else 
06181 #endif
06182          mail_expunge(vms->mailstream);
06183       ast_mutex_unlock(&vms->lock);
06184    }
06185 #endif
06186 
06187    ast_free(tmp);
06188    return res;
06189 }
06190 
06191 #if !defined(IMAP_STORAGE)
06192 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06193 {
06194    /* we know the actual number of messages, so stop process when number is hit */
06195 
06196    int x, dest;
06197    char sfn[PATH_MAX];
06198    char dfn[PATH_MAX];
06199 
06200    if (vm_lock_path(dir)) {
06201       return ERROR_LOCK_PATH;
06202    }
06203 
06204    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06205       make_file(sfn, sizeof(sfn), dir, x);
06206       if (EXISTS(dir, x, sfn, NULL)) {
06207 
06208          if (x != dest) {
06209             make_file(dfn, sizeof(dfn), dir, dest);
06210             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06211          }
06212 
06213          dest++;
06214       }
06215    }
06216    ast_unlock_path(dir);
06217 
06218    return dest;
06219 }
06220 #endif
06221 
06222 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06223 {
06224    int d;
06225    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06226    return d;
06227 }
06228 
06229 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06230 {
06231 #ifdef IMAP_STORAGE
06232    /* we must use mbox(x) folder names, and copy the message there */
06233    /* simple. huh? */
06234    char sequence[10];
06235    char mailbox[256];
06236    int res;
06237 
06238    /* get the real IMAP message number for this message */
06239    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06240    
06241    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06242    ast_mutex_lock(&vms->lock);
06243    /* if save to Old folder, put in INBOX as read */
06244    if (box == OLD_FOLDER) {
06245       mail_setflag(vms->mailstream, sequence, "\\Seen");
06246       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06247    } else if (box == NEW_FOLDER) {
06248       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06249       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06250    }
06251    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06252       ast_mutex_unlock(&vms->lock);
06253       return 0;
06254    }
06255    /* Create the folder if it don't exist */
06256    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06257    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06258    if (mail_create(vms->mailstream, mailbox) == NIL) 
06259       ast_debug(5, "Folder exists.\n");
06260    else
06261       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06262    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06263    ast_mutex_unlock(&vms->lock);
06264    return res;
06265 #else
06266    char *dir = vms->curdir;
06267    char *username = vms->username;
06268    char *context = vmu->context;
06269    char sfn[PATH_MAX];
06270    char dfn[PATH_MAX];
06271    char ddir[PATH_MAX];
06272    const char *dbox = mbox(vmu, box);
06273    int x, i;
06274    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06275 
06276    if (vm_lock_path(ddir))
06277       return ERROR_LOCK_PATH;
06278 
06279    x = last_message_index(vmu, ddir) + 1;
06280 
06281    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06282       x--;
06283       for (i = 1; i <= x; i++) {
06284          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06285          make_file(sfn, sizeof(sfn), ddir, i);
06286          make_file(dfn, sizeof(dfn), ddir, i - 1);
06287          if (EXISTS(ddir, i, sfn, NULL)) {
06288             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06289          } else
06290             break;
06291       }
06292    } else {
06293       if (x >= vmu->maxmsg) {
06294          ast_unlock_path(ddir);
06295          return -1;
06296       }
06297    }
06298    make_file(sfn, sizeof(sfn), dir, msg);
06299    make_file(dfn, sizeof(dfn), ddir, x);
06300    if (strcmp(sfn, dfn)) {
06301       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06302    }
06303    ast_unlock_path(ddir);
06304 #endif
06305    return 0;
06306 }
06307 
06308 static int adsi_logo(unsigned char *buf)
06309 {
06310    int bytes = 0;
06311    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06312    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06313    return bytes;
06314 }
06315 
06316 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06317 {
06318    unsigned char buf[256];
06319    int bytes = 0;
06320    int x;
06321    char num[5];
06322 
06323    *useadsi = 0;
06324    bytes += ast_adsi_data_mode(buf + bytes);
06325    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06326 
06327    bytes = 0;
06328    bytes += adsi_logo(buf);
06329    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06330 #ifdef DISPLAY
06331    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06332 #endif
06333    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06334    bytes += ast_adsi_data_mode(buf + bytes);
06335    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06336 
06337    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06338       bytes = 0;
06339       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06340       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06341       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06342       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06343       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06344       return 0;
06345    }
06346 
06347 #ifdef DISPLAY
06348    /* Add a dot */
06349    bytes = 0;
06350    bytes += ast_adsi_logo(buf);
06351    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06352    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06353    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06354    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06355 #endif
06356    bytes = 0;
06357    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06358    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06359    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06360    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06361    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06362    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06363    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06364 
06365 #ifdef DISPLAY
06366    /* Add another dot */
06367    bytes = 0;
06368    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06369    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06370 
06371    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06372    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06373 #endif
06374 
06375    bytes = 0;
06376    /* These buttons we load but don't use yet */
06377    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06378    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06379    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06380    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06381    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06382    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06383    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06384 
06385 #ifdef DISPLAY
06386    /* Add another dot */
06387    bytes = 0;
06388    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06389    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06390    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06391 #endif
06392 
06393    bytes = 0;
06394    for (x = 0; x < 5; x++) {
06395       snprintf(num, sizeof(num), "%d", x);
06396       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06397    }
06398    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06399    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06400 
06401 #ifdef DISPLAY
06402    /* Add another dot */
06403    bytes = 0;
06404    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06405    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06406    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06407 #endif
06408 
06409    if (ast_adsi_end_download(chan)) {
06410       bytes = 0;
06411       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06412       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06413       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06414       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06415       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06416       return 0;
06417    }
06418    bytes = 0;
06419    bytes += ast_adsi_download_disconnect(buf + bytes);
06420    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06421    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06422 
06423    ast_debug(1, "Done downloading scripts...\n");
06424 
06425 #ifdef DISPLAY
06426    /* Add last dot */
06427    bytes = 0;
06428    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06429    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06430 #endif
06431    ast_debug(1, "Restarting session...\n");
06432 
06433    bytes = 0;
06434    /* Load the session now */
06435    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06436       *useadsi = 1;
06437       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06438    } else
06439       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06440 
06441    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06442    return 0;
06443 }
06444 
06445 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06446 {
06447    int x;
06448    if (!ast_adsi_available(chan))
06449       return;
06450    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06451    if (x < 0)
06452       return;
06453    if (!x) {
06454       if (adsi_load_vmail(chan, useadsi)) {
06455          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06456          return;
06457       }
06458    } else
06459       *useadsi = 1;
06460 }
06461 
06462 static void adsi_login(struct ast_channel *chan)
06463 {
06464    unsigned char buf[256];
06465    int bytes = 0;
06466    unsigned char keys[8];
06467    int x;
06468    if (!ast_adsi_available(chan))
06469       return;
06470 
06471    for (x = 0; x < 8; x++)
06472       keys[x] = 0;
06473    /* Set one key for next */
06474    keys[3] = ADSI_KEY_APPS + 3;
06475 
06476    bytes += adsi_logo(buf + bytes);
06477    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06478    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06479    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06480    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06481    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06482    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06483    bytes += ast_adsi_set_keys(buf + bytes, keys);
06484    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06485    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06486 }
06487 
06488 static void adsi_password(struct ast_channel *chan)
06489 {
06490    unsigned char buf[256];
06491    int bytes = 0;
06492    unsigned char keys[8];
06493    int x;
06494    if (!ast_adsi_available(chan))
06495       return;
06496 
06497    for (x = 0; x < 8; x++)
06498       keys[x] = 0;
06499    /* Set one key for next */
06500    keys[3] = ADSI_KEY_APPS + 3;
06501 
06502    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06503    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06504    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06505    bytes += ast_adsi_set_keys(buf + bytes, keys);
06506    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06507    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06508 }
06509 
06510 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06511 {
06512    unsigned char buf[256];
06513    int bytes = 0;
06514    unsigned char keys[8];
06515    int x, y;
06516 
06517    if (!ast_adsi_available(chan))
06518       return;
06519 
06520    for (x = 0; x < 5; x++) {
06521       y = ADSI_KEY_APPS + 12 + start + x;
06522       if (y > ADSI_KEY_APPS + 12 + 4)
06523          y = 0;
06524       keys[x] = ADSI_KEY_SKT | y;
06525    }
06526    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06527    keys[6] = 0;
06528    keys[7] = 0;
06529 
06530    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06531    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06532    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06533    bytes += ast_adsi_set_keys(buf + bytes, keys);
06534    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06535 
06536    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06537 }
06538 
06539 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06540 {
06541    int bytes = 0;
06542    unsigned char buf[256]; 
06543    char buf1[256], buf2[256];
06544    char fn2[PATH_MAX];
06545 
06546    char cid[256] = "";
06547    char *val;
06548    char *name, *num;
06549    char datetime[21] = "";
06550    FILE *f;
06551 
06552    unsigned char keys[8];
06553 
06554    int x;
06555 
06556    if (!ast_adsi_available(chan))
06557       return;
06558 
06559    /* Retrieve important info */
06560    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06561    f = fopen(fn2, "r");
06562    if (f) {
06563       while (!feof(f)) {   
06564          if (!fgets((char *) buf, sizeof(buf), f)) {
06565             continue;
06566          }
06567          if (!feof(f)) {
06568             char *stringp = NULL;
06569             stringp = (char *) buf;
06570             strsep(&stringp, "=");
06571             val = strsep(&stringp, "=");
06572             if (!ast_strlen_zero(val)) {
06573                if (!strcmp((char *) buf, "callerid"))
06574                   ast_copy_string(cid, val, sizeof(cid));
06575                if (!strcmp((char *) buf, "origdate"))
06576                   ast_copy_string(datetime, val, sizeof(datetime));
06577             }
06578          }
06579       }
06580       fclose(f);
06581    }
06582    /* New meaning for keys */
06583    for (x = 0; x < 5; x++)
06584       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06585    keys[6] = 0x0;
06586    keys[7] = 0x0;
06587 
06588    if (!vms->curmsg) {
06589       /* No prev key, provide "Folder" instead */
06590       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06591    }
06592    if (vms->curmsg >= vms->lastmsg) {
06593       /* If last message ... */
06594       if (vms->curmsg) {
06595          /* but not only message, provide "Folder" instead */
06596          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06597          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06598 
06599       } else {
06600          /* Otherwise if only message, leave blank */
06601          keys[3] = 1;
06602       }
06603    }
06604 
06605    if (!ast_strlen_zero(cid)) {
06606       ast_callerid_parse(cid, &name, &num);
06607       if (!name)
06608          name = num;
06609    } else
06610       name = "Unknown Caller";
06611 
06612    /* If deleted, show "undeleted" */
06613 #ifdef IMAP_STORAGE
06614    ast_mutex_lock(&vms->lock);
06615 #endif
06616    if (vms->deleted[vms->curmsg]) {
06617       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06618    }
06619 #ifdef IMAP_STORAGE
06620    ast_mutex_unlock(&vms->lock);
06621 #endif
06622 
06623    /* Except "Exit" */
06624    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06625    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06626       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06627    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06628 
06629    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06630    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06631    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06632    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06633    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06634    bytes += ast_adsi_set_keys(buf + bytes, keys);
06635    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06636 
06637    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06638 }
06639 
06640 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06641 {
06642    int bytes = 0;
06643    unsigned char buf[256];
06644    unsigned char keys[8];
06645 
06646    int x;
06647 
06648    if (!ast_adsi_available(chan))
06649       return;
06650 
06651    /* New meaning for keys */
06652    for (x = 0; x < 5; x++)
06653       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06654 
06655    keys[6] = 0x0;
06656    keys[7] = 0x0;
06657 
06658    if (!vms->curmsg) {
06659       /* No prev key, provide "Folder" instead */
06660       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06661    }
06662    if (vms->curmsg >= vms->lastmsg) {
06663       /* If last message ... */
06664       if (vms->curmsg) {
06665          /* but not only message, provide "Folder" instead */
06666          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06667       } else {
06668          /* Otherwise if only message, leave blank */
06669          keys[3] = 1;
06670       }
06671    }
06672 
06673    /* If deleted, show "undeleted" */
06674 #ifdef IMAP_STORAGE
06675    ast_mutex_lock(&vms->lock);
06676 #endif
06677    if (vms->deleted[vms->curmsg]) {
06678       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06679    }
06680 #ifdef IMAP_STORAGE
06681    ast_mutex_unlock(&vms->lock);
06682 #endif
06683 
06684    /* Except "Exit" */
06685    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06686    bytes += ast_adsi_set_keys(buf + bytes, keys);
06687    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06688 
06689    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06690 }
06691 
06692 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06693 {
06694    unsigned char buf[256] = "";
06695    char buf1[256] = "", buf2[256] = "";
06696    int bytes = 0;
06697    unsigned char keys[8];
06698    int x;
06699 
06700    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06701    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06702    if (!ast_adsi_available(chan))
06703       return;
06704    if (vms->newmessages) {
06705       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06706       if (vms->oldmessages) {
06707          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06708          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06709       } else {
06710          snprintf(buf2, sizeof(buf2), "%s.", newm);
06711       }
06712    } else if (vms->oldmessages) {
06713       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06714       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06715    } else {
06716       strcpy(buf1, "You have no messages.");
06717       buf2[0] = ' ';
06718       buf2[1] = '\0';
06719    }
06720    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06721    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06722    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06723 
06724    for (x = 0; x < 6; x++)
06725       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06726    keys[6] = 0;
06727    keys[7] = 0;
06728 
06729    /* Don't let them listen if there are none */
06730    if (vms->lastmsg < 0)
06731       keys[0] = 1;
06732    bytes += ast_adsi_set_keys(buf + bytes, keys);
06733 
06734    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06735 
06736    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06737 }
06738 
06739 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06740 {
06741    unsigned char buf[256] = "";
06742    char buf1[256] = "", buf2[256] = "";
06743    int bytes = 0;
06744    unsigned char keys[8];
06745    int x;
06746 
06747    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06748 
06749    if (!ast_adsi_available(chan))
06750       return;
06751 
06752    /* Original command keys */
06753    for (x = 0; x < 6; x++)
06754       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06755 
06756    keys[6] = 0;
06757    keys[7] = 0;
06758 
06759    if ((vms->lastmsg + 1) < 1)
06760       keys[0] = 0;
06761 
06762    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06763       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06764 
06765    if (vms->lastmsg + 1)
06766       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06767    else
06768       strcpy(buf2, "no messages.");
06769    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06770    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06771    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06772    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06773    bytes += ast_adsi_set_keys(buf + bytes, keys);
06774 
06775    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06776 
06777    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06778    
06779 }
06780 
06781 /*
06782 static void adsi_clear(struct ast_channel *chan)
06783 {
06784    char buf[256];
06785    int bytes=0;
06786    if (!ast_adsi_available(chan))
06787       return;
06788    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06789    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06790 
06791    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06792 }
06793 */
06794 
06795 static void adsi_goodbye(struct ast_channel *chan)
06796 {
06797    unsigned char buf[256];
06798    int bytes = 0;
06799 
06800    if (!ast_adsi_available(chan))
06801       return;
06802    bytes += adsi_logo(buf + bytes);
06803    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06804    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06805    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06806    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06807 
06808    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06809 }
06810 
06811 /*!\brief get_folder: Folder menu
06812  * Plays "press 1 for INBOX messages" etc.
06813  * Should possibly be internationalized
06814  */
06815 static int get_folder(struct ast_channel *chan, int start)
06816 {
06817    int x;
06818    int d;
06819    char fn[PATH_MAX];
06820    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06821    if (d)
06822       return d;
06823    for (x = start; x < 5; x++) { /* For all folders */
06824       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06825          return d;
06826       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06827       if (d)
06828          return d;
06829       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06830 
06831       /* The inbox folder can have its name changed under certain conditions
06832        * so this checks if the sound file exists for the inbox folder name and
06833        * if it doesn't, plays the default name instead. */
06834       if (x == 0) {
06835          if (ast_fileexists(fn, NULL, NULL)) {
06836             d = vm_play_folder_name(chan, fn);
06837          } else {
06838             ast_verb(1, "failed to find %s\n", fn);
06839             d = vm_play_folder_name(chan, "vm-INBOX");
06840          }
06841       } else {
06842          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06843          d = vm_play_folder_name(chan, fn);
06844       }
06845 
06846       if (d)
06847          return d;
06848       d = ast_waitfordigit(chan, 500);
06849       if (d)
06850          return d;
06851    }
06852 
06853    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06854    if (d)
06855       return d;
06856    d = ast_waitfordigit(chan, 4000);
06857    return d;
06858 }
06859 
06860 /*!
06861  * \brief plays a prompt and waits for a keypress.
06862  * \param chan
06863  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06864  * \param start Does not appear to be used at this time.
06865  *
06866  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06867  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06868  * prompting for the number inputs that correspond to the available folders.
06869  * 
06870  * \return zero on success, or -1 on error.
06871  */
06872 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06873 {
06874    int res = 0;
06875    int loops = 0;
06876 
06877    res = ast_play_and_wait(chan, fn);  /* Folder name */
06878    while (((res < '0') || (res > '9')) &&
06879          (res != '#') && (res >= 0) &&
06880          loops < 4) {
06881       res = get_folder(chan, 0);
06882       loops++;
06883    }
06884    if (loops == 4) { /* give up */
06885       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06886       return '#';
06887    }
06888    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06889    return res;
06890 }
06891 
06892 /*!
06893  * \brief presents the option to prepend to an existing message when forwarding it.
06894  * \param chan
06895  * \param vmu
06896  * \param curdir
06897  * \param curmsg
06898  * \param vm_fmts
06899  * \param context
06900  * \param record_gain
06901  * \param duration
06902  * \param vms
06903  * \param flag 
06904  *
06905  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06906  *
06907  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06908  * \return zero on success, -1 on error.
06909  */
06910 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06911          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06912 {
06913    int cmd = 0;
06914    int retries = 0, prepend_duration = 0, already_recorded = 0;
06915    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06916    char textfile[PATH_MAX];
06917    struct ast_config *msg_cfg;
06918    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06919 #ifndef IMAP_STORAGE
06920    signed char zero_gain = 0;
06921 #endif
06922    const char *duration_str;
06923 
06924    /* Must always populate duration correctly */
06925    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06926    strcpy(textfile, msgfile);
06927    strcpy(backup, msgfile);
06928    strcpy(backup_textfile, msgfile);
06929    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06930    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06931    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06932 
06933    if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06934       *duration = atoi(duration_str);
06935    } else {
06936       *duration = 0;
06937    }
06938 
06939    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06940       if (cmd)
06941          retries = 0;
06942       switch (cmd) {
06943       case '1': 
06944 
06945 #ifdef IMAP_STORAGE
06946          /* Record new intro file */
06947          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06948          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06949          ast_play_and_wait(chan, INTRO);
06950          ast_play_and_wait(chan, "beep");
06951          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06952          if (cmd == -1) {
06953             break;
06954          }
06955          cmd = 't';
06956 #else
06957 
06958          /* prepend a message to the current message, update the metadata and return */
06959 
06960          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06961          strcpy(textfile, msgfile);
06962          strncat(textfile, ".txt", sizeof(textfile) - 1);
06963          *duration = 0;
06964 
06965          /* if we can't read the message metadata, stop now */
06966          if (!valid_config(msg_cfg)) {
06967             cmd = 0;
06968             break;
06969          }
06970 
06971          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06972 #ifndef IMAP_STORAGE
06973          if (already_recorded) {
06974             ast_filecopy(backup, msgfile, NULL);
06975             copy(backup_textfile, textfile);
06976          }
06977          else {
06978             ast_filecopy(msgfile, backup, NULL);
06979             copy(textfile, backup_textfile);
06980          }
06981 #endif
06982          already_recorded = 1;
06983 
06984          if (record_gain)
06985             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06986 
06987          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06988 
06989          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06990             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06991             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06992             ast_filerename(backup, msgfile, NULL);
06993          }
06994 
06995          if (record_gain)
06996             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06997 
06998          
06999          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
07000             *duration = atoi(duration_str);
07001 
07002          if (prepend_duration) {
07003             struct ast_category *msg_cat;
07004             /* need enough space for a maximum-length message duration */
07005             char duration_buf[12];
07006 
07007             *duration += prepend_duration;
07008             msg_cat = ast_category_get(msg_cfg, "message");
07009             snprintf(duration_buf, 11, "%ld", *duration);
07010             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
07011                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
07012             }
07013          }
07014 
07015 #endif
07016          break;
07017       case '2': 
07018          /* NULL out introfile so we know there is no intro! */
07019 #ifdef IMAP_STORAGE
07020          *vms->introfn = '\0';
07021 #endif
07022          cmd = 't';
07023          break;
07024       case '*':
07025          cmd = '*';
07026          break;
07027       default: 
07028          /* If time_out and return to menu, reset already_recorded */
07029          already_recorded = 0;
07030 
07031          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07032             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07033          if (!cmd) {
07034             cmd = ast_play_and_wait(chan, "vm-starmain");
07035             /* "press star to return to the main menu" */
07036          }
07037          if (!cmd) {
07038             cmd = ast_waitfordigit(chan, 6000);
07039          }
07040          if (!cmd) {
07041             retries++;
07042          }
07043          if (retries > 3) {
07044             cmd = '*'; /* Let's cancel this beast */
07045          }
07046          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07047       }
07048    }
07049 
07050    if (valid_config(msg_cfg))
07051       ast_config_destroy(msg_cfg);
07052    if (prepend_duration)
07053       *duration = prepend_duration;
07054 
07055    if (already_recorded && cmd == -1) {
07056       /* restore original message if prepention cancelled */
07057       ast_filerename(backup, msgfile, NULL);
07058       rename(backup_textfile, textfile);
07059    }
07060 
07061    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07062       cmd = 0;
07063    return cmd;
07064 }
07065 
07066 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07067 {
07068    struct ast_event *event;
07069    char *mailbox, *context;
07070 
07071    /* Strip off @default */
07072    context = mailbox = ast_strdupa(box);
07073    strsep(&context, "@");
07074    if (ast_strlen_zero(context))
07075       context = "default";
07076 
07077    if (!(event = ast_event_new(AST_EVENT_MWI,
07078          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07079          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07080          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07081          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07082          AST_EVENT_IE_END))) {
07083       return;
07084    }
07085 
07086    ast_event_queue_and_cache(event);
07087 }
07088 
07089 /*!
07090  * \brief Sends email notification that a user has a new voicemail waiting for them.
07091  * \param chan
07092  * \param vmu
07093  * \param vms
07094  * \param msgnum
07095  * \param duration
07096  * \param fmt
07097  * \param cidnum The Caller ID phone number value.
07098  * \param cidname The Caller ID name value.
07099  * \param flag
07100  *
07101  * \return zero on success, -1 on error.
07102  */
07103 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)
07104 {
07105    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07106    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07107    const char *category;
07108    char *myserveremail = serveremail;
07109 
07110    ast_channel_lock(chan);
07111    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07112       category = ast_strdupa(category);
07113    }
07114    ast_channel_unlock(chan);
07115 
07116 #ifndef IMAP_STORAGE
07117    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07118 #else
07119    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07120 #endif
07121    make_file(fn, sizeof(fn), todir, msgnum);
07122    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07123 
07124    if (!ast_strlen_zero(vmu->attachfmt)) {
07125       if (strstr(fmt, vmu->attachfmt))
07126          fmt = vmu->attachfmt;
07127       else
07128          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);
07129    }
07130 
07131    /* Attach only the first format */
07132    fmt = ast_strdupa(fmt);
07133    stringp = fmt;
07134    strsep(&stringp, "|");
07135 
07136    if (!ast_strlen_zero(vmu->serveremail))
07137       myserveremail = vmu->serveremail;
07138 
07139    if (!ast_strlen_zero(vmu->email)) {
07140       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07141 
07142       if (attach_user_voicemail)
07143          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07144 
07145       /* XXX possible imap issue, should category be NULL XXX */
07146       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07147 
07148       if (attach_user_voicemail)
07149          DISPOSE(todir, msgnum);
07150    }
07151 
07152    if (!ast_strlen_zero(vmu->pager)) {
07153       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07154    }
07155 
07156    if (ast_test_flag(vmu, VM_DELETE))
07157       DELETE(todir, msgnum, fn, vmu);
07158 
07159    /* Leave voicemail for someone */
07160    if (ast_app_has_voicemail(ext_context, NULL)) 
07161       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07162 
07163    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07164 
07165    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);
07166    run_externnotify(vmu->context, vmu->mailbox, flag);
07167 
07168 #ifdef IMAP_STORAGE
07169    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07170    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07171       vm_imap_delete(NULL, vms->curmsg, vmu);
07172       vms->newmessages--;  /* Fix new message count */
07173    }
07174 #endif
07175 
07176    return 0;
07177 }
07178 
07179 /*!
07180  * \brief Sends a voicemail message to a mailbox recipient.
07181  * \param chan
07182  * \param context
07183  * \param vms
07184  * \param sender
07185  * \param fmt
07186  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07187  *             Will be 0 when called to forward an existing message (option 8)
07188  *             Will be 1 when called to leave a message (option 3->5)
07189  * \param record_gain 
07190  * \param urgent
07191  *
07192  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07193  * 
07194  * When in the leave message mode (is_new_message == 1):
07195  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07196  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07197  *
07198  * When in the forward message mode (is_new_message == 0):
07199  *   - retreives the current message to be forwarded
07200  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07201  *   - determines the target mailbox and folders
07202  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07203  *
07204  * \return zero on success, -1 on error.
07205  */
07206 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)
07207 {
07208 #ifdef IMAP_STORAGE
07209    int todircount = 0;
07210    struct vm_state *dstvms;
07211 #endif
07212    char username[70]="";
07213    char fn[PATH_MAX]; /* for playback of name greeting */
07214    char ecodes[16] = "#";
07215    int res = 0, cmd = 0;
07216    struct ast_vm_user *receiver = NULL, *vmtmp;
07217    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07218    char *stringp;
07219    const char *s;
07220    int saved_messages = 0;
07221    int valid_extensions = 0;
07222    char *dir;
07223    int curmsg;
07224    char urgent_str[7] = "";
07225    int prompt_played = 0;
07226 #ifndef IMAP_STORAGE
07227    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07228 #endif
07229    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07230       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07231    }
07232 
07233    if (vms == NULL) return -1;
07234    dir = vms->curdir;
07235    curmsg = vms->curmsg;
07236 
07237    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07238    while (!res && !valid_extensions) {
07239       int use_directory = 0;
07240       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07241          int done = 0;
07242          int retries = 0;
07243          cmd = 0;
07244          while ((cmd >= 0) && !done ){
07245             if (cmd)
07246                retries = 0;
07247             switch (cmd) {
07248             case '1': 
07249                use_directory = 0;
07250                done = 1;
07251                break;
07252             case '2': 
07253                use_directory = 1;
07254                done = 1;
07255                break;
07256             case '*': 
07257                cmd = 't';
07258                done = 1;
07259                break;
07260             default: 
07261                /* Press 1 to enter an extension press 2 to use the directory */
07262                cmd = ast_play_and_wait(chan, "vm-forward");
07263                if (!cmd) {
07264                   cmd = ast_waitfordigit(chan, 3000);
07265                }
07266                if (!cmd) {
07267                   retries++;
07268                }
07269                if (retries > 3) {
07270                   cmd = 't';
07271                   done = 1;
07272                }
07273                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07274             }
07275          }
07276          if (cmd < 0 || cmd == 't')
07277             break;
07278       }
07279       
07280       if (use_directory) {
07281          /* use app_directory */
07282          
07283          char old_context[sizeof(chan->context)];
07284          char old_exten[sizeof(chan->exten)];
07285          int old_priority;
07286          struct ast_app* directory_app;
07287 
07288          directory_app = pbx_findapp("Directory");
07289          if (directory_app) {
07290             char vmcontext[256];
07291             /* make backup copies */
07292             memcpy(old_context, chan->context, sizeof(chan->context));
07293             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07294             old_priority = chan->priority;
07295             
07296             /* call the the Directory, changes the channel */
07297             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07298             res = pbx_exec(chan, directory_app, vmcontext);
07299             
07300             ast_copy_string(username, chan->exten, sizeof(username));
07301             
07302             /* restore the old context, exten, and priority */
07303             memcpy(chan->context, old_context, sizeof(chan->context));
07304             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07305             chan->priority = old_priority;
07306          } else {
07307             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07308             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07309          }
07310       } else {
07311          /* Ask for an extension */
07312          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07313          prompt_played++;
07314          if (res || prompt_played > 4)
07315             break;
07316          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07317             break;
07318       }
07319       
07320       /* start all over if no username */
07321       if (ast_strlen_zero(username))
07322          continue;
07323       stringp = username;
07324       s = strsep(&stringp, "*");
07325       /* start optimistic */
07326       valid_extensions = 1;
07327       while (s) {
07328          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07329             int oldmsgs;
07330             int newmsgs;
07331             int capacity;
07332             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07333                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07334                /* Shouldn't happen, but allow trying another extension if it does */
07335                res = ast_play_and_wait(chan, "pbx-invalid");
07336                valid_extensions = 0;
07337                break;
07338             }
07339             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07340             if ((newmsgs + oldmsgs) >= capacity) {
07341                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07342                res = ast_play_and_wait(chan, "vm-mailboxfull");
07343                valid_extensions = 0;
07344                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07345                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07346                   free_user(vmtmp);
07347                }
07348                inprocess_count(receiver->mailbox, receiver->context, -1);
07349                break;
07350             }
07351             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07352          } else {
07353             /* XXX Optimization for the future.  When we encounter a single bad extension,
07354              * bailing out on all of the extensions may not be the way to go.  We should
07355              * probably just bail on that single extension, then allow the user to enter
07356              * several more. XXX
07357              */
07358             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07359                free_user(receiver);
07360             }
07361             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07362             /* "I am sorry, that's not a valid extension.  Please try again." */
07363             res = ast_play_and_wait(chan, "pbx-invalid");
07364             valid_extensions = 0;
07365             break;
07366          }
07367 
07368          /* play name if available, else play extension number */
07369          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07370          RETRIEVE(fn, -1, s, receiver->context);
07371          if (ast_fileexists(fn, NULL, NULL) > 0) {
07372             res = ast_stream_and_wait(chan, fn, ecodes);
07373             if (res) {
07374                DISPOSE(fn, -1);
07375                return res;
07376             }
07377          } else {
07378             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07379          }
07380          DISPOSE(fn, -1);
07381 
07382          s = strsep(&stringp, "*");
07383       }
07384       /* break from the loop of reading the extensions */
07385       if (valid_extensions)
07386          break;
07387    }
07388    /* check if we're clear to proceed */
07389    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07390       return res;
07391    if (is_new_message == 1) {
07392       struct leave_vm_options leave_options;
07393       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07394       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07395 
07396       /* Send VoiceMail */
07397       memset(&leave_options, 0, sizeof(leave_options));
07398       leave_options.record_gain = record_gain;
07399       cmd = leave_voicemail(chan, mailbox, &leave_options);
07400    } else {
07401       /* Forward VoiceMail */
07402       long duration = 0;
07403       struct vm_state vmstmp;
07404       int copy_msg_result = 0;
07405       memcpy(&vmstmp, vms, sizeof(vmstmp));
07406 
07407       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07408 
07409       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07410       if (!cmd) {
07411          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07412 #ifdef IMAP_STORAGE
07413             int attach_user_voicemail;
07414             char *myserveremail = serveremail;
07415             
07416             /* get destination mailbox */
07417             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07418             if (!dstvms) {
07419                dstvms = create_vm_state_from_user(vmtmp);
07420             }
07421             if (dstvms) {
07422                init_mailstream(dstvms, 0);
07423                if (!dstvms->mailstream) {
07424                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07425                } else {
07426                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07427                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07428                }
07429             } else {
07430                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07431             }
07432             if (!ast_strlen_zero(vmtmp->serveremail))
07433                myserveremail = vmtmp->serveremail;
07434             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07435             /* NULL category for IMAP storage */
07436             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07437                dstvms->curbox,
07438                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07439                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07440                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07441                NULL, urgent_str);
07442 #else
07443             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07444 #endif
07445             saved_messages++;
07446             AST_LIST_REMOVE_CURRENT(list);
07447             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07448             free_user(vmtmp);
07449             if (res)
07450                break;
07451          }
07452          AST_LIST_TRAVERSE_SAFE_END;
07453          if (saved_messages > 0 && !copy_msg_result) {
07454             /* give confirmation that the message was saved */
07455             /* commented out since we can't forward batches yet
07456             if (saved_messages == 1)
07457                res = ast_play_and_wait(chan, "vm-message");
07458             else
07459                res = ast_play_and_wait(chan, "vm-messages");
07460             if (!res)
07461                res = ast_play_and_wait(chan, "vm-saved"); */
07462 #ifdef IMAP_STORAGE
07463             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07464             if (ast_strlen_zero(vmstmp.introfn))
07465 #endif
07466             res = ast_play_and_wait(chan, "vm-msgsaved");
07467          }
07468 #ifndef IMAP_STORAGE
07469          else {
07470             /* with IMAP, mailbox full warning played by imap_check_limits */
07471             res = ast_play_and_wait(chan, "vm-mailboxfull");
07472          }
07473          /* Restore original message without prepended message if backup exists */
07474          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07475          strcpy(textfile, msgfile);
07476          strcpy(backup, msgfile);
07477          strcpy(backup_textfile, msgfile);
07478          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07479          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07480          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07481          if (ast_fileexists(backup, NULL, NULL) > 0) {
07482             ast_filerename(backup, msgfile, NULL);
07483             rename(backup_textfile, textfile);
07484          }
07485 #endif
07486       }
07487       DISPOSE(dir, curmsg);
07488 #ifndef IMAP_STORAGE
07489       if (cmd) { /* assuming hangup, cleanup backup file */
07490          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07491          strcpy(textfile, msgfile);
07492          strcpy(backup_textfile, msgfile);
07493          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07494          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07495          rename(backup_textfile, textfile);
07496       }
07497 #endif
07498    }
07499 
07500    /* If anything failed above, we still have this list to free */
07501    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07502       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07503       free_user(vmtmp);
07504    }
07505    return res ? res : cmd;
07506 }
07507 
07508 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07509 {
07510    int res;
07511    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07512       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07513    return res;
07514 }
07515 
07516 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07517 {
07518    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07519    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);
07520 }
07521 
07522 static int play_message_category(struct ast_channel *chan, const char *category)
07523 {
07524    int res = 0;
07525 
07526    if (!ast_strlen_zero(category))
07527       res = ast_play_and_wait(chan, category);
07528 
07529    if (res) {
07530       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07531       res = 0;
07532    }
07533 
07534    return res;
07535 }
07536 
07537 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07538 {
07539    int res = 0;
07540    struct vm_zone *the_zone = NULL;
07541    time_t t;
07542 
07543    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07544       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07545       return 0;
07546    }
07547 
07548    /* Does this user have a timezone specified? */
07549    if (!ast_strlen_zero(vmu->zonetag)) {
07550       /* Find the zone in the list */
07551       struct vm_zone *z;
07552       AST_LIST_LOCK(&zones);
07553       AST_LIST_TRAVERSE(&zones, z, list) {
07554          if (!strcmp(z->name, vmu->zonetag)) {
07555             the_zone = z;
07556             break;
07557          }
07558       }
07559       AST_LIST_UNLOCK(&zones);
07560    }
07561 
07562 /* No internal variable parsing for now, so we'll comment it out for the time being */
07563 #if 0
07564    /* Set the DIFF_* variables */
07565    ast_localtime(&t, &time_now, NULL);
07566    tv_now = ast_tvnow();
07567    ast_localtime(&tv_now, &time_then, NULL);
07568 
07569    /* Day difference */
07570    if (time_now.tm_year == time_then.tm_year)
07571       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07572    else
07573       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07574    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07575 
07576    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07577 #endif
07578    if (the_zone) {
07579       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07580    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07581       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07582    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07583       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07584    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07585       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);
07586    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07587       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07588    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN 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, "pl", 2)) {     /* POLISH syntax */
07591       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07592    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07593       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);
07594    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07595       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07596    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07597       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07598    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07599       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);
07600    } else {
07601       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07602    }
07603 #if 0
07604    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07605 #endif
07606    return res;
07607 }
07608 
07609 
07610 
07611 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07612 {
07613    int res = 0;
07614    int i;
07615    char *callerid, *name;
07616    char prefile[PATH_MAX] = "";
07617    
07618 
07619    /* If voicemail cid is not enabled, or we didn't get cid or context from
07620     * the attribute file, leave now.
07621     *
07622     * TODO Still need to change this so that if this function is called by the
07623     * message envelope (and someone is explicitly requesting to hear the CID),
07624     * it does not check to see if CID is enabled in the config file.
07625     */
07626    if ((cid == NULL)||(context == NULL))
07627       return res;
07628 
07629    /* Strip off caller ID number from name */
07630    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07631    ast_callerid_parse(cid, &name, &callerid);
07632    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07633       /* Check for internal contexts and only */
07634       /* say extension when the call didn't come from an internal context in the list */
07635       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07636          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07637          if ((strcmp(cidinternalcontexts[i], context) == 0))
07638             break;
07639       }
07640       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07641          if (!res) {
07642             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07643             if (!ast_strlen_zero(prefile)) {
07644             /* See if we can find a recorded name for this person instead of their extension number */
07645                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07646                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07647                   if (!callback)
07648                      res = wait_file2(chan, vms, "vm-from");
07649                   res = ast_stream_and_wait(chan, prefile, "");
07650                } else {
07651                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07652                   /* Say "from extension" as one saying to sound smoother */
07653                   if (!callback)
07654                      res = wait_file2(chan, vms, "vm-from-extension");
07655                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07656                }
07657             }
07658          }
07659       } else if (!res) {
07660          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07661          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07662          if (!callback)
07663             res = wait_file2(chan, vms, "vm-from-phonenumber");
07664          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07665       }
07666    } else {
07667       /* Number unknown */
07668       ast_debug(1, "VM-CID: From an unknown number\n");
07669       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07670       res = wait_file2(chan, vms, "vm-unknown-caller");
07671    }
07672    return res;
07673 }
07674 
07675 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07676 {
07677    int res = 0;
07678    int durationm;
07679    int durations;
07680    /* Verify that we have a duration for the message */
07681    if (duration == NULL)
07682       return res;
07683 
07684    /* Convert from seconds to minutes */
07685    durations = atoi(duration);
07686    durationm = (durations / 60);
07687 
07688    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07689 
07690    if ((!res) && (durationm >= minduration)) {
07691       res = wait_file2(chan, vms, "vm-duration");
07692 
07693       /* POLISH syntax */
07694       if (!strncasecmp(chan->language, "pl", 2)) {
07695          div_t num = div(durationm, 10);
07696 
07697          if (durationm == 1) {
07698             res = ast_play_and_wait(chan, "digits/1z");
07699             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07700          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07701             if (num.rem == 2) {
07702                if (!num.quot) {
07703                   res = ast_play_and_wait(chan, "digits/2-ie");
07704                } else {
07705                   res = say_and_wait(chan, durationm - 2 , chan->language);
07706                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07707                }
07708             } else {
07709                res = say_and_wait(chan, durationm, chan->language);
07710             }
07711             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07712          } else {
07713             res = say_and_wait(chan, durationm, chan->language);
07714             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07715          }
07716       /* DEFAULT syntax */
07717       } else {
07718          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07719          res = wait_file2(chan, vms, "vm-minutes");
07720       }
07721    }
07722    return res;
07723 }
07724 
07725 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07726 {
07727    int res = 0;
07728    char filename[256], *cid;
07729    const char *origtime, *context, *category, *duration, *flag;
07730    struct ast_config *msg_cfg;
07731    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07732 
07733    vms->starting = 0;
07734    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07735    adsi_message(chan, vms);
07736    if (!vms->curmsg) {
07737       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07738    } else if (vms->curmsg == vms->lastmsg) {
07739       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07740    }
07741 
07742    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07743    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07744    msg_cfg = ast_config_load(filename, config_flags);
07745    if (!valid_config(msg_cfg)) {
07746       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07747       return 0;
07748    }
07749    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07750 
07751    /* Play the word urgent if we are listening to urgent messages */
07752    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07753       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07754    }
07755 
07756    if (!res) {
07757       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07758       /* POLISH syntax */
07759       if (!strncasecmp(chan->language, "pl", 2)) {
07760          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07761             int ten, one;
07762             char nextmsg[256];
07763             ten = (vms->curmsg + 1) / 10;
07764             one = (vms->curmsg + 1) % 10;
07765 
07766             if (vms->curmsg < 20) {
07767                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07768                res = wait_file2(chan, vms, nextmsg);
07769             } else {
07770                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07771                res = wait_file2(chan, vms, nextmsg);
07772                if (one > 0) {
07773                   if (!res) {
07774                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07775                      res = wait_file2(chan, vms, nextmsg);
07776                   }
07777                }
07778             }
07779          }
07780          if (!res)
07781             res = wait_file2(chan, vms, "vm-message");
07782       /* HEBREW syntax */
07783       } else if (!strncasecmp(chan->language, "he", 2)) {
07784          if (!vms->curmsg) {
07785             res = wait_file2(chan, vms, "vm-message");
07786             res = wait_file2(chan, vms, "vm-first");
07787          } else if (vms->curmsg == vms->lastmsg) {
07788             res = wait_file2(chan, vms, "vm-message");
07789             res = wait_file2(chan, vms, "vm-last");
07790          } else {
07791             res = wait_file2(chan, vms, "vm-message");
07792             res = wait_file2(chan, vms, "vm-number");
07793             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07794          }
07795       /* VIETNAMESE syntax */
07796       } else if (!strncasecmp(chan->language, "vi", 2)) {
07797          if (!vms->curmsg) {
07798             res = wait_file2(chan, vms, "vm-message");
07799             res = wait_file2(chan, vms, "vm-first");
07800          } else if (vms->curmsg == vms->lastmsg) {
07801             res = wait_file2(chan, vms, "vm-message");
07802             res = wait_file2(chan, vms, "vm-last");
07803          } else {
07804             res = wait_file2(chan, vms, "vm-message");
07805             res = wait_file2(chan, vms, "vm-number");
07806             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07807          }
07808       } else {
07809          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07810             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07811          } else { /* DEFAULT syntax */
07812             res = wait_file2(chan, vms, "vm-message");
07813          }
07814          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07815             if (!res) {
07816                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07817                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07818             }
07819          }
07820       }
07821    }
07822 
07823    if (!valid_config(msg_cfg)) {
07824       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07825       return 0;
07826    }
07827 
07828    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07829       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07830       DISPOSE(vms->curdir, vms->curmsg);
07831       ast_config_destroy(msg_cfg);
07832       return 0;
07833    }
07834 
07835    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07836    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07837    category = ast_variable_retrieve(msg_cfg, "message", "category");
07838 
07839    context = ast_variable_retrieve(msg_cfg, "message", "context");
07840    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07841       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07842    if (!res) {
07843       res = play_message_category(chan, category);
07844    }
07845    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07846       res = play_message_datetime(chan, vmu, origtime, filename);
07847    }
07848    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07849       res = play_message_callerid(chan, vms, cid, context, 0);
07850    }
07851    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07852       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07853    }
07854    /* Allow pressing '1' to skip envelope / callerid */
07855    if (res == '1') {
07856       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07857       res = 0;
07858    }
07859    ast_config_destroy(msg_cfg);
07860 
07861    if (!res) {
07862       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07863 #ifdef IMAP_STORAGE
07864       ast_mutex_lock(&vms->lock);
07865 #endif
07866       vms->heard[vms->curmsg] = 1;
07867 #ifdef IMAP_STORAGE
07868       ast_mutex_unlock(&vms->lock);
07869       /*IMAP storage stores any prepended message from a forward
07870        * as a separate file from the rest of the message
07871        */
07872       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07873          wait_file(chan, vms, vms->introfn);
07874       }
07875 #endif
07876       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07877          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07878          res = 0;
07879       }
07880       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07881    }
07882    DISPOSE(vms->curdir, vms->curmsg);
07883    return res;
07884 }
07885 
07886 #ifdef IMAP_STORAGE
07887 static int imap_remove_file(char *dir, int msgnum)
07888 {
07889    char fn[PATH_MAX];
07890    char full_fn[PATH_MAX];
07891    char intro[PATH_MAX] = {0,};
07892    
07893    if (msgnum > -1) {
07894       make_file(fn, sizeof(fn), dir, msgnum);
07895       snprintf(intro, sizeof(intro), "%sintro", fn);
07896    } else
07897       ast_copy_string(fn, dir, sizeof(fn));
07898    
07899    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07900       ast_filedelete(fn, NULL);
07901       if (!ast_strlen_zero(intro)) {
07902          ast_filedelete(intro, NULL);
07903       }
07904       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07905       unlink(full_fn);
07906    }
07907    return 0;
07908 }
07909 
07910 
07911 
07912 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07913 {
07914    char *file, *filename;
07915    char *attachment;
07916    char arg[10];
07917    int i;
07918    BODY* body;
07919 
07920    file = strrchr(ast_strdupa(dir), '/');
07921    if (file) {
07922       *file++ = '\0';
07923    } else {
07924       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07925       return -1;
07926    }
07927 
07928    ast_mutex_lock(&vms->lock);
07929    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07930       mail_fetchstructure(vms->mailstream, i + 1, &body);
07931       /* We have the body, now we extract the file name of the first attachment. */
07932       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07933          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07934       } else {
07935          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07936          ast_mutex_unlock(&vms->lock);
07937          return -1;
07938       }
07939       filename = strsep(&attachment, ".");
07940       if (!strcmp(filename, file)) {
07941          sprintf(arg, "%d", i + 1);
07942          mail_setflag(vms->mailstream, arg, "\\DELETED");
07943       }
07944    }
07945    mail_expunge(vms->mailstream);
07946    ast_mutex_unlock(&vms->lock);
07947    return 0;
07948 }
07949 
07950 #elif !defined(IMAP_STORAGE)
07951 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07952 {
07953    int count_msg, last_msg;
07954 
07955    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07956 
07957    /* Rename the member vmbox HERE so that we don't try to return before
07958     * we know what's going on.
07959     */
07960    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07961 
07962    /* Faster to make the directory than to check if it exists. */
07963    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07964 
07965    /* traverses directory using readdir (or select query for ODBC) */
07966    count_msg = count_messages(vmu, vms->curdir);
07967    if (count_msg < 0) {
07968       return count_msg;
07969    } else {
07970       vms->lastmsg = count_msg - 1;
07971    }
07972 
07973    if (vm_allocate_dh(vms, vmu, count_msg)) {
07974       return -1;
07975    }
07976 
07977    /*
07978    The following test is needed in case sequencing gets messed up.
07979    There appears to be more than one way to mess up sequence, so
07980    we will not try to find all of the root causes--just fix it when
07981    detected.
07982    */
07983 
07984    if (vm_lock_path(vms->curdir)) {
07985       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07986       return ERROR_LOCK_PATH;
07987    }
07988 
07989    /* for local storage, checks directory for messages up to maxmsg limit */
07990    last_msg = last_message_index(vmu, vms->curdir);
07991    ast_unlock_path(vms->curdir);
07992 
07993    if (last_msg < -1) {
07994       return last_msg;
07995    } else if (vms->lastmsg != last_msg) {
07996       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);
07997       resequence_mailbox(vmu, vms->curdir, count_msg);
07998    }
07999 
08000    return 0;
08001 }
08002 #endif
08003 
08004 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
08005 {
08006    int x = 0;
08007    int last_msg_idx = 0;
08008 
08009 #ifndef IMAP_STORAGE
08010    int res = 0, nummsg;
08011    char fn2[PATH_MAX];
08012 #endif
08013 
08014    if (vms->lastmsg <= -1) {
08015       goto done;
08016    }
08017 
08018    vms->curmsg = -1;
08019 #ifndef IMAP_STORAGE
08020    /* Get the deleted messages fixed */
08021    if (vm_lock_path(vms->curdir)) {
08022       return ERROR_LOCK_PATH;
08023    }
08024 
08025    /* update count as message may have arrived while we've got mailbox open */
08026    last_msg_idx = last_message_index(vmu, vms->curdir);
08027    if (last_msg_idx != vms->lastmsg) {
08028       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08029    }
08030 
08031    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08032    for (x = 0; x < last_msg_idx + 1; x++) {
08033       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08034          /* Save this message.  It's not in INBOX or hasn't been heard */
08035          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08036          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08037             break;
08038          }
08039          vms->curmsg++;
08040          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08041          if (strcmp(vms->fn, fn2)) {
08042             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08043          }
08044       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08045          /* Move to old folder before deleting */
08046          res = save_to_folder(vmu, vms, x, 1);
08047          if (res == ERROR_LOCK_PATH) {
08048             /* If save failed do not delete the message */
08049             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08050             vms->deleted[x] = 0;
08051             vms->heard[x] = 0;
08052             --x;
08053          }
08054       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08055          /* Move to deleted folder */
08056          res = save_to_folder(vmu, vms, x, 10);
08057          if (res == ERROR_LOCK_PATH) {
08058             /* If save failed do not delete the message */
08059             vms->deleted[x] = 0;
08060             vms->heard[x] = 0;
08061             --x;
08062          }
08063       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08064          /* If realtime storage enabled - we should explicitly delete this message,
08065          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08066          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08067          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08068             DELETE(vms->curdir, x, vms->fn, vmu);
08069          }
08070       }
08071    }
08072 
08073    /* Delete ALL remaining messages */
08074    nummsg = x - 1;
08075    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08076       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08077       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08078          DELETE(vms->curdir, x, vms->fn, vmu);
08079       }
08080    }
08081    ast_unlock_path(vms->curdir);
08082 #else /* defined(IMAP_STORAGE) */
08083    ast_mutex_lock(&vms->lock);
08084    if (vms->deleted) {
08085       /* Since we now expunge after each delete, deleting in reverse order
08086        * ensures that no reordering occurs between each step. */
08087       last_msg_idx = vms->dh_arraysize;
08088       for (x = last_msg_idx - 1; x >= 0; x--) {
08089          if (vms->deleted[x]) {
08090             ast_debug(3, "IMAP delete of %d\n", x);
08091             DELETE(vms->curdir, x, vms->fn, vmu);
08092          }
08093       }
08094    }
08095 #endif
08096 
08097 done:
08098    if (vms->deleted) {
08099       ast_free(vms->deleted);
08100       vms->deleted = NULL;
08101    }
08102    if (vms->heard) {
08103       ast_free(vms->heard);
08104       vms->heard = NULL;
08105    }
08106    vms->dh_arraysize = 0;
08107 #ifdef IMAP_STORAGE
08108    ast_mutex_unlock(&vms->lock);
08109 #endif
08110 
08111    return 0;
08112 }
08113 
08114 /* In Greek even though we CAN use a syntax like "friends messages"
08115  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08116  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08117  * syntax for the above three categories which is more elegant.
08118  */
08119 
08120 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08121 {
08122    int cmd;
08123    char *buf;
08124 
08125    buf = ast_alloca(strlen(box) + 2);
08126    strcpy(buf, box);
08127    strcat(buf, "s");
08128 
08129    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08130       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08131       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08132    } else {
08133       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08134       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08135    }
08136 }
08137 
08138 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08139 {
08140    int cmd;
08141 
08142    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08143       if (!strcasecmp(box, "vm-INBOX"))
08144          cmd = ast_play_and_wait(chan, "vm-new-e");
08145       else
08146          cmd = ast_play_and_wait(chan, "vm-old-e");
08147       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08148    } else {
08149       cmd = ast_play_and_wait(chan, "vm-messages");
08150       return cmd ? cmd : ast_play_and_wait(chan, box);
08151    }
08152 }
08153 
08154 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08155 {
08156    int cmd;
08157 
08158    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08159       cmd = ast_play_and_wait(chan, "vm-messages");
08160       return cmd ? cmd : ast_play_and_wait(chan, box);
08161    } else {
08162       cmd = ast_play_and_wait(chan, box);
08163       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08164    }
08165 }
08166 
08167 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08168 {
08169    int cmd;
08170 
08171    if (  !strncasecmp(chan->language, "it", 2) ||
08172         !strncasecmp(chan->language, "es", 2) ||
08173         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08174       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08175       return cmd ? cmd : ast_play_and_wait(chan, box);
08176    } else if (!strncasecmp(chan->language, "gr", 2)) {
08177       return vm_play_folder_name_gr(chan, box);
08178    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08179       return ast_play_and_wait(chan, box);
08180    } else if (!strncasecmp(chan->language, "pl", 2)) {
08181       return vm_play_folder_name_pl(chan, box);
08182    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08183       return vm_play_folder_name_ua(chan, box);
08184    } else if (!strncasecmp(chan->language, "vi", 2)) {
08185       return ast_play_and_wait(chan, box);
08186    } else {  /* Default English */
08187       cmd = ast_play_and_wait(chan, box);
08188       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08189    }
08190 }
08191 
08192 /* GREEK SYNTAX
08193    In greek the plural for old/new is
08194    different so we need the following files
08195    We also need vm-denExeteMynhmata because
08196    this syntax is different.
08197 
08198    -> vm-Olds.wav : "Palia"
08199    -> vm-INBOXs.wav : "Nea"
08200    -> vm-denExeteMynhmata : "den exete mynhmata"
08201 */
08202 
08203 
08204 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08205 {
08206    int res = 0;
08207 
08208    if (vms->newmessages) {
08209       res = ast_play_and_wait(chan, "vm-youhave");
08210       if (!res) 
08211          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08212       if (!res) {
08213          if ((vms->newmessages == 1)) {
08214             res = ast_play_and_wait(chan, "vm-INBOX");
08215             if (!res)
08216                res = ast_play_and_wait(chan, "vm-message");
08217          } else {
08218             res = ast_play_and_wait(chan, "vm-INBOXs");
08219             if (!res)
08220                res = ast_play_and_wait(chan, "vm-messages");
08221          }
08222       }
08223    } else if (vms->oldmessages){
08224       res = ast_play_and_wait(chan, "vm-youhave");
08225       if (!res)
08226          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08227       if ((vms->oldmessages == 1)){
08228          res = ast_play_and_wait(chan, "vm-Old");
08229          if (!res)
08230             res = ast_play_and_wait(chan, "vm-message");
08231       } else {
08232          res = ast_play_and_wait(chan, "vm-Olds");
08233          if (!res)
08234             res = ast_play_and_wait(chan, "vm-messages");
08235       }
08236    } else if (!vms->oldmessages && !vms->newmessages) 
08237       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08238    return res;
08239 }
08240 
08241 /* Version of vm_intro() designed to work for many languages.
08242  *
08243  * It is hoped that this function can prevent the proliferation of 
08244  * language-specific vm_intro() functions and in time replace the language-
08245  * specific functions which already exist.  An examination of the language-
08246  * specific functions revealed that they all corrected the same deficiencies
08247  * in vm_intro_en() (which was the default function). Namely:
08248  *
08249  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08250  *     wording of the voicemail greeting hides this problem.  For example,
08251  *     vm-INBOX contains only the word "new".  This means that both of these
08252  *     sequences produce valid utterances:
08253  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08254  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08255  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08256  *     in many languages) the first utterance becomes "you have 1 the new message".
08257  *  2) The function contains hardcoded rules for pluralizing the word "message".
08258  *     These rules are correct for English, but not for many other languages.
08259  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08260  *     required in many languages.
08261  *  4) The gender of the word for "message" is not specified. This is a problem
08262  *     because in many languages the gender of the number in phrases such
08263  *     as "you have one new message" must match the gender of the word
08264  *     meaning "message".
08265  *
08266  * Fixing these problems for each new language has meant duplication of effort.
08267  * This new function solves the problems in the following general ways:
08268  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08269  *     and vm-Old respectively for those languages where it makes sense.
08270  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08271  *     on vm-message.
08272  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08273  *     prefix on vm-new and vm-old (none for English).
08274  *  4) Pass the gender of the language's word for "message" as an agument to
08275  *     this function which is can in turn pass on to the functions which 
08276  *     say numbers and put endings on nounds and adjectives.
08277  *
08278  * All languages require these messages:
08279  *  vm-youhave    "You have..."
08280  *  vm-and     "and"
08281  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08282  *
08283  * To use it for English, you will need these additional sound files:
08284  *  vm-new     "new"
08285  *  vm-message    "message", singular
08286  *  vm-messages      "messages", plural
08287  *
08288  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08289  *
08290  *  vm-newn    "novoye" (singular, neuter)
08291  *  vm-newx    "novikh" (counting plural form, genative plural)
08292  *  vm-message    "sobsheniye" (singular form)
08293  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08294  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08295  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08296  *  digits/2n     "dva" (neuter singular)
08297  */
08298 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08299 {
08300    int res;
08301    int lastnum = 0;
08302 
08303    res = ast_play_and_wait(chan, "vm-youhave");
08304 
08305    if (!res && vms->newmessages) {
08306       lastnum = vms->newmessages;
08307 
08308       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08309          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08310       }
08311 
08312       if (!res && vms->oldmessages) {
08313          res = ast_play_and_wait(chan, "vm-and");
08314       }
08315    }
08316 
08317    if (!res && vms->oldmessages) {
08318       lastnum = vms->oldmessages;
08319 
08320       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08321          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08322       }
08323    }
08324 
08325    if (!res) {
08326       if (lastnum == 0) {
08327          res = ast_play_and_wait(chan, "vm-no");
08328       }
08329       if (!res) {
08330          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08331       }
08332    }
08333 
08334    return res;
08335 }
08336 
08337 /* Default Hebrew syntax */
08338 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08339 {
08340    int res = 0;
08341 
08342    /* Introduce messages they have */
08343    if (!res) {
08344       if ((vms->newmessages) || (vms->oldmessages)) {
08345          res = ast_play_and_wait(chan, "vm-youhave");
08346       }
08347       /*
08348        * The word "shtei" refers to the number 2 in hebrew when performing a count
08349        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08350        * an element, this is one of them.
08351        */
08352       if (vms->newmessages) {
08353          if (!res) {
08354             if (vms->newmessages == 1) {
08355                res = ast_play_and_wait(chan, "vm-INBOX1");
08356             } else {
08357                if (vms->newmessages == 2) {
08358                   res = ast_play_and_wait(chan, "vm-shtei");
08359                } else {
08360                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08361                }
08362                res = ast_play_and_wait(chan, "vm-INBOX");
08363             }
08364          }
08365          if (vms->oldmessages && !res) {
08366             res = ast_play_and_wait(chan, "vm-and");
08367             if (vms->oldmessages == 1) {
08368                res = ast_play_and_wait(chan, "vm-Old1");
08369             } else {
08370                if (vms->oldmessages == 2) {
08371                   res = ast_play_and_wait(chan, "vm-shtei");
08372                } else {
08373                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08374                }
08375                res = ast_play_and_wait(chan, "vm-Old");
08376             }
08377          }
08378       }
08379       if (!res && vms->oldmessages && !vms->newmessages) {
08380          if (!res) {
08381             if (vms->oldmessages == 1) {
08382                res = ast_play_and_wait(chan, "vm-Old1");
08383             } else {
08384                if (vms->oldmessages == 2) {
08385                   res = ast_play_and_wait(chan, "vm-shtei");
08386                } else {
08387                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08388                }
08389                res = ast_play_and_wait(chan, "vm-Old");
08390             }
08391          }
08392       }
08393       if (!res) {
08394          if (!vms->oldmessages && !vms->newmessages) {
08395             if (!res) {
08396                res = ast_play_and_wait(chan, "vm-nomessages");
08397             }
08398          }
08399       }
08400    }
08401    return res;
08402 }
08403    
08404 /* Default English syntax */
08405 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08406 {
08407    int res;
08408 
08409    /* Introduce messages they have */
08410    res = ast_play_and_wait(chan, "vm-youhave");
08411    if (!res) {
08412       if (vms->urgentmessages) {
08413          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08414          if (!res)
08415             res = ast_play_and_wait(chan, "vm-Urgent");
08416          if ((vms->oldmessages || vms->newmessages) && !res) {
08417             res = ast_play_and_wait(chan, "vm-and");
08418          } else if (!res) {
08419             if ((vms->urgentmessages == 1))
08420                res = ast_play_and_wait(chan, "vm-message");
08421             else
08422                res = ast_play_and_wait(chan, "vm-messages");
08423          }
08424       }
08425       if (vms->newmessages) {
08426          res = say_and_wait(chan, vms->newmessages, chan->language);
08427          if (!res)
08428             res = ast_play_and_wait(chan, "vm-INBOX");
08429          if (vms->oldmessages && !res)
08430             res = ast_play_and_wait(chan, "vm-and");
08431          else if (!res) {
08432             if ((vms->newmessages == 1))
08433                res = ast_play_and_wait(chan, "vm-message");
08434             else
08435                res = ast_play_and_wait(chan, "vm-messages");
08436          }
08437             
08438       }
08439       if (!res && vms->oldmessages) {
08440          res = say_and_wait(chan, vms->oldmessages, chan->language);
08441          if (!res)
08442             res = ast_play_and_wait(chan, "vm-Old");
08443          if (!res) {
08444             if (vms->oldmessages == 1)
08445                res = ast_play_and_wait(chan, "vm-message");
08446             else
08447                res = ast_play_and_wait(chan, "vm-messages");
08448          }
08449       }
08450       if (!res) {
08451          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08452             res = ast_play_and_wait(chan, "vm-no");
08453             if (!res)
08454                res = ast_play_and_wait(chan, "vm-messages");
08455          }
08456       }
08457    }
08458    return res;
08459 }
08460 
08461 /* ITALIAN syntax */
08462 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08463 {
08464    /* Introduce messages they have */
08465    int res;
08466    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08467       res = ast_play_and_wait(chan, "vm-no") ||
08468          ast_play_and_wait(chan, "vm-message");
08469    else
08470       res = ast_play_and_wait(chan, "vm-youhave");
08471    if (!res && vms->newmessages) {
08472       res = (vms->newmessages == 1) ?
08473          ast_play_and_wait(chan, "digits/un") ||
08474          ast_play_and_wait(chan, "vm-nuovo") ||
08475          ast_play_and_wait(chan, "vm-message") :
08476          /* 2 or more new messages */
08477          say_and_wait(chan, vms->newmessages, chan->language) ||
08478          ast_play_and_wait(chan, "vm-nuovi") ||
08479          ast_play_and_wait(chan, "vm-messages");
08480       if (!res && vms->oldmessages)
08481          res = ast_play_and_wait(chan, "vm-and");
08482    }
08483    if (!res && vms->oldmessages) {
08484       res = (vms->oldmessages == 1) ?
08485          ast_play_and_wait(chan, "digits/un") ||
08486          ast_play_and_wait(chan, "vm-vecchio") ||
08487          ast_play_and_wait(chan, "vm-message") :
08488          /* 2 or more old messages */
08489          say_and_wait(chan, vms->oldmessages, chan->language) ||
08490          ast_play_and_wait(chan, "vm-vecchi") ||
08491          ast_play_and_wait(chan, "vm-messages");
08492    }
08493    return res;
08494 }
08495 
08496 /* POLISH syntax */
08497 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08498 {
08499    /* Introduce messages they have */
08500    int res;
08501    div_t num;
08502 
08503    if (!vms->oldmessages && !vms->newmessages) {
08504       res = ast_play_and_wait(chan, "vm-no");
08505       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08506       return res;
08507    } else {
08508       res = ast_play_and_wait(chan, "vm-youhave");
08509    }
08510 
08511    if (vms->newmessages) {
08512       num = div(vms->newmessages, 10);
08513       if (vms->newmessages == 1) {
08514          res = ast_play_and_wait(chan, "digits/1-a");
08515          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08516          res = res ? res : ast_play_and_wait(chan, "vm-message");
08517       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08518          if (num.rem == 2) {
08519             if (!num.quot) {
08520                res = ast_play_and_wait(chan, "digits/2-ie");
08521             } else {
08522                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08523                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08524             }
08525          } else {
08526             res = say_and_wait(chan, vms->newmessages, chan->language);
08527          }
08528          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08529          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08530       } else {
08531          res = say_and_wait(chan, vms->newmessages, chan->language);
08532          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08533          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08534       }
08535       if (!res && vms->oldmessages)
08536          res = ast_play_and_wait(chan, "vm-and");
08537    }
08538    if (!res && vms->oldmessages) {
08539       num = div(vms->oldmessages, 10);
08540       if (vms->oldmessages == 1) {
08541          res = ast_play_and_wait(chan, "digits/1-a");
08542          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08543          res = res ? res : ast_play_and_wait(chan, "vm-message");
08544       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08545          if (num.rem == 2) {
08546             if (!num.quot) {
08547                res = ast_play_and_wait(chan, "digits/2-ie");
08548             } else {
08549                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08550                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08551             }
08552          } else {
08553             res = say_and_wait(chan, vms->oldmessages, chan->language);
08554          }
08555          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08556          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08557       } else {
08558          res = say_and_wait(chan, vms->oldmessages, chan->language);
08559          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08560          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08561       }
08562    }
08563 
08564    return res;
08565 }
08566 
08567 /* SWEDISH syntax */
08568 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08569 {
08570    /* Introduce messages they have */
08571    int res;
08572 
08573    res = ast_play_and_wait(chan, "vm-youhave");
08574    if (res)
08575       return res;
08576 
08577    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08578       res = ast_play_and_wait(chan, "vm-no");
08579       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08580       return res;
08581    }
08582 
08583    if (vms->newmessages) {
08584       if ((vms->newmessages == 1)) {
08585          res = ast_play_and_wait(chan, "digits/ett");
08586          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08587          res = res ? res : ast_play_and_wait(chan, "vm-message");
08588       } else {
08589          res = say_and_wait(chan, vms->newmessages, chan->language);
08590          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08591          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08592       }
08593       if (!res && vms->oldmessages)
08594          res = ast_play_and_wait(chan, "vm-and");
08595    }
08596    if (!res && vms->oldmessages) {
08597       if (vms->oldmessages == 1) {
08598          res = ast_play_and_wait(chan, "digits/ett");
08599          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08600          res = res ? res : ast_play_and_wait(chan, "vm-message");
08601       } else {
08602          res = say_and_wait(chan, vms->oldmessages, chan->language);
08603          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08604          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08605       }
08606    }
08607 
08608    return res;
08609 }
08610 
08611 /* NORWEGIAN syntax */
08612 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08613 {
08614    /* Introduce messages they have */
08615    int res;
08616 
08617    res = ast_play_and_wait(chan, "vm-youhave");
08618    if (res)
08619       return res;
08620 
08621    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08622       res = ast_play_and_wait(chan, "vm-no");
08623       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08624       return res;
08625    }
08626 
08627    if (vms->newmessages) {
08628       if ((vms->newmessages == 1)) {
08629          res = ast_play_and_wait(chan, "digits/1");
08630          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08631          res = res ? res : ast_play_and_wait(chan, "vm-message");
08632       } else {
08633          res = say_and_wait(chan, vms->newmessages, chan->language);
08634          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08635          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08636       }
08637       if (!res && vms->oldmessages)
08638          res = ast_play_and_wait(chan, "vm-and");
08639    }
08640    if (!res && vms->oldmessages) {
08641       if (vms->oldmessages == 1) {
08642          res = ast_play_and_wait(chan, "digits/1");
08643          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08644          res = res ? res : ast_play_and_wait(chan, "vm-message");
08645       } else {
08646          res = say_and_wait(chan, vms->oldmessages, chan->language);
08647          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08648          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08649       }
08650    }
08651 
08652    return res;
08653 }
08654 
08655 /* GERMAN syntax */
08656 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08657 {
08658    /* Introduce messages they have */
08659    int res;
08660    res = ast_play_and_wait(chan, "vm-youhave");
08661    if (!res) {
08662       if (vms->newmessages) {
08663          if ((vms->newmessages == 1))
08664             res = ast_play_and_wait(chan, "digits/1F");
08665          else
08666             res = say_and_wait(chan, vms->newmessages, chan->language);
08667          if (!res)
08668             res = ast_play_and_wait(chan, "vm-INBOX");
08669          if (vms->oldmessages && !res)
08670             res = ast_play_and_wait(chan, "vm-and");
08671          else if (!res) {
08672             if ((vms->newmessages == 1))
08673                res = ast_play_and_wait(chan, "vm-message");
08674             else
08675                res = ast_play_and_wait(chan, "vm-messages");
08676          }
08677             
08678       }
08679       if (!res && vms->oldmessages) {
08680          if (vms->oldmessages == 1)
08681             res = ast_play_and_wait(chan, "digits/1F");
08682          else
08683             res = say_and_wait(chan, vms->oldmessages, chan->language);
08684          if (!res)
08685             res = ast_play_and_wait(chan, "vm-Old");
08686          if (!res) {
08687             if (vms->oldmessages == 1)
08688                res = ast_play_and_wait(chan, "vm-message");
08689             else
08690                res = ast_play_and_wait(chan, "vm-messages");
08691          }
08692       }
08693       if (!res) {
08694          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08695             res = ast_play_and_wait(chan, "vm-no");
08696             if (!res)
08697                res = ast_play_and_wait(chan, "vm-messages");
08698          }
08699       }
08700    }
08701    return res;
08702 }
08703 
08704 /* SPANISH syntax */
08705 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08706 {
08707    /* Introduce messages they have */
08708    int res;
08709    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08710       res = ast_play_and_wait(chan, "vm-youhaveno");
08711       if (!res)
08712          res = ast_play_and_wait(chan, "vm-messages");
08713    } else {
08714       res = ast_play_and_wait(chan, "vm-youhave");
08715    }
08716    if (!res) {
08717       if (vms->newmessages) {
08718          if (!res) {
08719             if ((vms->newmessages == 1)) {
08720                res = ast_play_and_wait(chan, "digits/1M");
08721                if (!res)
08722                   res = ast_play_and_wait(chan, "vm-message");
08723                if (!res)
08724                   res = ast_play_and_wait(chan, "vm-INBOXs");
08725             } else {
08726                res = say_and_wait(chan, vms->newmessages, chan->language);
08727                if (!res)
08728                   res = ast_play_and_wait(chan, "vm-messages");
08729                if (!res)
08730                   res = ast_play_and_wait(chan, "vm-INBOX");
08731             }
08732          }
08733          if (vms->oldmessages && !res)
08734             res = ast_play_and_wait(chan, "vm-and");
08735       }
08736       if (vms->oldmessages) {
08737          if (!res) {
08738             if (vms->oldmessages == 1) {
08739                res = ast_play_and_wait(chan, "digits/1M");
08740                if (!res)
08741                   res = ast_play_and_wait(chan, "vm-message");
08742                if (!res)
08743                   res = ast_play_and_wait(chan, "vm-Olds");
08744             } else {
08745                res = say_and_wait(chan, vms->oldmessages, chan->language);
08746                if (!res)
08747                   res = ast_play_and_wait(chan, "vm-messages");
08748                if (!res)
08749                   res = ast_play_and_wait(chan, "vm-Old");
08750             }
08751          }
08752       }
08753    }
08754 return res;
08755 }
08756 
08757 /* BRAZILIAN PORTUGUESE syntax */
08758 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08759    /* Introduce messages they have */
08760    int res;
08761    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08762       res = ast_play_and_wait(chan, "vm-nomessages");
08763       return res;
08764    } else {
08765       res = ast_play_and_wait(chan, "vm-youhave");
08766    }
08767    if (vms->newmessages) {
08768       if (!res)
08769          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08770       if ((vms->newmessages == 1)) {
08771          if (!res)
08772             res = ast_play_and_wait(chan, "vm-message");
08773          if (!res)
08774             res = ast_play_and_wait(chan, "vm-INBOXs");
08775       } else {
08776          if (!res)
08777             res = ast_play_and_wait(chan, "vm-messages");
08778          if (!res)
08779             res = ast_play_and_wait(chan, "vm-INBOX");
08780       }
08781       if (vms->oldmessages && !res)
08782          res = ast_play_and_wait(chan, "vm-and");
08783    }
08784    if (vms->oldmessages) {
08785       if (!res)
08786          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08787       if (vms->oldmessages == 1) {
08788          if (!res)
08789             res = ast_play_and_wait(chan, "vm-message");
08790          if (!res)
08791             res = ast_play_and_wait(chan, "vm-Olds");
08792       } else {
08793          if (!res)
08794             res = ast_play_and_wait(chan, "vm-messages");
08795          if (!res)
08796             res = ast_play_and_wait(chan, "vm-Old");
08797       }
08798    }
08799    return res;
08800 }
08801 
08802 /* FRENCH syntax */
08803 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08804 {
08805    /* Introduce messages they have */
08806    int res;
08807    res = ast_play_and_wait(chan, "vm-youhave");
08808    if (!res) {
08809       if (vms->newmessages) {
08810          res = say_and_wait(chan, vms->newmessages, chan->language);
08811          if (!res)
08812             res = ast_play_and_wait(chan, "vm-INBOX");
08813          if (vms->oldmessages && !res)
08814             res = ast_play_and_wait(chan, "vm-and");
08815          else if (!res) {
08816             if ((vms->newmessages == 1))
08817                res = ast_play_and_wait(chan, "vm-message");
08818             else
08819                res = ast_play_and_wait(chan, "vm-messages");
08820          }
08821             
08822       }
08823       if (!res && vms->oldmessages) {
08824          res = say_and_wait(chan, vms->oldmessages, chan->language);
08825          if (!res)
08826             res = ast_play_and_wait(chan, "vm-Old");
08827          if (!res) {
08828             if (vms->oldmessages == 1)
08829                res = ast_play_and_wait(chan, "vm-message");
08830             else
08831                res = ast_play_and_wait(chan, "vm-messages");
08832          }
08833       }
08834       if (!res) {
08835          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08836             res = ast_play_and_wait(chan, "vm-no");
08837             if (!res)
08838                res = ast_play_and_wait(chan, "vm-messages");
08839          }
08840       }
08841    }
08842    return res;
08843 }
08844 
08845 /* DUTCH syntax */
08846 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08847 {
08848    /* Introduce messages they have */
08849    int res;
08850    res = ast_play_and_wait(chan, "vm-youhave");
08851    if (!res) {
08852       if (vms->newmessages) {
08853          res = say_and_wait(chan, vms->newmessages, chan->language);
08854          if (!res) {
08855             if (vms->newmessages == 1)
08856                res = ast_play_and_wait(chan, "vm-INBOXs");
08857             else
08858                res = ast_play_and_wait(chan, "vm-INBOX");
08859          }
08860          if (vms->oldmessages && !res)
08861             res = ast_play_and_wait(chan, "vm-and");
08862          else if (!res) {
08863             if ((vms->newmessages == 1))
08864                res = ast_play_and_wait(chan, "vm-message");
08865             else
08866                res = ast_play_and_wait(chan, "vm-messages");
08867          }
08868             
08869       }
08870       if (!res && vms->oldmessages) {
08871          res = say_and_wait(chan, vms->oldmessages, chan->language);
08872          if (!res) {
08873             if (vms->oldmessages == 1)
08874                res = ast_play_and_wait(chan, "vm-Olds");
08875             else
08876                res = ast_play_and_wait(chan, "vm-Old");
08877          }
08878          if (!res) {
08879             if (vms->oldmessages == 1)
08880                res = ast_play_and_wait(chan, "vm-message");
08881             else
08882                res = ast_play_and_wait(chan, "vm-messages");
08883          }
08884       }
08885       if (!res) {
08886          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08887             res = ast_play_and_wait(chan, "vm-no");
08888             if (!res)
08889                res = ast_play_and_wait(chan, "vm-messages");
08890          }
08891       }
08892    }
08893    return res;
08894 }
08895 
08896 /* PORTUGUESE syntax */
08897 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08898 {
08899    /* Introduce messages they have */
08900    int res;
08901    res = ast_play_and_wait(chan, "vm-youhave");
08902    if (!res) {
08903       if (vms->newmessages) {
08904          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08905          if (!res) {
08906             if ((vms->newmessages == 1)) {
08907                res = ast_play_and_wait(chan, "vm-message");
08908                if (!res)
08909                   res = ast_play_and_wait(chan, "vm-INBOXs");
08910             } else {
08911                res = ast_play_and_wait(chan, "vm-messages");
08912                if (!res)
08913                   res = ast_play_and_wait(chan, "vm-INBOX");
08914             }
08915          }
08916          if (vms->oldmessages && !res)
08917             res = ast_play_and_wait(chan, "vm-and");
08918       }
08919       if (!res && vms->oldmessages) {
08920          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08921          if (!res) {
08922             if (vms->oldmessages == 1) {
08923                res = ast_play_and_wait(chan, "vm-message");
08924                if (!res)
08925                   res = ast_play_and_wait(chan, "vm-Olds");
08926             } else {
08927                res = ast_play_and_wait(chan, "vm-messages");
08928                if (!res)
08929                   res = ast_play_and_wait(chan, "vm-Old");
08930             }
08931          }
08932       }
08933       if (!res) {
08934          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08935             res = ast_play_and_wait(chan, "vm-no");
08936             if (!res)
08937                res = ast_play_and_wait(chan, "vm-messages");
08938          }
08939       }
08940    }
08941    return res;
08942 }
08943 
08944 
08945 /* CZECH syntax */
08946 /* in czech there must be declension of word new and message
08947  * czech        : english        : czech      : english
08948  * --------------------------------------------------------
08949  * vm-youhave   : you have 
08950  * vm-novou     : one new        : vm-zpravu  : message
08951  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08952  * vm-novych    : 5-infinite new : vm-zprav   : messages
08953  * vm-starou   : one old
08954  * vm-stare     : 2-4 old 
08955  * vm-starych   : 5-infinite old
08956  * jednu        : one   - falling 4. 
08957  * vm-no        : no  ( no messages )
08958  */
08959 
08960 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08961 {
08962    int res;
08963    res = ast_play_and_wait(chan, "vm-youhave");
08964    if (!res) {
08965       if (vms->newmessages) {
08966          if (vms->newmessages == 1) {
08967             res = ast_play_and_wait(chan, "digits/jednu");
08968          } else {
08969             res = say_and_wait(chan, vms->newmessages, chan->language);
08970          }
08971          if (!res) {
08972             if ((vms->newmessages == 1))
08973                res = ast_play_and_wait(chan, "vm-novou");
08974             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08975                res = ast_play_and_wait(chan, "vm-nove");
08976             if (vms->newmessages > 4)
08977                res = ast_play_and_wait(chan, "vm-novych");
08978          }
08979          if (vms->oldmessages && !res)
08980             res = ast_play_and_wait(chan, "vm-and");
08981          else if (!res) {
08982             if ((vms->newmessages == 1))
08983                res = ast_play_and_wait(chan, "vm-zpravu");
08984             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08985                res = ast_play_and_wait(chan, "vm-zpravy");
08986             if (vms->newmessages > 4)
08987                res = ast_play_and_wait(chan, "vm-zprav");
08988          }
08989       }
08990       if (!res && vms->oldmessages) {
08991          res = say_and_wait(chan, vms->oldmessages, chan->language);
08992          if (!res) {
08993             if ((vms->oldmessages == 1))
08994                res = ast_play_and_wait(chan, "vm-starou");
08995             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08996                res = ast_play_and_wait(chan, "vm-stare");
08997             if (vms->oldmessages > 4)
08998                res = ast_play_and_wait(chan, "vm-starych");
08999          }
09000          if (!res) {
09001             if ((vms->oldmessages == 1))
09002                res = ast_play_and_wait(chan, "vm-zpravu");
09003             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09004                res = ast_play_and_wait(chan, "vm-zpravy");
09005             if (vms->oldmessages > 4)
09006                res = ast_play_and_wait(chan, "vm-zprav");
09007          }
09008       }
09009       if (!res) {
09010          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09011             res = ast_play_and_wait(chan, "vm-no");
09012             if (!res)
09013                res = ast_play_and_wait(chan, "vm-zpravy");
09014          }
09015       }
09016    }
09017    return res;
09018 }
09019 
09020 /* CHINESE (Taiwan) syntax */
09021 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09022 {
09023    int res;
09024    /* Introduce messages they have */
09025    res = ast_play_and_wait(chan, "vm-you");
09026 
09027    if (!res && vms->newmessages) {
09028       res = ast_play_and_wait(chan, "vm-have");
09029       if (!res)
09030          res = say_and_wait(chan, vms->newmessages, chan->language);
09031       if (!res)
09032          res = ast_play_and_wait(chan, "vm-tong");
09033       if (!res)
09034          res = ast_play_and_wait(chan, "vm-INBOX");
09035       if (vms->oldmessages && !res)
09036          res = ast_play_and_wait(chan, "vm-and");
09037       else if (!res) 
09038          res = ast_play_and_wait(chan, "vm-messages");
09039    }
09040    if (!res && vms->oldmessages) {
09041       res = ast_play_and_wait(chan, "vm-have");
09042       if (!res)
09043          res = say_and_wait(chan, vms->oldmessages, chan->language);
09044       if (!res)
09045          res = ast_play_and_wait(chan, "vm-tong");
09046       if (!res)
09047          res = ast_play_and_wait(chan, "vm-Old");
09048       if (!res)
09049          res = ast_play_and_wait(chan, "vm-messages");
09050    }
09051    if (!res && !vms->oldmessages && !vms->newmessages) {
09052       res = ast_play_and_wait(chan, "vm-haveno");
09053       if (!res)
09054          res = ast_play_and_wait(chan, "vm-messages");
09055    }
09056    return res;
09057 }
09058 
09059 /* Vietnamese syntax */
09060 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09061 {
09062    int res;
09063 
09064    /* Introduce messages they have */
09065    res = ast_play_and_wait(chan, "vm-youhave");
09066    if (!res) {
09067       if (vms->newmessages) {
09068          res = say_and_wait(chan, vms->newmessages, chan->language);
09069          if (!res)
09070             res = ast_play_and_wait(chan, "vm-INBOX");
09071          if (vms->oldmessages && !res)
09072             res = ast_play_and_wait(chan, "vm-and");
09073       }
09074       if (!res && vms->oldmessages) {
09075          res = say_and_wait(chan, vms->oldmessages, chan->language);
09076          if (!res)
09077             res = ast_play_and_wait(chan, "vm-Old");        
09078       }
09079       if (!res) {
09080          if (!vms->oldmessages && !vms->newmessages) {
09081             res = ast_play_and_wait(chan, "vm-no");
09082             if (!res)
09083                res = ast_play_and_wait(chan, "vm-message");
09084          }
09085       }
09086    }
09087    return res;
09088 }
09089 
09090 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09091 {
09092    char prefile[256];
09093    
09094    /* Notify the user that the temp greeting is set and give them the option to remove it */
09095    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09096    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09097       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09098       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09099          ast_play_and_wait(chan, "vm-tempgreetactive");
09100       }
09101       DISPOSE(prefile, -1);
09102    }
09103 
09104    /* Play voicemail intro - syntax is different for different languages */
09105    if (0) {
09106       return 0;
09107    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09108       return vm_intro_cs(chan, vms);
09109    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09110       static int deprecation_warning = 0;
09111       if (deprecation_warning++ % 10 == 0) {
09112          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09113       }
09114       return vm_intro_cs(chan, vms);
09115    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09116       return vm_intro_de(chan, vms);
09117    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09118       return vm_intro_es(chan, vms);
09119    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09120       return vm_intro_fr(chan, vms);
09121    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09122       return vm_intro_gr(chan, vms);
09123    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09124       return vm_intro_he(chan, vms);
09125    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09126       return vm_intro_it(chan, vms);
09127    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09128       return vm_intro_nl(chan, vms);
09129    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09130       return vm_intro_no(chan, vms);
09131    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09132       return vm_intro_pl(chan, vms);
09133    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09134       return vm_intro_pt_BR(chan, vms);
09135    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09136       return vm_intro_pt(chan, vms);
09137    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09138       return vm_intro_multilang(chan, vms, "n");
09139    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09140       return vm_intro_se(chan, vms);
09141    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09142       return vm_intro_multilang(chan, vms, "n");
09143    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09144       return vm_intro_vi(chan, vms);
09145    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09146       return vm_intro_zh(chan, vms);
09147    } else {                                             /* Default to ENGLISH */
09148       return vm_intro_en(chan, vms);
09149    }
09150 }
09151 
09152 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09153 {
09154    int res = 0;
09155    /* Play instructions and wait for new command */
09156    while (!res) {
09157       if (vms->starting) {
09158          if (vms->lastmsg > -1) {
09159             if (skipadvanced)
09160                res = ast_play_and_wait(chan, "vm-onefor-full");
09161             else
09162                res = ast_play_and_wait(chan, "vm-onefor");
09163             if (!res)
09164                res = vm_play_folder_name(chan, vms->vmbox);
09165          }
09166          if (!res) {
09167             if (skipadvanced)
09168                res = ast_play_and_wait(chan, "vm-opts-full");
09169             else
09170                res = ast_play_and_wait(chan, "vm-opts");
09171          }
09172       } else {
09173          /* Added for additional help */
09174          if (skipadvanced) {
09175             res = ast_play_and_wait(chan, "vm-onefor-full");
09176             if (!res)
09177                res = vm_play_folder_name(chan, vms->vmbox);
09178             res = ast_play_and_wait(chan, "vm-opts-full");
09179          }
09180          /* Logic:
09181           * If the current message is not the first OR
09182           * if we're listening to the first new message and there are
09183           * also urgent messages, then prompt for navigation to the
09184           * previous message
09185           */
09186          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09187             res = ast_play_and_wait(chan, "vm-prev");
09188          }
09189          if (!res && !skipadvanced)
09190             res = ast_play_and_wait(chan, "vm-advopts");
09191          if (!res)
09192             res = ast_play_and_wait(chan, "vm-repeat");
09193          /* Logic:
09194           * If we're not listening to the last message OR
09195           * we're listening to the last urgent message and there are
09196           * also new non-urgent messages, then prompt for navigation
09197           * to the next message
09198           */
09199          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09200             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09201             res = ast_play_and_wait(chan, "vm-next");
09202          }
09203          if (!res) {
09204             int curmsg_deleted;
09205 #ifdef IMAP_STORAGE
09206             ast_mutex_lock(&vms->lock);
09207 #endif
09208             curmsg_deleted = vms->deleted[vms->curmsg];
09209 #ifdef IMAP_STORAGE
09210             ast_mutex_unlock(&vms->lock);
09211 #endif
09212             if (!curmsg_deleted) {
09213                res = ast_play_and_wait(chan, "vm-delete");
09214             } else {
09215                res = ast_play_and_wait(chan, "vm-undelete");
09216             }
09217             if (!res) {
09218                res = ast_play_and_wait(chan, "vm-toforward");
09219             }
09220             if (!res) {
09221                res = ast_play_and_wait(chan, "vm-savemessage");
09222             }
09223          }
09224       }
09225       if (!res) {
09226          res = ast_play_and_wait(chan, "vm-helpexit");
09227       }
09228       if (!res)
09229          res = ast_waitfordigit(chan, 6000);
09230       if (!res) {
09231          vms->repeats++;
09232          if (vms->repeats > 2) {
09233             res = 't';
09234          }
09235       }
09236    }
09237    return res;
09238 }
09239 
09240 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09241 {
09242    int res = 0;
09243    /* Play instructions and wait for new command */
09244    while (!res) {
09245       if (vms->lastmsg > -1) {
09246          res = ast_play_and_wait(chan, "vm-listen");
09247          if (!res)
09248             res = vm_play_folder_name(chan, vms->vmbox);
09249          if (!res)
09250             res = ast_play_and_wait(chan, "press");
09251          if (!res)
09252             res = ast_play_and_wait(chan, "digits/1");
09253       }
09254       if (!res)
09255          res = ast_play_and_wait(chan, "vm-opts");
09256       if (!res) {
09257          vms->starting = 0;
09258          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09259       }
09260    }
09261    return res;
09262 }
09263 
09264 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09265 {
09266    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09267       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09268    } else {             /* Default to ENGLISH */
09269       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09270    }
09271 }
09272 
09273 
09274 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09275 {
09276    int cmd = 0;
09277    int duration = 0;
09278    int tries = 0;
09279    char newpassword[80] = "";
09280    char newpassword2[80] = "";
09281    char prefile[PATH_MAX] = "";
09282    unsigned char buf[256];
09283    int bytes = 0;
09284 
09285    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09286    if (ast_adsi_available(chan)) {
09287       bytes += adsi_logo(buf + bytes);
09288       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09289       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09290       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09291       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09292       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09293    }
09294 
09295    /* If forcename is set, have the user record their name */
09296    if (ast_test_flag(vmu, VM_FORCENAME)) {
09297       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09298       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09299          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09300          if (cmd < 0 || cmd == 't' || cmd == '#')
09301             return cmd;
09302       }
09303    }
09304 
09305    /* If forcegreetings is set, have the user record their greetings */
09306    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09307       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09308       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09309          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09310          if (cmd < 0 || cmd == 't' || cmd == '#')
09311             return cmd;
09312       }
09313 
09314       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09315       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09316          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09317          if (cmd < 0 || cmd == 't' || cmd == '#')
09318             return cmd;
09319       }
09320    }
09321 
09322    /*
09323     * Change the password last since new users will be able to skip over any steps this one comes before
09324     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09325     */
09326    for (;;) {
09327       newpassword[1] = '\0';
09328       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09329       if (cmd == '#')
09330          newpassword[0] = '\0';
09331       if (cmd < 0 || cmd == 't' || cmd == '#')
09332          return cmd;
09333       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09334       if (cmd < 0 || cmd == 't' || cmd == '#')
09335          return cmd;
09336       cmd = check_password(vmu, newpassword); /* perform password validation */
09337       if (cmd != 0) {
09338          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09339          cmd = ast_play_and_wait(chan, vm_invalid_password);
09340       } else {
09341          newpassword2[1] = '\0';
09342          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09343          if (cmd == '#')
09344             newpassword2[0] = '\0';
09345          if (cmd < 0 || cmd == 't' || cmd == '#')
09346             return cmd;
09347          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09348          if (cmd < 0 || cmd == 't' || cmd == '#')
09349             return cmd;
09350          if (!strcmp(newpassword, newpassword2))
09351             break;
09352          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09353          cmd = ast_play_and_wait(chan, vm_mismatch);
09354       }
09355       if (++tries == 3)
09356          return -1;
09357       if (cmd != 0) {
09358          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09359       }
09360    }
09361    if (pwdchange & PWDCHANGE_INTERNAL)
09362       vm_change_password(vmu, newpassword);
09363    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09364       vm_change_password_shell(vmu, newpassword);
09365 
09366    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09367    cmd = ast_play_and_wait(chan, vm_passchanged);
09368 
09369    return cmd;
09370 }
09371 
09372 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09373 {
09374    int cmd = 0;
09375    int retries = 0;
09376    int duration = 0;
09377    char newpassword[80] = "";
09378    char newpassword2[80] = "";
09379    char prefile[PATH_MAX] = "";
09380    unsigned char buf[256];
09381    int bytes = 0;
09382 
09383    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09384    if (ast_adsi_available(chan)) {
09385       bytes += adsi_logo(buf + bytes);
09386       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09387       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09388       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09389       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09390       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09391    }
09392    while ((cmd >= 0) && (cmd != 't')) {
09393       if (cmd)
09394          retries = 0;
09395       switch (cmd) {
09396       case '1': /* Record your unavailable message */
09397          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09398          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09399          break;
09400       case '2':  /* Record your busy message */
09401          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09402          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09403          break;
09404       case '3': /* Record greeting */
09405          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09406          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09407          break;
09408       case '4':  /* manage the temporary greeting */
09409          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09410          break;
09411       case '5': /* change password */
09412          if (vmu->password[0] == '-') {
09413             cmd = ast_play_and_wait(chan, "vm-no");
09414             break;
09415          }
09416          newpassword[1] = '\0';
09417          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09418          if (cmd == '#')
09419             newpassword[0] = '\0';
09420          else {
09421             if (cmd < 0)
09422                break;
09423             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09424                break;
09425             }
09426          }
09427          cmd = check_password(vmu, newpassword); /* perform password validation */
09428          if (cmd != 0) {
09429             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09430             cmd = ast_play_and_wait(chan, vm_invalid_password);
09431             if (!cmd) {
09432                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09433             }
09434             break;
09435          }
09436          newpassword2[1] = '\0';
09437          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09438          if (cmd == '#')
09439             newpassword2[0] = '\0';
09440          else {
09441             if (cmd < 0)
09442                break;
09443 
09444             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09445                break;
09446             }
09447          }
09448          if (strcmp(newpassword, newpassword2)) {
09449             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09450             cmd = ast_play_and_wait(chan, vm_mismatch);
09451             if (!cmd) {
09452                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09453             }
09454             break;
09455          }
09456 
09457          if (pwdchange & PWDCHANGE_INTERNAL) {
09458             vm_change_password(vmu, newpassword);
09459          }
09460          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09461             vm_change_password_shell(vmu, newpassword);
09462          }
09463 
09464          ast_debug(1, "User %s set password to %s of length %d\n",
09465             vms->username, newpassword, (int) strlen(newpassword));
09466          cmd = ast_play_and_wait(chan, vm_passchanged);
09467          break;
09468       case '*': 
09469          cmd = 't';
09470          break;
09471       default: 
09472          cmd = 0;
09473          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09474          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09475          if (ast_fileexists(prefile, NULL, NULL)) {
09476             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09477          }
09478          DISPOSE(prefile, -1);
09479          if (!cmd) {
09480             cmd = ast_play_and_wait(chan, "vm-options");
09481          }
09482          if (!cmd) {
09483             cmd = ast_waitfordigit(chan, 6000);
09484          }
09485          if (!cmd) {
09486             retries++;
09487          }
09488          if (retries > 3) {
09489             cmd = 't';
09490          }
09491          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09492       }
09493    }
09494    if (cmd == 't')
09495       cmd = 0;
09496    return cmd;
09497 }
09498 
09499 /*!
09500  * \brief The handler for 'record a temporary greeting'. 
09501  * \param chan
09502  * \param vmu
09503  * \param vms
09504  * \param fmtc
09505  * \param record_gain
09506  *
09507  * This is option 4 from the mailbox options menu.
09508  * This function manages the following promptings:
09509  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09510  * 2: remove (delete) the temporary greeting.
09511  * *: return to the main menu.
09512  *
09513  * \return zero on success, -1 on error.
09514  */
09515 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09516 {
09517    int cmd = 0;
09518    int retries = 0;
09519    int duration = 0;
09520    char prefile[PATH_MAX] = "";
09521    unsigned char buf[256];
09522    int bytes = 0;
09523 
09524    if (ast_adsi_available(chan)) {
09525       bytes += adsi_logo(buf + bytes);
09526       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09527       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09528       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09529       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09530       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09531    }
09532 
09533    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09534    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09535    while ((cmd >= 0) && (cmd != 't')) {
09536       if (cmd)
09537          retries = 0;
09538       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09539       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09540          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09541          if (cmd == -1) {
09542             break;
09543          }
09544          cmd = 't';  
09545       } else {
09546          switch (cmd) {
09547          case '1':
09548             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09549             break;
09550          case '2':
09551             DELETE(prefile, -1, prefile, vmu);
09552             ast_play_and_wait(chan, "vm-tempremoved");
09553             cmd = 't';  
09554             break;
09555          case '*': 
09556             cmd = 't';
09557             break;
09558          default:
09559             cmd = ast_play_and_wait(chan,
09560                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09561                   "vm-tempgreeting2" : "vm-tempgreeting");
09562             if (!cmd) {
09563                cmd = ast_waitfordigit(chan, 6000);
09564             }
09565             if (!cmd) {
09566                retries++;
09567             }
09568             if (retries > 3) {
09569                cmd = 't';
09570             }
09571             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09572          }
09573       }
09574       DISPOSE(prefile, -1);
09575    }
09576    if (cmd == 't')
09577       cmd = 0;
09578    return cmd;
09579 }
09580 
09581 /*!
09582  * \brief Greek syntax for 'You have N messages' greeting.
09583  * \param chan
09584  * \param vms
09585  * \param vmu
09586  *
09587  * \return zero on success, -1 on error.
09588  */   
09589 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09590 {
09591    int cmd = 0;
09592 
09593    if (vms->lastmsg > -1) {
09594       cmd = play_message(chan, vmu, vms);
09595    } else {
09596       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09597       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09598          if (!cmd) {
09599             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09600             cmd = ast_play_and_wait(chan, vms->fn);
09601          }
09602          if (!cmd)
09603             cmd = ast_play_and_wait(chan, "vm-messages");
09604       } else {
09605          if (!cmd)
09606             cmd = ast_play_and_wait(chan, "vm-messages");
09607          if (!cmd) {
09608             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09609             cmd = ast_play_and_wait(chan, vms->fn);
09610          }
09611       }
09612    } 
09613    return cmd;
09614 }
09615 
09616 /* Hebrew Syntax */
09617 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09618 {
09619    int cmd = 0;
09620 
09621    if (vms->lastmsg > -1) {
09622       cmd = play_message(chan, vmu, vms);
09623    } else {
09624       if (!strcasecmp(vms->fn, "INBOX")) {
09625          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09626       } else {
09627          cmd = ast_play_and_wait(chan, "vm-nomessages");
09628       }
09629    }
09630    return cmd;
09631 }
09632 
09633 /*! 
09634  * \brief Default English syntax for 'You have N messages' greeting.
09635  * \param chan
09636  * \param vms
09637  * \param vmu
09638  *
09639  * \return zero on success, -1 on error.
09640  */
09641 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09642 {
09643    int cmd = 0;
09644 
09645    if (vms->lastmsg > -1) {
09646       cmd = play_message(chan, vmu, vms);
09647    } else {
09648       cmd = ast_play_and_wait(chan, "vm-youhave");
09649       if (!cmd) 
09650          cmd = ast_play_and_wait(chan, "vm-no");
09651       if (!cmd) {
09652          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09653          cmd = ast_play_and_wait(chan, vms->fn);
09654       }
09655       if (!cmd)
09656          cmd = ast_play_and_wait(chan, "vm-messages");
09657    }
09658    return cmd;
09659 }
09660 
09661 /*! 
09662  *\brief Italian syntax for 'You have N messages' greeting.
09663  * \param chan
09664  * \param vms
09665  * \param vmu
09666  *
09667  * \return zero on success, -1 on error.
09668  */
09669 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09670 {
09671    int cmd;
09672 
09673    if (vms->lastmsg > -1) {
09674       cmd = play_message(chan, vmu, vms);
09675    } else {
09676       cmd = ast_play_and_wait(chan, "vm-no");
09677       if (!cmd)
09678          cmd = ast_play_and_wait(chan, "vm-message");
09679       if (!cmd) {
09680          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09681          cmd = ast_play_and_wait(chan, vms->fn);
09682       }
09683    }
09684    return cmd;
09685 }
09686 
09687 /*! 
09688  * \brief Spanish syntax for 'You have N messages' greeting.
09689  * \param chan
09690  * \param vms
09691  * \param vmu
09692  *
09693  * \return zero on success, -1 on error.
09694  */
09695 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09696 {
09697    int cmd;
09698 
09699    if (vms->lastmsg > -1) {
09700       cmd = play_message(chan, vmu, vms);
09701    } else {
09702       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09703       if (!cmd)
09704          cmd = ast_play_and_wait(chan, "vm-messages");
09705       if (!cmd) {
09706          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09707          cmd = ast_play_and_wait(chan, vms->fn);
09708       }
09709    }
09710    return cmd;
09711 }
09712 
09713 /*! 
09714  * \brief Portuguese syntax for 'You have N messages' greeting.
09715  * \param chan
09716  * \param vms
09717  * \param vmu
09718  *
09719  * \return zero on success, -1 on error.
09720  */
09721 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09722 {
09723    int cmd;
09724 
09725    if (vms->lastmsg > -1) {
09726       cmd = play_message(chan, vmu, vms);
09727    } else {
09728       cmd = ast_play_and_wait(chan, "vm-no");
09729       if (!cmd) {
09730          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09731          cmd = ast_play_and_wait(chan, vms->fn);
09732       }
09733       if (!cmd)
09734          cmd = ast_play_and_wait(chan, "vm-messages");
09735    }
09736    return cmd;
09737 }
09738 
09739 /*! 
09740  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09741  * \param chan
09742  * \param vms
09743  * \param vmu
09744  *
09745  * \return zero on success, -1 on error.
09746  */
09747 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09748 {
09749    int cmd;
09750 
09751    if (vms->lastmsg > -1) {
09752       cmd = play_message(chan, vmu, vms);
09753    } else {
09754       cmd = ast_play_and_wait(chan, "vm-you");
09755       if (!cmd) 
09756          cmd = ast_play_and_wait(chan, "vm-haveno");
09757       if (!cmd)
09758          cmd = ast_play_and_wait(chan, "vm-messages");
09759       if (!cmd) {
09760          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09761          cmd = ast_play_and_wait(chan, vms->fn);
09762       }
09763    }
09764    return cmd;
09765 }
09766 
09767 /*! 
09768  * \brief Vietnamese syntax for 'You have N messages' greeting.
09769  * \param chan
09770  * \param vms
09771  * \param vmu
09772  *
09773  * \return zero on success, -1 on error.
09774  */
09775 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09776 {
09777    int cmd = 0;
09778 
09779    if (vms->lastmsg > -1) {
09780       cmd = play_message(chan, vmu, vms);
09781    } else {
09782       cmd = ast_play_and_wait(chan, "vm-no");
09783       if (!cmd) {
09784          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09785          cmd = ast_play_and_wait(chan, vms->fn);
09786       }
09787    }
09788    return cmd;
09789 }
09790 
09791 /*!
09792  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09793  * \param chan The channel for the current user. We read the language property from this.
09794  * \param vms passed into the language-specific vm_browse_messages function.
09795  * \param vmu passed into the language-specific vm_browse_messages function.
09796  * 
09797  * The method to be invoked is determined by the value of language code property in the user's channel.
09798  * The default (when unable to match) is to use english.
09799  *
09800  * \return zero on success, -1 on error.
09801  */
09802 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09803 {
09804    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09805       return vm_browse_messages_es(chan, vms, vmu);
09806    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09807       return vm_browse_messages_gr(chan, vms, vmu);
09808    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09809       return vm_browse_messages_he(chan, vms, vmu);
09810    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09811       return vm_browse_messages_it(chan, vms, vmu);
09812    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09813       return vm_browse_messages_pt(chan, vms, vmu);
09814    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09815       return vm_browse_messages_vi(chan, vms, vmu);
09816    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09817       return vm_browse_messages_zh(chan, vms, vmu);
09818    } else {                                             /* Default to English syntax */
09819       return vm_browse_messages_en(chan, vms, vmu);
09820    }
09821 }
09822 
09823 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09824          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09825          int skipuser, int max_logins, int silent)
09826 {
09827    int useadsi = 0, valid = 0, logretries = 0;
09828    char password[AST_MAX_EXTENSION]="", *passptr;
09829    struct ast_vm_user vmus, *vmu = NULL;
09830 
09831    /* If ADSI is supported, setup login screen */
09832    adsi_begin(chan, &useadsi);
09833    if (!skipuser && useadsi)
09834       adsi_login(chan);
09835    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09836       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09837       return -1;
09838    }
09839 
09840    /* Authenticate them and get their mailbox/password */
09841 
09842    while (!valid && (logretries < max_logins)) {
09843       /* Prompt for, and read in the username */
09844       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09845          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09846          return -1;
09847       }
09848       if (ast_strlen_zero(mailbox)) {
09849          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09850             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09851          } else {
09852             ast_verb(3, "Username not entered\n"); 
09853             return -1;
09854          }
09855       } else if (mailbox[0] == '*') {
09856          /* user entered '*' */
09857          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09858          if (ast_exists_extension(chan, chan->context, "a", 1,
09859             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09860             return -1;
09861          }
09862          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09863          mailbox[0] = '\0';
09864       }
09865 
09866       if (useadsi)
09867          adsi_password(chan);
09868 
09869       if (!ast_strlen_zero(prefix)) {
09870          char fullusername[80] = "";
09871          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09872          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09873          ast_copy_string(mailbox, fullusername, mailbox_size);
09874       }
09875 
09876       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09877       vmu = find_user(&vmus, context, mailbox);
09878       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09879          /* saved password is blank, so don't bother asking */
09880          password[0] = '\0';
09881       } else {
09882          if (ast_streamfile(chan, vm_password, chan->language)) {
09883             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09884             return -1;
09885          }
09886          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09887             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09888             return -1;
09889          } else if (password[0] == '*') {
09890             /* user entered '*' */
09891             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09892             if (ast_exists_extension(chan, chan->context, "a", 1,
09893                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09894                mailbox[0] = '*';
09895                return -1;
09896             }
09897             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09898             mailbox[0] = '\0';
09899             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09900             vmu = NULL;
09901          }
09902       }
09903 
09904       if (vmu) {
09905          passptr = vmu->password;
09906          if (passptr[0] == '-') passptr++;
09907       }
09908       if (vmu && !strcmp(passptr, password))
09909          valid++;
09910       else {
09911          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09912          if (!ast_strlen_zero(prefix))
09913             mailbox[0] = '\0';
09914       }
09915       logretries++;
09916       if (!valid) {
09917          if (skipuser || logretries >= max_logins) {
09918             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09919                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09920                return -1;
09921             }
09922          } else {
09923             if (useadsi)
09924                adsi_login(chan);
09925             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09926                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09927                return -1;
09928             }
09929          }
09930          if (ast_waitstream(chan, "")) /* Channel is hung up */
09931             return -1;
09932       }
09933    }
09934    if (!valid && (logretries >= max_logins)) {
09935       ast_stopstream(chan);
09936       ast_play_and_wait(chan, "vm-goodbye");
09937       return -1;
09938    }
09939    if (vmu && !skipuser) {
09940       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09941    }
09942    return 0;
09943 }
09944 
09945 static int vm_execmain(struct ast_channel *chan, const char *data)
09946 {
09947    /* XXX This is, admittedly, some pretty horrendous code.  For some
09948       reason it just seemed a lot easier to do with GOTO's.  I feel
09949       like I'm back in my GWBASIC days. XXX */
09950    int res = -1;
09951    int cmd = 0;
09952    int valid = 0;
09953    char prefixstr[80] ="";
09954    char ext_context[256]="";
09955    int box;
09956    int useadsi = 0;
09957    int skipuser = 0;
09958    struct vm_state vms;
09959    struct ast_vm_user *vmu = NULL, vmus;
09960    char *context = NULL;
09961    int silentexit = 0;
09962    struct ast_flags flags = { 0 };
09963    signed char record_gain = 0;
09964    int play_auto = 0;
09965    int play_folder = 0;
09966    int in_urgent = 0;
09967 #ifdef IMAP_STORAGE
09968    int deleted = 0;
09969 #endif
09970 
09971    /* Add the vm_state to the active list and keep it active */
09972    memset(&vms, 0, sizeof(vms));
09973 
09974    vms.lastmsg = -1;
09975 
09976    memset(&vmus, 0, sizeof(vmus));
09977 
09978    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09979    if (chan->_state != AST_STATE_UP) {
09980       ast_debug(1, "Before ast_answer\n");
09981       ast_answer(chan);
09982    }
09983 
09984    if (!ast_strlen_zero(data)) {
09985       char *opts[OPT_ARG_ARRAY_SIZE];
09986       char *parse;
09987       AST_DECLARE_APP_ARGS(args,
09988          AST_APP_ARG(argv0);
09989          AST_APP_ARG(argv1);
09990       );
09991 
09992       parse = ast_strdupa(data);
09993 
09994       AST_STANDARD_APP_ARGS(args, parse);
09995 
09996       if (args.argc == 2) {
09997          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09998             return -1;
09999          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10000             int gain;
10001             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
10002                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10003                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10004                   return -1;
10005                } else {
10006                   record_gain = (signed char) gain;
10007                }
10008             } else {
10009                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
10010             }
10011          }
10012          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
10013             play_auto = 1;
10014             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
10015                /* See if it is a folder name first */
10016                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
10017                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
10018                      play_folder = -1;
10019                   }
10020                } else {
10021                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10022                }
10023             } else {
10024                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10025             }
10026             if (play_folder > 9 || play_folder < 0) {
10027                ast_log(AST_LOG_WARNING,
10028                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10029                   opts[OPT_ARG_PLAYFOLDER]);
10030                play_folder = 0;
10031             }
10032          }
10033       } else {
10034          /* old style options parsing */
10035          while (*(args.argv0)) {
10036             if (*(args.argv0) == 's')
10037                ast_set_flag(&flags, OPT_SILENT);
10038             else if (*(args.argv0) == 'p')
10039                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10040             else 
10041                break;
10042             (args.argv0)++;
10043          }
10044 
10045       }
10046 
10047       valid = ast_test_flag(&flags, OPT_SILENT);
10048 
10049       if ((context = strchr(args.argv0, '@')))
10050          *context++ = '\0';
10051 
10052       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10053          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10054       else
10055          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10056 
10057       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10058          skipuser++;
10059       else
10060          valid = 0;
10061    }
10062 
10063    if (!valid)
10064       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10065 
10066    ast_debug(1, "After vm_authenticate\n");
10067 
10068    if (vms.username[0] == '*') {
10069       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10070 
10071       /* user entered '*' */
10072       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10073          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10074          res = 0; /* prevent hangup */
10075          goto out;
10076       }
10077    }
10078 
10079    if (!res) {
10080       valid = 1;
10081       if (!skipuser)
10082          vmu = &vmus;
10083    } else {
10084       res = 0;
10085    }
10086 
10087    /* If ADSI is supported, setup login screen */
10088    adsi_begin(chan, &useadsi);
10089 
10090    ast_test_suite_assert(valid);
10091    if (!valid) {
10092       goto out;
10093    }
10094    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10095 
10096 #ifdef IMAP_STORAGE
10097    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10098    pthread_setspecific(ts_vmstate.key, &vms);
10099 
10100    vms.interactive = 1;
10101    vms.updated = 1;
10102    if (vmu)
10103       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10104    vmstate_insert(&vms);
10105    init_vm_state(&vms);
10106 #endif
10107    
10108    /* Set language from config to override channel language */
10109    if (!ast_strlen_zero(vmu->language))
10110       ast_string_field_set(chan, language, vmu->language);
10111 
10112    /* Retrieve urgent, old and new message counts */
10113    ast_debug(1, "Before open_mailbox\n");
10114    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10115    if (res < 0)
10116       goto out;
10117    vms.oldmessages = vms.lastmsg + 1;
10118    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10119    /* check INBOX */
10120    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10121    if (res < 0)
10122       goto out;
10123    vms.newmessages = vms.lastmsg + 1;
10124    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10125    /* Start in Urgent */
10126    in_urgent = 1;
10127    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10128    if (res < 0)
10129       goto out;
10130    vms.urgentmessages = vms.lastmsg + 1;
10131    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10132 
10133    /* Select proper mailbox FIRST!! */
10134    if (play_auto) {
10135       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10136       if (vms.urgentmessages) {
10137          in_urgent = 1;
10138          res = open_mailbox(&vms, vmu, 11);
10139       } else {
10140          in_urgent = 0;
10141          res = open_mailbox(&vms, vmu, play_folder);
10142       }
10143       if (res < 0)
10144          goto out;
10145 
10146       /* If there are no new messages, inform the user and hangup */
10147       if (vms.lastmsg == -1) {
10148          in_urgent = 0;
10149          cmd = vm_browse_messages(chan, &vms, vmu);
10150          res = 0;
10151          goto out;
10152       }
10153    } else {
10154       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10155          /* If we only have old messages start here */
10156          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10157          in_urgent = 0;
10158          play_folder = 1;
10159          if (res < 0)
10160             goto out;
10161       } else if (!vms.urgentmessages && vms.newmessages) {
10162          /* If we have new messages but none are urgent */
10163          in_urgent = 0;
10164          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10165          if (res < 0)
10166             goto out;
10167       }
10168    }
10169 
10170    if (useadsi)
10171       adsi_status(chan, &vms);
10172    res = 0;
10173 
10174    /* Check to see if this is a new user */
10175    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10176       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10177       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10178          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10179       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10180       if ((cmd == 't') || (cmd == '#')) {
10181          /* Timeout */
10182          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10183          res = 0;
10184          goto out;
10185       } else if (cmd < 0) {
10186          /* Hangup */
10187          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10188          res = -1;
10189          goto out;
10190       }
10191    }
10192 #ifdef IMAP_STORAGE
10193       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10194       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10195          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10196          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10197       }
10198       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10199       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10200          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10201          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10202       }
10203 #endif
10204 
10205    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10206    if (play_auto) {
10207       cmd = '1';
10208    } else {
10209       cmd = vm_intro(chan, vmu, &vms);
10210    }
10211    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10212 
10213    vms.repeats = 0;
10214    vms.starting = 1;
10215    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10216       /* Run main menu */
10217       switch (cmd) {
10218       case '1': /* First message */
10219          vms.curmsg = 0;
10220          /* Fall through */
10221       case '5': /* Play current message */
10222          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10223          cmd = vm_browse_messages(chan, &vms, vmu);
10224          break;
10225       case '2': /* Change folders */
10226          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10227          if (useadsi)
10228             adsi_folders(chan, 0, "Change to folder...");
10229 
10230          cmd = get_folder2(chan, "vm-changeto", 0);
10231          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10232          if (cmd == '#') {
10233             cmd = 0;
10234          } else if (cmd > 0) {
10235             cmd = cmd - '0';
10236             res = close_mailbox(&vms, vmu);
10237             if (res == ERROR_LOCK_PATH)
10238                goto out;
10239             /* If folder is not urgent, set in_urgent to zero! */
10240             if (cmd != 11) in_urgent = 0;
10241             res = open_mailbox(&vms, vmu, cmd);
10242             if (res < 0)
10243                goto out;
10244             play_folder = cmd;
10245             cmd = 0;
10246          }
10247          if (useadsi)
10248             adsi_status2(chan, &vms);
10249 
10250          if (!cmd) {
10251             cmd = vm_play_folder_name(chan, vms.vmbox);
10252          }
10253 
10254          vms.starting = 1;
10255          vms.curmsg = 0;
10256          break;
10257       case '3': /* Advanced options */
10258          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10259          cmd = 0;
10260          vms.repeats = 0;
10261          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10262             switch (cmd) {
10263             case '1': /* Reply */
10264                if (vms.lastmsg > -1 && !vms.starting) {
10265                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10266                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10267                      res = cmd;
10268                      goto out;
10269                   }
10270                } else {
10271                   cmd = ast_play_and_wait(chan, "vm-sorry");
10272                }
10273                cmd = 't';
10274                break;
10275             case '2': /* Callback */
10276                if (!vms.starting)
10277                   ast_verb(3, "Callback Requested\n");
10278                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10279                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10280                   if (cmd == 9) {
10281                      silentexit = 1;
10282                      goto out;
10283                   } else if (cmd == ERROR_LOCK_PATH) {
10284                      res = cmd;
10285                      goto out;
10286                   }
10287                } else {
10288                   cmd = ast_play_and_wait(chan, "vm-sorry");
10289                }
10290                cmd = 't';
10291                break;
10292             case '3': /* Envelope */
10293                if (vms.lastmsg > -1 && !vms.starting) {
10294                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10295                   if (cmd == ERROR_LOCK_PATH) {
10296                      res = cmd;
10297                      goto out;
10298                   }
10299                } else {
10300                   cmd = ast_play_and_wait(chan, "vm-sorry");
10301                }
10302                cmd = 't';
10303                break;
10304             case '4': /* Dialout */
10305                if (!ast_strlen_zero(vmu->dialout)) {
10306                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10307                   if (cmd == 9) {
10308                      silentexit = 1;
10309                      goto out;
10310                   }
10311                } else {
10312                   cmd = ast_play_and_wait(chan, "vm-sorry");
10313                }
10314                cmd = 't';
10315                break;
10316 
10317             case '5': /* Leave VoiceMail */
10318                if (ast_test_flag(vmu, VM_SVMAIL)) {
10319                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10320                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10321                      res = cmd;
10322                      goto out;
10323                   }
10324                } else {
10325                   cmd = ast_play_and_wait(chan, "vm-sorry");
10326                }
10327                cmd = 't';
10328                break;
10329 
10330             case '*': /* Return to main menu */
10331                cmd = 't';
10332                break;
10333 
10334             default:
10335                cmd = 0;
10336                if (!vms.starting) {
10337                   cmd = ast_play_and_wait(chan, "vm-toreply");
10338                }
10339                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10340                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10341                }
10342                if (!cmd && !vms.starting) {
10343                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10344                }
10345                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10346                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10347                }
10348                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10349                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10350                }
10351                if (!cmd) {
10352                   cmd = ast_play_and_wait(chan, "vm-starmain");
10353                }
10354                if (!cmd) {
10355                   cmd = ast_waitfordigit(chan, 6000);
10356                }
10357                if (!cmd) {
10358                   vms.repeats++;
10359                }
10360                if (vms.repeats > 3) {
10361                   cmd = 't';
10362                }
10363                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10364             }
10365          }
10366          if (cmd == 't') {
10367             cmd = 0;
10368             vms.repeats = 0;
10369          }
10370          break;
10371       case '4': /* Go to the previous message */
10372          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10373          if (vms.curmsg > 0) {
10374             vms.curmsg--;
10375             cmd = play_message(chan, vmu, &vms);
10376          } else {
10377             /* Check if we were listening to new
10378                messages.  If so, go to Urgent messages
10379                instead of saying "no more messages"
10380             */
10381             if (in_urgent == 0 && vms.urgentmessages > 0) {
10382                /* Check for Urgent messages */
10383                in_urgent = 1;
10384                res = close_mailbox(&vms, vmu);
10385                if (res == ERROR_LOCK_PATH)
10386                   goto out;
10387                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10388                if (res < 0)
10389                   goto out;
10390                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10391                vms.curmsg = vms.lastmsg;
10392                if (vms.lastmsg < 0) {
10393                   cmd = ast_play_and_wait(chan, "vm-nomore");
10394                }
10395             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10396                vms.curmsg = vms.lastmsg;
10397                cmd = play_message(chan, vmu, &vms);
10398             } else {
10399                cmd = ast_play_and_wait(chan, "vm-nomore");
10400             }
10401          }
10402          break;
10403       case '6': /* Go to the next message */
10404          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10405          if (vms.curmsg < vms.lastmsg) {
10406             vms.curmsg++;
10407             cmd = play_message(chan, vmu, &vms);
10408          } else {
10409             if (in_urgent && vms.newmessages > 0) {
10410                /* Check if we were listening to urgent
10411                 * messages.  If so, go to regular new messages
10412                 * instead of saying "no more messages"
10413                 */
10414                in_urgent = 0;
10415                res = close_mailbox(&vms, vmu);
10416                if (res == ERROR_LOCK_PATH)
10417                   goto out;
10418                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10419                if (res < 0)
10420                   goto out;
10421                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10422                vms.curmsg = -1;
10423                if (vms.lastmsg < 0) {
10424                   cmd = ast_play_and_wait(chan, "vm-nomore");
10425                }
10426             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10427                vms.curmsg = 0;
10428                cmd = play_message(chan, vmu, &vms);
10429             } else {
10430                cmd = ast_play_and_wait(chan, "vm-nomore");
10431             }
10432          }
10433          break;
10434       case '7': /* Delete the current message */
10435          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10436             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10437             if (useadsi)
10438                adsi_delete(chan, &vms);
10439             if (vms.deleted[vms.curmsg]) {
10440                if (play_folder == 0) {
10441                   if (in_urgent) {
10442                      vms.urgentmessages--;
10443                   } else {
10444                      vms.newmessages--;
10445                   }
10446                }
10447                else if (play_folder == 1)
10448                   vms.oldmessages--;
10449                cmd = ast_play_and_wait(chan, "vm-deleted");
10450             } else {
10451                if (play_folder == 0) {
10452                   if (in_urgent) {
10453                      vms.urgentmessages++;
10454                   } else {
10455                      vms.newmessages++;
10456                   }
10457                }
10458                else if (play_folder == 1)
10459                   vms.oldmessages++;
10460                cmd = ast_play_and_wait(chan, "vm-undeleted");
10461             }
10462             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10463                if (vms.curmsg < vms.lastmsg) {
10464                   vms.curmsg++;
10465                   cmd = play_message(chan, vmu, &vms);
10466                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10467                   vms.curmsg = 0;
10468                   cmd = play_message(chan, vmu, &vms);
10469                } else {
10470                   /* Check if we were listening to urgent
10471                      messages.  If so, go to regular new messages
10472                      instead of saying "no more messages"
10473                   */
10474                   if (in_urgent == 1) {
10475                      /* Check for new messages */
10476                      in_urgent = 0;
10477                      res = close_mailbox(&vms, vmu);
10478                      if (res == ERROR_LOCK_PATH)
10479                         goto out;
10480                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10481                      if (res < 0)
10482                         goto out;
10483                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10484                      vms.curmsg = -1;
10485                      if (vms.lastmsg < 0) {
10486                         cmd = ast_play_and_wait(chan, "vm-nomore");
10487                      }
10488                   } else {
10489                      cmd = ast_play_and_wait(chan, "vm-nomore");
10490                   }
10491                }
10492             }
10493          } else /* Delete not valid if we haven't selected a message */
10494             cmd = 0;
10495 #ifdef IMAP_STORAGE
10496          deleted = 1;
10497 #endif
10498          break;
10499    
10500       case '8': /* Forward the current message */
10501          if (vms.lastmsg > -1) {
10502             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10503             if (cmd == ERROR_LOCK_PATH) {
10504                res = cmd;
10505                goto out;
10506             }
10507          } else {
10508             /* Check if we were listening to urgent
10509                messages.  If so, go to regular new messages
10510                instead of saying "no more messages"
10511             */
10512             if (in_urgent == 1 && vms.newmessages > 0) {
10513                /* Check for new messages */
10514                in_urgent = 0;
10515                res = close_mailbox(&vms, vmu);
10516                if (res == ERROR_LOCK_PATH)
10517                   goto out;
10518                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10519                if (res < 0)
10520                   goto out;
10521                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10522                vms.curmsg = -1;
10523                if (vms.lastmsg < 0) {
10524                   cmd = ast_play_and_wait(chan, "vm-nomore");
10525                }
10526             } else {
10527                cmd = ast_play_and_wait(chan, "vm-nomore");
10528             }
10529          }
10530          break;
10531       case '9': /* Save message to folder */
10532          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10533          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10534             /* No message selected */
10535             cmd = 0;
10536             break;
10537          }
10538          if (useadsi)
10539             adsi_folders(chan, 1, "Save to folder...");
10540          cmd = get_folder2(chan, "vm-savefolder", 1);
10541          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10542          box = 0; /* Shut up compiler */
10543          if (cmd == '#') {
10544             cmd = 0;
10545             break;
10546          } else if (cmd > 0) {
10547             box = cmd = cmd - '0';
10548             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10549             if (cmd == ERROR_LOCK_PATH) {
10550                res = cmd;
10551                goto out;
10552 #ifndef IMAP_STORAGE
10553             } else if (!cmd) {
10554                vms.deleted[vms.curmsg] = 1;
10555 #endif
10556             } else {
10557                vms.deleted[vms.curmsg] = 0;
10558                vms.heard[vms.curmsg] = 0;
10559             }
10560          }
10561          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10562          if (useadsi)
10563             adsi_message(chan, &vms);
10564          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10565          if (!cmd) {
10566             cmd = ast_play_and_wait(chan, "vm-message");
10567             if (!cmd) 
10568                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10569             if (!cmd)
10570                cmd = ast_play_and_wait(chan, "vm-savedto");
10571             if (!cmd)
10572                cmd = vm_play_folder_name(chan, vms.fn);
10573          } else {
10574             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10575          }
10576          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10577             if (vms.curmsg < vms.lastmsg) {
10578                vms.curmsg++;
10579                cmd = play_message(chan, vmu, &vms);
10580             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10581                vms.curmsg = 0;
10582                cmd = play_message(chan, vmu, &vms);
10583             } else {
10584                /* Check if we were listening to urgent
10585                   messages.  If so, go to regular new messages
10586                   instead of saying "no more messages"
10587                */
10588                if (in_urgent == 1 && vms.newmessages > 0) {
10589                   /* Check for new messages */
10590                   in_urgent = 0;
10591                   res = close_mailbox(&vms, vmu);
10592                   if (res == ERROR_LOCK_PATH)
10593                      goto out;
10594                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10595                   if (res < 0)
10596                      goto out;
10597                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10598                   vms.curmsg = -1;
10599                   if (vms.lastmsg < 0) {
10600                      cmd = ast_play_and_wait(chan, "vm-nomore");
10601                   }
10602                } else {
10603                   cmd = ast_play_and_wait(chan, "vm-nomore");
10604                }
10605             }
10606          }
10607          break;
10608       case '*': /* Help */
10609          if (!vms.starting) {
10610             cmd = ast_play_and_wait(chan, "vm-onefor");
10611             if (!strncasecmp(chan->language, "he", 2)) {
10612                cmd = ast_play_and_wait(chan, "vm-for");
10613             }
10614             if (!cmd)
10615                cmd = vm_play_folder_name(chan, vms.vmbox);
10616             if (!cmd)
10617                cmd = ast_play_and_wait(chan, "vm-opts");
10618             if (!cmd)
10619                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10620          } else
10621             cmd = 0;
10622          break;
10623       case '0': /* Mailbox options */
10624          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10625          if (useadsi)
10626             adsi_status(chan, &vms);
10627          break;
10628       default: /* Nothing */
10629          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10630          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10631          break;
10632       }
10633    }
10634    if ((cmd == 't') || (cmd == '#')) {
10635       /* Timeout */
10636       res = 0;
10637    } else {
10638       /* Hangup */
10639       res = -1;
10640    }
10641 
10642 out:
10643    if (res > -1) {
10644       ast_stopstream(chan);
10645       adsi_goodbye(chan);
10646       if (valid && res != OPERATOR_EXIT) {
10647          if (silentexit)
10648             res = ast_play_and_wait(chan, "vm-dialout");
10649          else 
10650             res = ast_play_and_wait(chan, "vm-goodbye");
10651       }
10652       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10653          res = 0;
10654       }
10655       if (useadsi)
10656          ast_adsi_unload_session(chan);
10657    }
10658    if (vmu)
10659       close_mailbox(&vms, vmu);
10660    if (valid) {
10661       int new = 0, old = 0, urgent = 0;
10662       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10663       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10664       /* Urgent flag not passwd to externnotify here */
10665       run_externnotify(vmu->context, vmu->mailbox, NULL);
10666       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10667       queue_mwi_event(ext_context, urgent, new, old);
10668    }
10669 #ifdef IMAP_STORAGE
10670    /* expunge message - use UID Expunge if supported on IMAP server*/
10671    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10672    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10673       ast_mutex_lock(&vms.lock);
10674 #ifdef HAVE_IMAP_TK2006
10675       if (LEVELUIDPLUS (vms.mailstream)) {
10676          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10677       } else 
10678 #endif
10679          mail_expunge(vms.mailstream);
10680       ast_mutex_unlock(&vms.lock);
10681    }
10682    /*  before we delete the state, we should copy pertinent info
10683     *  back to the persistent model */
10684    if (vmu) {
10685       vmstate_delete(&vms);
10686    }
10687 #endif
10688    if (vmu)
10689       free_user(vmu);
10690 
10691 #ifdef IMAP_STORAGE
10692    pthread_setspecific(ts_vmstate.key, NULL);
10693 #endif
10694    return res;
10695 }
10696 
10697 static int vm_exec(struct ast_channel *chan, const char *data)
10698 {
10699    int res = 0;
10700    char *tmp;
10701    struct leave_vm_options leave_options;
10702    struct ast_flags flags = { 0 };
10703    char *opts[OPT_ARG_ARRAY_SIZE];
10704    AST_DECLARE_APP_ARGS(args,
10705       AST_APP_ARG(argv0);
10706       AST_APP_ARG(argv1);
10707    );
10708    
10709    memset(&leave_options, 0, sizeof(leave_options));
10710 
10711    if (chan->_state != AST_STATE_UP)
10712       ast_answer(chan);
10713 
10714    if (!ast_strlen_zero(data)) {
10715       tmp = ast_strdupa(data);
10716       AST_STANDARD_APP_ARGS(args, tmp);
10717       if (args.argc == 2) {
10718          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10719             return -1;
10720          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10721          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10722             int gain;
10723 
10724             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10725                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10726                return -1;
10727             } else {
10728                leave_options.record_gain = (signed char) gain;
10729             }
10730          }
10731          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10732             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10733                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10734          }
10735       }
10736    } else {
10737       char temp[256];
10738       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10739       if (res < 0)
10740          return res;
10741       if (ast_strlen_zero(temp))
10742          return 0;
10743       args.argv0 = ast_strdupa(temp);
10744    }
10745 
10746    res = leave_voicemail(chan, args.argv0, &leave_options);
10747    if (res == 't') {
10748       ast_play_and_wait(chan, "vm-goodbye");
10749       res = 0;
10750    }
10751 
10752    if (res == OPERATOR_EXIT) {
10753       res = 0;
10754    }
10755 
10756    if (res == ERROR_LOCK_PATH) {
10757       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10758       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10759       res = 0;
10760    }
10761 
10762    return res;
10763 }
10764 
10765 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10766 {
10767    struct ast_vm_user *vmu;
10768 
10769    if (!ast_strlen_zero(box) && box[0] == '*') {
10770       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10771             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10772             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10773             "\n\tand will be ignored.\n", box, context);
10774       return NULL;
10775    }
10776 
10777    AST_LIST_TRAVERSE(&users, vmu, list) {
10778       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10779          if (strcasecmp(vmu->context, context)) {
10780             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10781                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10782                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10783                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10784          }
10785          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10786          return NULL;
10787       }
10788       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10789          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10790          return NULL;
10791       }
10792    }
10793    
10794    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10795       return NULL;
10796    
10797    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10798    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10799 
10800    AST_LIST_INSERT_TAIL(&users, vmu, list);
10801    
10802    return vmu;
10803 }
10804 
10805 static int append_mailbox(const char *context, const char *box, const char *data)
10806 {
10807    /* Assumes lock is already held */
10808    char *tmp;
10809    char *stringp;
10810    char *s;
10811    struct ast_vm_user *vmu;
10812    char *mailbox_full;
10813    int new = 0, old = 0, urgent = 0;
10814    char secretfn[PATH_MAX] = "";
10815 
10816    tmp = ast_strdupa(data);
10817 
10818    if (!(vmu = find_or_create(context, box)))
10819       return -1;
10820 
10821    populate_defaults(vmu);
10822 
10823    stringp = tmp;
10824    if ((s = strsep(&stringp, ","))) {
10825       if (!ast_strlen_zero(s) && s[0] == '*') {
10826          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10827             "\n\tmust be reset in voicemail.conf.\n", box);
10828       }
10829       /* assign password regardless of validity to prevent NULL password from being assigned */
10830       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10831    }
10832    if (stringp && (s = strsep(&stringp, ","))) {
10833       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10834    }
10835    if (stringp && (s = strsep(&stringp, ","))) {
10836       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10837    }
10838    if (stringp && (s = strsep(&stringp, ","))) {
10839       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10840    }
10841    if (stringp && (s = strsep(&stringp, ","))) {
10842       apply_options(vmu, s);
10843    }
10844 
10845    switch (vmu->passwordlocation) {
10846    case OPT_PWLOC_SPOOLDIR:
10847       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10848       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10849    }
10850 
10851    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10852    strcpy(mailbox_full, box);
10853    strcat(mailbox_full, "@");
10854    strcat(mailbox_full, context);
10855 
10856    inboxcount2(mailbox_full, &urgent, &new, &old);
10857    queue_mwi_event(mailbox_full, urgent, new, old);
10858 
10859    return 0;
10860 }
10861 
10862 AST_TEST_DEFINE(test_voicemail_vmuser)
10863 {
10864    int res = 0;
10865    struct ast_vm_user *vmu;
10866    /* language parameter seems to only be used for display in manager action */
10867    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10868       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10869       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10870       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10871       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10872       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10873       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10874       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10875       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10876 #ifdef IMAP_STORAGE
10877    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10878       "imapfolder=INBOX|imapvmshareid=6000";
10879 #endif
10880 
10881    switch (cmd) {
10882    case TEST_INIT:
10883       info->name = "vmuser";
10884       info->category = "/apps/app_voicemail/";
10885       info->summary = "Vmuser unit test";
10886       info->description =
10887          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10888       return AST_TEST_NOT_RUN;
10889    case TEST_EXECUTE:
10890       break;
10891    }
10892 
10893    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10894       return AST_TEST_NOT_RUN;
10895    }
10896    populate_defaults(vmu);
10897    ast_set_flag(vmu, VM_ALLOCED);
10898 
10899    apply_options(vmu, options_string);
10900 
10901    if (!ast_test_flag(vmu, VM_ATTACH)) {
10902       ast_test_status_update(test, "Parse failure for attach option\n");
10903       res = 1;
10904    }
10905    if (strcasecmp(vmu->attachfmt, "wav49")) {
10906       ast_test_status_update(test, "Parse failure for attachftm option\n");
10907       res = 1;
10908    }
10909    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10910       ast_test_status_update(test, "Parse failure for serveremail option\n");
10911       res = 1;
10912    }
10913    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10914       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10915       res = 1;
10916    }
10917    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10918       ast_test_status_update(test, "Parse failure for emailbody option\n");
10919       res = 1;
10920    }
10921    if (strcasecmp(vmu->zonetag, "central")) {
10922       ast_test_status_update(test, "Parse failure for tz option\n");
10923       res = 1;
10924    }
10925    if (!ast_test_flag(vmu, VM_DELETE)) {
10926       ast_test_status_update(test, "Parse failure for delete option\n");
10927       res = 1;
10928    }
10929    if (!ast_test_flag(vmu, VM_SAYCID)) {
10930       ast_test_status_update(test, "Parse failure for saycid option\n");
10931       res = 1;
10932    }
10933    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10934       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10935       res = 1;
10936    }
10937    if (!ast_test_flag(vmu, VM_REVIEW)) {
10938       ast_test_status_update(test, "Parse failure for review option\n");
10939       res = 1;
10940    }
10941    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10942       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10943       res = 1;
10944    }
10945    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10946       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10947       res = 1;
10948    }
10949    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10950       ast_test_status_update(test, "Parse failure for operator option\n");
10951       res = 1;
10952    }
10953    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10954       ast_test_status_update(test, "Parse failure for envelope option\n");
10955       res = 1;
10956    }
10957    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10958       ast_test_status_update(test, "Parse failure for moveheard option\n");
10959       res = 1;
10960    }
10961    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10962       ast_test_status_update(test, "Parse failure for sayduration option\n");
10963       res = 1;
10964    }
10965    if (vmu->saydurationm != 5) {
10966       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10967       res = 1;
10968    }
10969    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10970       ast_test_status_update(test, "Parse failure for forcename option\n");
10971       res = 1;
10972    }
10973    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10974       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10975       res = 1;
10976    }
10977    if (strcasecmp(vmu->callback, "somecontext")) {
10978       ast_test_status_update(test, "Parse failure for callbacks option\n");
10979       res = 1;
10980    }
10981    if (strcasecmp(vmu->dialout, "somecontext2")) {
10982       ast_test_status_update(test, "Parse failure for dialout option\n");
10983       res = 1;
10984    }
10985    if (strcasecmp(vmu->exit, "somecontext3")) {
10986       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10987       res = 1;
10988    }
10989    if (vmu->minsecs != 10) {
10990       ast_test_status_update(test, "Parse failure for minsecs option\n");
10991       res = 1;
10992    }
10993    if (vmu->maxsecs != 100) {
10994       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10995       res = 1;
10996    }
10997    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10998       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10999       res = 1;
11000    }
11001    if (vmu->maxdeletedmsg != 50) {
11002       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
11003       res = 1;
11004    }
11005    if (vmu->volgain != 1.3) {
11006       ast_test_status_update(test, "Parse failure for volgain option\n");
11007       res = 1;
11008    }
11009    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
11010       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
11011       res = 1;
11012    }
11013 #ifdef IMAP_STORAGE
11014    apply_options(vmu, option_string2);
11015 
11016    if (strcasecmp(vmu->imapuser, "imapuser")) {
11017       ast_test_status_update(test, "Parse failure for imapuser option\n");
11018       res = 1;
11019    }
11020    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11021       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11022       res = 1;
11023    }
11024    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11025       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11026       res = 1;
11027    }
11028    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11029       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11030       res = 1;
11031    }
11032 #endif
11033 
11034    free_user(vmu);
11035    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11036 }
11037 
11038 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11039 {
11040    struct ast_vm_user svm;
11041    char *context, *box;
11042    AST_DECLARE_APP_ARGS(args,
11043       AST_APP_ARG(mbox);
11044       AST_APP_ARG(options);
11045    );
11046    static int dep_warning = 0;
11047 
11048    if (ast_strlen_zero(data)) {
11049       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11050       return -1;
11051    }
11052 
11053    if (!dep_warning) {
11054       dep_warning = 1;
11055       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11056    }
11057 
11058    box = ast_strdupa(data);
11059 
11060    AST_STANDARD_APP_ARGS(args, box);
11061 
11062    if (args.options) {
11063    }
11064 
11065    if ((context = strchr(args.mbox, '@'))) {
11066       *context = '\0';
11067       context++;
11068    }
11069 
11070    if (find_user(&svm, context, args.mbox)) {
11071       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11072    } else
11073       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11074 
11075    return 0;
11076 }
11077 
11078 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11079 {
11080    struct ast_vm_user svm;
11081    AST_DECLARE_APP_ARGS(arg,
11082       AST_APP_ARG(mbox);
11083       AST_APP_ARG(context);
11084    );
11085 
11086    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11087 
11088    if (ast_strlen_zero(arg.mbox)) {
11089       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11090       return -1;
11091    }
11092 
11093    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11094    return 0;
11095 }
11096 
11097 static struct ast_custom_function mailbox_exists_acf = {
11098    .name = "MAILBOX_EXISTS",
11099    .read = acf_mailbox_exists,
11100 };
11101 
11102 static int vmauthenticate(struct ast_channel *chan, const char *data)
11103 {
11104    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11105    struct ast_vm_user vmus;
11106    char *options = NULL;
11107    int silent = 0, skipuser = 0;
11108    int res = -1;
11109    
11110    if (data) {
11111       s = ast_strdupa(data);
11112       user = strsep(&s, ",");
11113       options = strsep(&s, ",");
11114       if (user) {
11115          s = user;
11116          user = strsep(&s, "@");
11117          context = strsep(&s, "");
11118          if (!ast_strlen_zero(user))
11119             skipuser++;
11120          ast_copy_string(mailbox, user, sizeof(mailbox));
11121       }
11122    }
11123 
11124    if (options) {
11125       silent = (strchr(options, 's')) != NULL;
11126    }
11127 
11128    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11129       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11130       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11131       ast_play_and_wait(chan, "auth-thankyou");
11132       res = 0;
11133    } else if (mailbox[0] == '*') {
11134       /* user entered '*' */
11135       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11136          res = 0; /* prevent hangup */
11137       }
11138    }
11139 
11140    return res;
11141 }
11142 
11143 static char *show_users_realtime(int fd, const char *context)
11144 {
11145    struct ast_config *cfg;
11146    const char *cat = NULL;
11147 
11148    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11149       "context", context, SENTINEL))) {
11150       return CLI_FAILURE;
11151    }
11152 
11153    ast_cli(fd,
11154       "\n"
11155       "=============================================================\n"
11156       "=== Configured Voicemail Users ==============================\n"
11157       "=============================================================\n"
11158       "===\n");
11159 
11160    while ((cat = ast_category_browse(cfg, cat))) {
11161       struct ast_variable *var = NULL;
11162       ast_cli(fd,
11163          "=== Mailbox ...\n"
11164          "===\n");
11165       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11166          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11167       ast_cli(fd,
11168          "===\n"
11169          "=== ---------------------------------------------------------\n"
11170          "===\n");
11171    }
11172 
11173    ast_cli(fd,
11174       "=============================================================\n"
11175       "\n");
11176 
11177    ast_config_destroy(cfg);
11178 
11179    return CLI_SUCCESS;
11180 }
11181 
11182 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11183 {
11184    int which = 0;
11185    int wordlen;
11186    struct ast_vm_user *vmu;
11187    const char *context = "";
11188 
11189    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11190    if (pos > 4)
11191       return NULL;
11192    if (pos == 3)
11193       return (state == 0) ? ast_strdup("for") : NULL;
11194    wordlen = strlen(word);
11195    AST_LIST_TRAVERSE(&users, vmu, list) {
11196       if (!strncasecmp(word, vmu->context, wordlen)) {
11197          if (context && strcmp(context, vmu->context) && ++which > state)
11198             return ast_strdup(vmu->context);
11199          /* ignore repeated contexts ? */
11200          context = vmu->context;
11201       }
11202    }
11203    return NULL;
11204 }
11205 
11206 /*! \brief Show a list of voicemail users in the CLI */
11207 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11208 {
11209    struct ast_vm_user *vmu;
11210 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11211    const char *context = NULL;
11212    int users_counter = 0;
11213 
11214    switch (cmd) {
11215    case CLI_INIT:
11216       e->command = "voicemail show users";
11217       e->usage =
11218          "Usage: voicemail show users [for <context>]\n"
11219          "       Lists all mailboxes currently set up\n";
11220       return NULL;
11221    case CLI_GENERATE:
11222       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11223    }  
11224 
11225    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11226       return CLI_SHOWUSAGE;
11227    if (a->argc == 5) {
11228       if (strcmp(a->argv[3],"for"))
11229          return CLI_SHOWUSAGE;
11230       context = a->argv[4];
11231    }
11232 
11233    if (ast_check_realtime("voicemail")) {
11234       if (!context) {
11235          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11236          return CLI_SHOWUSAGE;
11237       }
11238       return show_users_realtime(a->fd, context);
11239    }
11240 
11241    AST_LIST_LOCK(&users);
11242    if (AST_LIST_EMPTY(&users)) {
11243       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11244       AST_LIST_UNLOCK(&users);
11245       return CLI_FAILURE;
11246    }
11247    if (!context) {
11248       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11249    } else {
11250       int count = 0;
11251       AST_LIST_TRAVERSE(&users, vmu, list) {
11252          if (!strcmp(context, vmu->context)) {
11253             count++;
11254             break;
11255          }
11256       }
11257       if (count) {
11258          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11259       } else {
11260          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11261          AST_LIST_UNLOCK(&users);
11262          return CLI_FAILURE;
11263       }
11264    }
11265    AST_LIST_TRAVERSE(&users, vmu, list) {
11266       int newmsgs = 0, oldmsgs = 0;
11267       char count[12], tmp[256] = "";
11268 
11269       if (!context || !strcmp(context, vmu->context)) {
11270          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11271          inboxcount(tmp, &newmsgs, &oldmsgs);
11272          snprintf(count, sizeof(count), "%d", newmsgs);
11273          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11274          users_counter++;
11275       }
11276    }
11277    AST_LIST_UNLOCK(&users);
11278    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11279    return CLI_SUCCESS;
11280 }
11281 
11282 /*! \brief Show a list of voicemail zones in the CLI */
11283 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11284 {
11285    struct vm_zone *zone;
11286 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11287    char *res = CLI_SUCCESS;
11288 
11289    switch (cmd) {
11290    case CLI_INIT:
11291       e->command = "voicemail show zones";
11292       e->usage =
11293          "Usage: voicemail show zones\n"
11294          "       Lists zone message formats\n";
11295       return NULL;
11296    case CLI_GENERATE:
11297       return NULL;
11298    }
11299 
11300    if (a->argc != 3)
11301       return CLI_SHOWUSAGE;
11302 
11303    AST_LIST_LOCK(&zones);
11304    if (!AST_LIST_EMPTY(&zones)) {
11305       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11306       AST_LIST_TRAVERSE(&zones, zone, list) {
11307          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11308       }
11309    } else {
11310       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11311       res = CLI_FAILURE;
11312    }
11313    AST_LIST_UNLOCK(&zones);
11314 
11315    return res;
11316 }
11317 
11318 /*! \brief Reload voicemail configuration from the CLI */
11319 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11320 {
11321    switch (cmd) {
11322    case CLI_INIT:
11323       e->command = "voicemail reload";
11324       e->usage =
11325          "Usage: voicemail reload\n"
11326          "       Reload voicemail configuration\n";
11327       return NULL;
11328    case CLI_GENERATE:
11329       return NULL;
11330    }
11331 
11332    if (a->argc != 2)
11333       return CLI_SHOWUSAGE;
11334 
11335    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11336    load_config(1);
11337    
11338    return CLI_SUCCESS;
11339 }
11340 
11341 static struct ast_cli_entry cli_voicemail[] = {
11342    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11343    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11344    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11345 };
11346 
11347 #ifdef IMAP_STORAGE
11348    #define DATA_EXPORT_VM_USERS(USER)              \
11349       USER(ast_vm_user, context, AST_DATA_STRING)        \
11350       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11351       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11352       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11353       USER(ast_vm_user, email, AST_DATA_STRING)       \
11354       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11355       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11356       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11357       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11358       USER(ast_vm_user, language, AST_DATA_STRING)       \
11359       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11360       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11361       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11362       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11363       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11364       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11365       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11366       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11367       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11368       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11369       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11370       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11371       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11372       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11373       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11374 #else
11375    #define DATA_EXPORT_VM_USERS(USER)              \
11376       USER(ast_vm_user, context, AST_DATA_STRING)        \
11377       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11378       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11379       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11380       USER(ast_vm_user, email, AST_DATA_STRING)       \
11381       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11382       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11383       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11384       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11385       USER(ast_vm_user, language, AST_DATA_STRING)       \
11386       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11387       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11388       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11389       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11390       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11391       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11392       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11393       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11394       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11395       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11396       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11397       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11398 #endif
11399 
11400 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11401 
11402 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11403    ZONE(vm_zone, name, AST_DATA_STRING)      \
11404    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11405    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11406 
11407 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11408 
11409 /*!
11410  * \internal
11411  * \brief Add voicemail user to the data_root.
11412  * \param[in] search The search tree.
11413  * \param[in] data_root The main result node.
11414  * \param[in] user The voicemail user.
11415  */
11416 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11417     struct ast_data *data_root, struct ast_vm_user *user)
11418 {
11419    struct ast_data *data_user, *data_zone;
11420    struct ast_data *data_state;
11421    struct vm_zone *zone = NULL;
11422    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11423    char ext_context[256] = "";
11424 
11425    data_user = ast_data_add_node(data_root, "user");
11426    if (!data_user) {
11427       return -1;
11428    }
11429 
11430    ast_data_add_structure(ast_vm_user, data_user, user);
11431 
11432    AST_LIST_LOCK(&zones);
11433    AST_LIST_TRAVERSE(&zones, zone, list) {
11434       if (!strcmp(zone->name, user->zonetag)) {
11435          break;
11436       }
11437    }
11438    AST_LIST_UNLOCK(&zones);
11439 
11440    /* state */
11441    data_state = ast_data_add_node(data_user, "state");
11442    if (!data_state) {
11443       return -1;
11444    }
11445    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11446    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11447    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11448    ast_data_add_int(data_state, "newmsg", newmsg);
11449    ast_data_add_int(data_state, "oldmsg", oldmsg);
11450 
11451    if (zone) {
11452       data_zone = ast_data_add_node(data_user, "zone");
11453       ast_data_add_structure(vm_zone, data_zone, zone);
11454    }
11455 
11456    if (!ast_data_search_match(search, data_user)) {
11457       ast_data_remove_node(data_root, data_user);
11458    }
11459 
11460    return 0;
11461 }
11462 
11463 static int vm_users_data_provider_get(const struct ast_data_search *search,
11464    struct ast_data *data_root)
11465 {
11466    struct ast_vm_user *user;
11467 
11468    AST_LIST_LOCK(&users);
11469    AST_LIST_TRAVERSE(&users, user, list) {
11470       vm_users_data_provider_get_helper(search, data_root, user);
11471    }
11472    AST_LIST_UNLOCK(&users);
11473 
11474    return 0;
11475 }
11476 
11477 static const struct ast_data_handler vm_users_data_provider = {
11478    .version = AST_DATA_HANDLER_VERSION,
11479    .get = vm_users_data_provider_get
11480 };
11481 
11482 static const struct ast_data_entry vm_data_providers[] = {
11483    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11484 };
11485 
11486 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11487 {
11488    int new = 0, old = 0, urgent = 0;
11489 
11490    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11491 
11492    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11493       mwi_sub->old_urgent = urgent;
11494       mwi_sub->old_new = new;
11495       mwi_sub->old_old = old;
11496       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11497       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11498    }
11499 }
11500 
11501 static void poll_subscribed_mailboxes(void)
11502 {
11503    struct mwi_sub *mwi_sub;
11504 
11505    AST_RWLIST_RDLOCK(&mwi_subs);
11506    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11507       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11508          poll_subscribed_mailbox(mwi_sub);
11509       }
11510    }
11511    AST_RWLIST_UNLOCK(&mwi_subs);
11512 }
11513 
11514 static void *mb_poll_thread(void *data)
11515 {
11516    while (poll_thread_run) {
11517       struct timespec ts = { 0, };
11518       struct timeval wait;
11519 
11520       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11521       ts.tv_sec = wait.tv_sec;
11522       ts.tv_nsec = wait.tv_usec * 1000;
11523 
11524       ast_mutex_lock(&poll_lock);
11525       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11526       ast_mutex_unlock(&poll_lock);
11527 
11528       if (!poll_thread_run)
11529          break;
11530 
11531       poll_subscribed_mailboxes();
11532    }
11533 
11534    return NULL;
11535 }
11536 
11537 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11538 {
11539    ast_free(mwi_sub);
11540 }
11541 
11542 static int handle_unsubscribe(void *datap)
11543 {
11544    struct mwi_sub *mwi_sub;
11545    uint32_t *uniqueid = datap;
11546    
11547    AST_RWLIST_WRLOCK(&mwi_subs);
11548    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11549       if (mwi_sub->uniqueid == *uniqueid) {
11550          AST_LIST_REMOVE_CURRENT(entry);
11551          break;
11552       }
11553    }
11554    AST_RWLIST_TRAVERSE_SAFE_END
11555    AST_RWLIST_UNLOCK(&mwi_subs);
11556 
11557    if (mwi_sub)
11558       mwi_sub_destroy(mwi_sub);
11559 
11560    ast_free(uniqueid);  
11561    return 0;
11562 }
11563 
11564 static int handle_subscribe(void *datap)
11565 {
11566    unsigned int len;
11567    struct mwi_sub *mwi_sub;
11568    struct mwi_sub_task *p = datap;
11569 
11570    len = sizeof(*mwi_sub);
11571    if (!ast_strlen_zero(p->mailbox))
11572       len += strlen(p->mailbox);
11573 
11574    if (!ast_strlen_zero(p->context))
11575       len += strlen(p->context) + 1; /* Allow for seperator */
11576 
11577    if (!(mwi_sub = ast_calloc(1, len)))
11578       return -1;
11579 
11580    mwi_sub->uniqueid = p->uniqueid;
11581    if (!ast_strlen_zero(p->mailbox))
11582       strcpy(mwi_sub->mailbox, p->mailbox);
11583 
11584    if (!ast_strlen_zero(p->context)) {
11585       strcat(mwi_sub->mailbox, "@");
11586       strcat(mwi_sub->mailbox, p->context);
11587    }
11588 
11589    AST_RWLIST_WRLOCK(&mwi_subs);
11590    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11591    AST_RWLIST_UNLOCK(&mwi_subs);
11592    ast_free((void *) p->mailbox);
11593    ast_free((void *) p->context);
11594    ast_free(p);
11595    poll_subscribed_mailbox(mwi_sub);
11596    return 0;
11597 }
11598 
11599 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11600 {
11601    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11602 
11603    if (!uniqueid) {
11604       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11605       return;
11606    }
11607 
11608    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11609       ast_free(uniqueid);
11610       return;
11611    }
11612 
11613    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11614       ast_free(uniqueid);
11615       return;
11616    }
11617 
11618    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11619    *uniqueid = u;
11620    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11621       ast_free(uniqueid);
11622    }
11623 }
11624 
11625 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11626 {
11627    struct mwi_sub_task *mwist;
11628    
11629    if (ast_event_get_type(event) != AST_EVENT_SUB)
11630       return;
11631 
11632    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11633       return;
11634 
11635    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11636       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11637       return;
11638    }
11639    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11640    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11641    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11642    
11643    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11644       ast_free(mwist);
11645    }
11646 }
11647 
11648 static void start_poll_thread(void)
11649 {
11650    int errcode;
11651    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11652       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11653       AST_EVENT_IE_END);
11654 
11655    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11656       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11657       AST_EVENT_IE_END);
11658 
11659    if (mwi_sub_sub)
11660       ast_event_report_subs(mwi_sub_sub);
11661 
11662    poll_thread_run = 1;
11663 
11664    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11665       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11666    }
11667 }
11668 
11669 static void stop_poll_thread(void)
11670 {
11671    poll_thread_run = 0;
11672 
11673    if (mwi_sub_sub) {
11674       ast_event_unsubscribe(mwi_sub_sub);
11675       mwi_sub_sub = NULL;
11676    }
11677 
11678    if (mwi_unsub_sub) {
11679       ast_event_unsubscribe(mwi_unsub_sub);
11680       mwi_unsub_sub = NULL;
11681    }
11682 
11683    ast_mutex_lock(&poll_lock);
11684    ast_cond_signal(&poll_cond);
11685    ast_mutex_unlock(&poll_lock);
11686 
11687    pthread_join(poll_thread, NULL);
11688 
11689    poll_thread = AST_PTHREADT_NULL;
11690 }
11691 
11692 /*! \brief Manager list voicemail users command */
11693 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11694 {
11695    struct ast_vm_user *vmu = NULL;
11696    const char *id = astman_get_header(m, "ActionID");
11697    char actionid[128] = "";
11698 
11699    if (!ast_strlen_zero(id))
11700       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11701 
11702    AST_LIST_LOCK(&users);
11703 
11704    if (AST_LIST_EMPTY(&users)) {
11705       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11706       AST_LIST_UNLOCK(&users);
11707       return RESULT_SUCCESS;
11708    }
11709    
11710    astman_send_ack(s, m, "Voicemail user list will follow");
11711    
11712    AST_LIST_TRAVERSE(&users, vmu, list) {
11713       char dirname[256];
11714 
11715 #ifdef IMAP_STORAGE
11716       int new, old;
11717       inboxcount(vmu->mailbox, &new, &old);
11718 #endif
11719       
11720       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11721       astman_append(s,
11722          "%s"
11723          "Event: VoicemailUserEntry\r\n"
11724          "VMContext: %s\r\n"
11725          "VoiceMailbox: %s\r\n"
11726          "Fullname: %s\r\n"
11727          "Email: %s\r\n"
11728          "Pager: %s\r\n"
11729          "ServerEmail: %s\r\n"
11730          "MailCommand: %s\r\n"
11731          "Language: %s\r\n"
11732          "TimeZone: %s\r\n"
11733          "Callback: %s\r\n"
11734          "Dialout: %s\r\n"
11735          "UniqueID: %s\r\n"
11736          "ExitContext: %s\r\n"
11737          "SayDurationMinimum: %d\r\n"
11738          "SayEnvelope: %s\r\n"
11739          "SayCID: %s\r\n"
11740          "AttachMessage: %s\r\n"
11741          "AttachmentFormat: %s\r\n"
11742          "DeleteMessage: %s\r\n"
11743          "VolumeGain: %.2f\r\n"
11744          "CanReview: %s\r\n"
11745          "CallOperator: %s\r\n"
11746          "MaxMessageCount: %d\r\n"
11747          "MaxMessageLength: %d\r\n"
11748          "NewMessageCount: %d\r\n"
11749 #ifdef IMAP_STORAGE
11750          "OldMessageCount: %d\r\n"
11751          "IMAPUser: %s\r\n"
11752 #endif
11753          "\r\n",
11754          actionid,
11755          vmu->context,
11756          vmu->mailbox,
11757          vmu->fullname,
11758          vmu->email,
11759          vmu->pager,
11760          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11761          mailcmd,
11762          vmu->language,
11763          vmu->zonetag,
11764          vmu->callback,
11765          vmu->dialout,
11766          vmu->uniqueid,
11767          vmu->exit,
11768          vmu->saydurationm,
11769          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11770          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11771          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11772          vmu->attachfmt,
11773          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11774          vmu->volgain,
11775          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11776          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11777          vmu->maxmsg,
11778          vmu->maxsecs,
11779 #ifdef IMAP_STORAGE
11780          new, old, vmu->imapuser
11781 #else
11782          count_messages(vmu, dirname)
11783 #endif
11784          );
11785    }     
11786    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11787 
11788    AST_LIST_UNLOCK(&users);
11789 
11790    return RESULT_SUCCESS;
11791 }
11792 
11793 /*! \brief Free the users structure. */
11794 static void free_vm_users(void) 
11795 {
11796    struct ast_vm_user *current;
11797    AST_LIST_LOCK(&users);
11798    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11799       ast_set_flag(current, VM_ALLOCED);
11800       free_user(current);
11801    }
11802    AST_LIST_UNLOCK(&users);
11803 }
11804 
11805 /*! \brief Free the zones structure. */
11806 static void free_vm_zones(void)
11807 {
11808    struct vm_zone *zcur;
11809    AST_LIST_LOCK(&zones);
11810    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11811       free_zone(zcur);
11812    AST_LIST_UNLOCK(&zones);
11813 }
11814 
11815 static const char *substitute_escapes(const char *value)
11816 {
11817    char *current;
11818 
11819    /* Add 16 for fudge factor */
11820    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11821 
11822    ast_str_reset(str);
11823    
11824    /* Substitute strings \r, \n, and \t into the appropriate characters */
11825    for (current = (char *) value; *current; current++) {
11826       if (*current == '\\') {
11827          current++;
11828          if (!*current) {
11829             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11830             break;
11831          }
11832          switch (*current) {
11833          case '\\':
11834             ast_str_append(&str, 0, "\\");
11835             break;
11836          case 'r':
11837             ast_str_append(&str, 0, "\r");
11838             break;
11839          case 'n':
11840 #ifdef IMAP_STORAGE
11841             if (!str->used || str->str[str->used - 1] != '\r') {
11842                ast_str_append(&str, 0, "\r");
11843             }
11844 #endif
11845             ast_str_append(&str, 0, "\n");
11846             break;
11847          case 't':
11848             ast_str_append(&str, 0, "\t");
11849             break;
11850          default:
11851             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11852             break;
11853          }
11854       } else {
11855          ast_str_append(&str, 0, "%c", *current);
11856       }
11857    }
11858 
11859    return ast_str_buffer(str);
11860 }
11861 
11862 static int load_config(int reload)
11863 {
11864    struct ast_config *cfg, *ucfg;
11865    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11866    int res;
11867 
11868    ast_unload_realtime("voicemail");
11869    ast_unload_realtime("voicemail_data");
11870 
11871    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11872       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11873          return 0;
11874       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11875          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11876          ucfg = NULL;
11877       }
11878       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11879       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11880          ast_config_destroy(ucfg);
11881          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11882          return 0;
11883       }
11884    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11885       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11886       return 0;
11887    } else {
11888       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11889       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11890          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11891          ucfg = NULL;
11892       }
11893    }
11894 
11895    res = actual_load_config(reload, cfg, ucfg);
11896 
11897    ast_config_destroy(cfg);
11898    ast_config_destroy(ucfg);
11899 
11900    return res;
11901 }
11902 
11903 #ifdef TEST_FRAMEWORK
11904 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11905 {
11906    ast_unload_realtime("voicemail");
11907    ast_unload_realtime("voicemail_data");
11908    return actual_load_config(reload, cfg, ucfg);
11909 }
11910 #endif
11911 
11912 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11913 {
11914    struct ast_vm_user *current;
11915    char *cat;
11916    struct ast_variable *var;
11917    const char *val;
11918    char *q, *stringp, *tmp;
11919    int x;
11920    unsigned int tmpadsi[4];
11921    char secretfn[PATH_MAX] = "";
11922 
11923 #ifdef IMAP_STORAGE
11924    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11925 #endif
11926    /* set audio control prompts */
11927    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11928    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11929    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11930    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11931    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11932 
11933    /* Free all the users structure */  
11934    free_vm_users();
11935 
11936    /* Free all the zones structure */
11937    free_vm_zones();
11938 
11939    AST_LIST_LOCK(&users);  
11940 
11941    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11942    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11943 
11944    if (cfg) {
11945       /* General settings */
11946 
11947       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11948          val = "default";
11949       ast_copy_string(userscontext, val, sizeof(userscontext));
11950       /* Attach voice message to mail message ? */
11951       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11952          val = "yes";
11953       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11954 
11955       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11956          val = "no";
11957       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11958 
11959       volgain = 0.0;
11960       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11961          sscanf(val, "%30lf", &volgain);
11962 
11963 #ifdef ODBC_STORAGE
11964       strcpy(odbc_database, "asterisk");
11965       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11966          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11967       }
11968       strcpy(odbc_table, "voicemessages");
11969       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11970          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11971       }
11972 #endif      
11973       /* Mail command */
11974       strcpy(mailcmd, SENDMAIL);
11975       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11976          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11977 
11978       maxsilence = 0;
11979       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11980          maxsilence = atoi(val);
11981          if (maxsilence > 0)
11982             maxsilence *= 1000;
11983       }
11984       
11985       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11986          maxmsg = MAXMSG;
11987       } else {
11988          maxmsg = atoi(val);
11989          if (maxmsg < 0) {
11990             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11991             maxmsg = MAXMSG;
11992          } else if (maxmsg > MAXMSGLIMIT) {
11993             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11994             maxmsg = MAXMSGLIMIT;
11995          }
11996       }
11997 
11998       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11999          maxdeletedmsg = 0;
12000       } else {
12001          if (sscanf(val, "%30d", &x) == 1)
12002             maxdeletedmsg = x;
12003          else if (ast_true(val))
12004             maxdeletedmsg = MAXMSG;
12005          else
12006             maxdeletedmsg = 0;
12007 
12008          if (maxdeletedmsg < 0) {
12009             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
12010             maxdeletedmsg = MAXMSG;
12011          } else if (maxdeletedmsg > MAXMSGLIMIT) {
12012             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12013             maxdeletedmsg = MAXMSGLIMIT;
12014          }
12015       }
12016 
12017       /* Load date format config for voicemail mail */
12018       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
12019          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
12020       }
12021 
12022       /* Load date format config for voicemail pager mail */
12023       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12024          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12025       }
12026 
12027       /* External password changing command */
12028       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12029          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12030          pwdchange = PWDCHANGE_EXTERNAL;
12031       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12032          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12033          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12034       }
12035  
12036       /* External password validation command */
12037       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12038          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12039          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12040       }
12041 
12042 #ifdef IMAP_STORAGE
12043       /* IMAP server address */
12044       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12045          ast_copy_string(imapserver, val, sizeof(imapserver));
12046       } else {
12047          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12048       }
12049       /* IMAP server port */
12050       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12051          ast_copy_string(imapport, val, sizeof(imapport));
12052       } else {
12053          ast_copy_string(imapport, "143", sizeof(imapport));
12054       }
12055       /* IMAP server flags */
12056       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12057          ast_copy_string(imapflags, val, sizeof(imapflags));
12058       }
12059       /* IMAP server master username */
12060       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12061          ast_copy_string(authuser, val, sizeof(authuser));
12062       }
12063       /* IMAP server master password */
12064       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12065          ast_copy_string(authpassword, val, sizeof(authpassword));
12066       }
12067       /* Expunge on exit */
12068       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12069          if (ast_false(val))
12070             expungeonhangup = 0;
12071          else
12072             expungeonhangup = 1;
12073       } else {
12074          expungeonhangup = 1;
12075       }
12076       /* IMAP voicemail folder */
12077       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12078          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12079       } else {
12080          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12081       }
12082       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12083          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12084       }
12085       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12086          imapgreetings = ast_true(val);
12087       } else {
12088          imapgreetings = 0;
12089       }
12090       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12091          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12092       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12093          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12094          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12095       } else {
12096          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12097       }
12098 
12099       /* There is some very unorthodox casting done here. This is due
12100        * to the way c-client handles the argument passed in. It expects a 
12101        * void pointer and casts the pointer directly to a long without
12102        * first dereferencing it. */
12103       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12104          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12105       } else {
12106          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12107       }
12108 
12109       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12110          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12111       } else {
12112          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12113       }
12114 
12115       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12116          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12117       } else {
12118          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12119       }
12120 
12121       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12122          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12123       } else {
12124          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12125       }
12126 
12127       /* Increment configuration version */
12128       imapversion++;
12129 #endif
12130       /* External voicemail notify application */
12131       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12132          ast_copy_string(externnotify, val, sizeof(externnotify));
12133          ast_debug(1, "found externnotify: %s\n", externnotify);
12134       } else {
12135          externnotify[0] = '\0';
12136       }
12137 
12138       /* SMDI voicemail notification */
12139       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12140          ast_debug(1, "Enabled SMDI voicemail notification\n");
12141          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12142             smdi_iface = ast_smdi_interface_find(val);
12143          } else {
12144             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12145             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12146          }
12147          if (!smdi_iface) {
12148             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12149          } 
12150       }
12151 
12152       /* Silence treshold */
12153       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12154       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12155          silencethreshold = atoi(val);
12156       
12157       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12158          val = ASTERISK_USERNAME;
12159       ast_copy_string(serveremail, val, sizeof(serveremail));
12160       
12161       vmmaxsecs = 0;
12162       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12163          if (sscanf(val, "%30d", &x) == 1) {
12164             vmmaxsecs = x;
12165          } else {
12166             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12167          }
12168       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12169          static int maxmessage_deprecate = 0;
12170          if (maxmessage_deprecate == 0) {
12171             maxmessage_deprecate = 1;
12172             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12173          }
12174          if (sscanf(val, "%30d", &x) == 1) {
12175             vmmaxsecs = x;
12176          } else {
12177             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12178          }
12179       }
12180 
12181       vmminsecs = 0;
12182       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12183          if (sscanf(val, "%30d", &x) == 1) {
12184             vmminsecs = x;
12185             if (maxsilence / 1000 >= vmminsecs) {
12186                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12187             }
12188          } else {
12189             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12190          }
12191       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12192          static int maxmessage_deprecate = 0;
12193          if (maxmessage_deprecate == 0) {
12194             maxmessage_deprecate = 1;
12195             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12196          }
12197          if (sscanf(val, "%30d", &x) == 1) {
12198             vmminsecs = x;
12199             if (maxsilence / 1000 >= vmminsecs) {
12200                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12201             }
12202          } else {
12203             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12204          }
12205       }
12206 
12207       val = ast_variable_retrieve(cfg, "general", "format");
12208       if (!val) {
12209          val = "wav";   
12210       } else {
12211          tmp = ast_strdupa(val);
12212          val = ast_format_str_reduce(tmp);
12213          if (!val) {
12214             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12215             val = "wav";
12216          }
12217       }
12218       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12219 
12220       skipms = 3000;
12221       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12222          if (sscanf(val, "%30d", &x) == 1) {
12223             maxgreet = x;
12224          } else {
12225             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12226          }
12227       }
12228 
12229       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12230          if (sscanf(val, "%30d", &x) == 1) {
12231             skipms = x;
12232          } else {
12233             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12234          }
12235       }
12236 
12237       maxlogins = 3;
12238       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12239          if (sscanf(val, "%30d", &x) == 1) {
12240             maxlogins = x;
12241          } else {
12242             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12243          }
12244       }
12245 
12246       minpassword = MINPASSWORD;
12247       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12248          if (sscanf(val, "%30d", &x) == 1) {
12249             minpassword = x;
12250          } else {
12251             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12252          }
12253       }
12254 
12255       /* Force new user to record name ? */
12256       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12257          val = "no";
12258       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12259 
12260       /* Force new user to record greetings ? */
12261       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12262          val = "no";
12263       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12264 
12265       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12266          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12267          stringp = ast_strdupa(val);
12268          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12269             if (!ast_strlen_zero(stringp)) {
12270                q = strsep(&stringp, ",");
12271                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12272                   q++;
12273                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12274                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12275             } else {
12276                cidinternalcontexts[x][0] = '\0';
12277             }
12278          }
12279       }
12280       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12281          ast_debug(1, "VM Review Option disabled globally\n");
12282          val = "no";
12283       }
12284       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12285 
12286       /* Temporary greeting reminder */
12287       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12288          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12289          val = "no";
12290       } else {
12291          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12292       }
12293       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12294       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12295          ast_debug(1, "VM next message wrap disabled globally\n");
12296          val = "no";
12297       }
12298       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12299 
12300       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12301          ast_debug(1, "VM Operator break disabled globally\n");
12302          val = "no";
12303       }
12304       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12305 
12306       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12307          ast_debug(1, "VM CID Info before msg disabled globally\n");
12308          val = "no";
12309       } 
12310       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12311 
12312       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12313          ast_debug(1, "Send Voicemail msg disabled globally\n");
12314          val = "no";
12315       }
12316       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12317    
12318       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12319          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12320          val = "yes";
12321       }
12322       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12323 
12324       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12325          ast_debug(1, "Move Heard enabled globally\n");
12326          val = "yes";
12327       }
12328       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12329 
12330       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12331          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12332          val = "no";
12333       }
12334       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12335 
12336       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12337          ast_debug(1, "Duration info before msg enabled globally\n");
12338          val = "yes";
12339       }
12340       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12341 
12342       saydurationminfo = 2;
12343       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12344          if (sscanf(val, "%30d", &x) == 1) {
12345             saydurationminfo = x;
12346          } else {
12347             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12348          }
12349       }
12350 
12351       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12352          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12353          val = "no";
12354       }
12355       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12356 
12357       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12358          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12359          ast_debug(1, "found dialout context: %s\n", dialcontext);
12360       } else {
12361          dialcontext[0] = '\0';  
12362       }
12363       
12364       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12365          ast_copy_string(callcontext, val, sizeof(callcontext));
12366          ast_debug(1, "found callback context: %s\n", callcontext);
12367       } else {
12368          callcontext[0] = '\0';
12369       }
12370 
12371       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12372          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12373          ast_debug(1, "found operator context: %s\n", exitcontext);
12374       } else {
12375          exitcontext[0] = '\0';
12376       }
12377       
12378       /* load password sounds configuration */
12379       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12380          ast_copy_string(vm_password, val, sizeof(vm_password));
12381       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12382          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12383       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12384          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12385       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12386          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12387       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12388          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12389       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12390          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12391       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12392          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12393       }
12394       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12395          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12396       }
12397       /* load configurable audio prompts */
12398       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12399          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12400       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12401          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12402       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12403          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12404       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12405          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12406       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12407          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12408 
12409       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12410          val = "no";
12411       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12412 
12413       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12414          val = "voicemail.conf";
12415       }
12416       if (!(strcmp(val, "spooldir"))) {
12417          passwordlocation = OPT_PWLOC_SPOOLDIR;
12418       } else {
12419          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12420       }
12421 
12422       poll_freq = DEFAULT_POLL_FREQ;
12423       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12424          if (sscanf(val, "%30u", &poll_freq) != 1) {
12425             poll_freq = DEFAULT_POLL_FREQ;
12426             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12427          }
12428       }
12429 
12430       poll_mailboxes = 0;
12431       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12432          poll_mailboxes = ast_true(val);
12433 
12434       memset(fromstring, 0, sizeof(fromstring));
12435       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12436       strcpy(charset, "ISO-8859-1");
12437       if (emailbody) {
12438          ast_free(emailbody);
12439          emailbody = NULL;
12440       }
12441       if (emailsubject) {
12442          ast_free(emailsubject);
12443          emailsubject = NULL;
12444       }
12445       if (pagerbody) {
12446          ast_free(pagerbody);
12447          pagerbody = NULL;
12448       }
12449       if (pagersubject) {
12450          ast_free(pagersubject);
12451          pagersubject = NULL;
12452       }
12453       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12454          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12455       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12456          ast_copy_string(fromstring, val, sizeof(fromstring));
12457       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12458          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12459       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12460          ast_copy_string(charset, val, sizeof(charset));
12461       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12462          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12463          for (x = 0; x < 4; x++) {
12464             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12465          }
12466       }
12467       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12468          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12469          for (x = 0; x < 4; x++) {
12470             memcpy(&adsisec[x], &tmpadsi[x], 1);
12471          }
12472       }
12473       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12474          if (atoi(val)) {
12475             adsiver = atoi(val);
12476          }
12477       }
12478       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12479          ast_copy_string(zonetag, val, sizeof(zonetag));
12480       }
12481       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12482          ast_copy_string(locale, val, sizeof(locale));
12483       }
12484       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12485          emailsubject = ast_strdup(substitute_escapes(val));
12486       }
12487       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12488          emailbody = ast_strdup(substitute_escapes(val));
12489       }
12490       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12491          pagersubject = ast_strdup(substitute_escapes(val));
12492       }
12493       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12494          pagerbody = ast_strdup(substitute_escapes(val));
12495       }
12496 
12497       /* load mailboxes from users.conf */
12498       if (ucfg) { 
12499          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12500             if (!strcasecmp(cat, "general")) {
12501                continue;
12502             }
12503             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12504                continue;
12505             if ((current = find_or_create(userscontext, cat))) {
12506                populate_defaults(current);
12507                apply_options_full(current, ast_variable_browse(ucfg, cat));
12508                ast_copy_string(current->context, userscontext, sizeof(current->context));
12509                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12510                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12511                }
12512 
12513                switch (current->passwordlocation) {
12514                case OPT_PWLOC_SPOOLDIR:
12515                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12516                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12517                }
12518             }
12519          }
12520       }
12521 
12522       /* load mailboxes from voicemail.conf */
12523       cat = ast_category_browse(cfg, NULL);
12524       while (cat) {
12525          if (strcasecmp(cat, "general")) {
12526             var = ast_variable_browse(cfg, cat);
12527             if (strcasecmp(cat, "zonemessages")) {
12528                /* Process mailboxes in this context */
12529                while (var) {
12530                   append_mailbox(cat, var->name, var->value);
12531                   var = var->next;
12532                }
12533             } else {
12534                /* Timezones in this context */
12535                while (var) {
12536                   struct vm_zone *z;
12537                   if ((z = ast_malloc(sizeof(*z)))) {
12538                      char *msg_format, *tzone;
12539                      msg_format = ast_strdupa(var->value);
12540                      tzone = strsep(&msg_format, "|,");
12541                      if (msg_format) {
12542                         ast_copy_string(z->name, var->name, sizeof(z->name));
12543                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12544                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12545                         AST_LIST_LOCK(&zones);
12546                         AST_LIST_INSERT_HEAD(&zones, z, list);
12547                         AST_LIST_UNLOCK(&zones);
12548                      } else {
12549                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12550                         ast_free(z);
12551                      }
12552                   } else {
12553                      AST_LIST_UNLOCK(&users);
12554                      return -1;
12555                   }
12556                   var = var->next;
12557                }
12558             }
12559          }
12560          cat = ast_category_browse(cfg, cat);
12561       }
12562 
12563       AST_LIST_UNLOCK(&users);
12564 
12565       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12566          start_poll_thread();
12567       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12568          stop_poll_thread();;
12569 
12570       return 0;
12571    } else {
12572       AST_LIST_UNLOCK(&users);
12573       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12574       return 0;
12575    }
12576 }
12577 
12578 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12579 {
12580    int res = -1;
12581    char dir[PATH_MAX];
12582    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12583    ast_debug(2, "About to try retrieving name file %s\n", dir);
12584    RETRIEVE(dir, -1, mailbox, context);
12585    if (ast_fileexists(dir, NULL, NULL)) {
12586       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12587    }
12588    DISPOSE(dir, -1);
12589    return res;
12590 }
12591 
12592 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12593    struct ast_config *pwconf;
12594    struct ast_flags config_flags = { 0 };
12595 
12596    pwconf = ast_config_load(secretfn, config_flags);
12597    if (valid_config(pwconf)) {
12598       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12599       if (val) {
12600          ast_copy_string(password, val, passwordlen);
12601          ast_config_destroy(pwconf);
12602          return;
12603       }
12604       ast_config_destroy(pwconf);
12605    }
12606    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12607 }
12608 
12609 static int write_password_to_file(const char *secretfn, const char *password) {
12610    struct ast_config *conf;
12611    struct ast_category *cat;
12612    struct ast_variable *var;
12613    int res = -1;
12614 
12615    if (!(conf = ast_config_new())) {
12616       ast_log(LOG_ERROR, "Error creating new config structure\n");
12617       return res;
12618    }
12619    if (!(cat = ast_category_new("general", "", 1))) {
12620       ast_log(LOG_ERROR, "Error creating new category structure\n");
12621       ast_config_destroy(conf);
12622       return res;
12623    }
12624    if (!(var = ast_variable_new("password", password, ""))) {
12625       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12626       ast_config_destroy(conf);
12627       ast_category_destroy(cat);
12628       return res;
12629    }
12630    ast_category_append(conf, cat);
12631    ast_variable_append(cat, var);
12632    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12633       res = 0;
12634    } else {
12635       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12636    }
12637 
12638    ast_config_destroy(conf);
12639    return res;
12640 }
12641 
12642 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12643 {
12644    char *context;
12645    char *args_copy;
12646    int res;
12647 
12648    if (ast_strlen_zero(data)) {
12649       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12650       return -1;
12651    }
12652 
12653    args_copy = ast_strdupa(data);
12654    if ((context = strchr(args_copy, '@'))) {
12655       *context++ = '\0';
12656    } else {
12657       context = "default";
12658    }
12659 
12660    if ((res = sayname(chan, args_copy, context) < 0)) {
12661       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12662       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12663       if (!res) {
12664          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12665       }
12666    }
12667 
12668    return res;
12669 }
12670 
12671 #ifdef TEST_FRAMEWORK
12672 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12673 {
12674    return 0;
12675 }
12676 
12677 static struct ast_frame *fake_read(struct ast_channel *ast)
12678 {
12679    return &ast_null_frame;
12680 }
12681 
12682 AST_TEST_DEFINE(test_voicemail_vmsayname)
12683 {
12684    char dir[PATH_MAX];
12685    char dir2[PATH_MAX];
12686    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12687    static const char TEST_EXTENSION[] = "1234";
12688 
12689    struct ast_channel *test_channel1 = NULL;
12690    int res = -1;
12691 
12692    static const struct ast_channel_tech fake_tech = {
12693       .write = fake_write,
12694       .read = fake_read,
12695    };
12696 
12697    switch (cmd) {
12698    case TEST_INIT:
12699       info->name = "vmsayname_exec";
12700       info->category = "/apps/app_voicemail/";
12701       info->summary = "Vmsayname unit test";
12702       info->description =
12703          "This tests passing various parameters to vmsayname";
12704       return AST_TEST_NOT_RUN;
12705    case TEST_EXECUTE:
12706       break;
12707    }
12708 
12709    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12710         NULL, NULL, 0, 0, "TestChannel1"))) {
12711       goto exit_vmsayname_test;
12712    }
12713 
12714    /* normally this is done in the channel driver */
12715    test_channel1->nativeformats = AST_FORMAT_GSM;
12716    test_channel1->writeformat = AST_FORMAT_GSM;
12717    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12718    test_channel1->readformat = AST_FORMAT_GSM;
12719    test_channel1->rawreadformat = AST_FORMAT_GSM;
12720    test_channel1->tech = &fake_tech;
12721 
12722    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12723    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12724    if (!(res = vmsayname_exec(test_channel1, dir))) {
12725       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12726       if (ast_fileexists(dir, NULL, NULL)) {
12727          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12728          res = -1;
12729          goto exit_vmsayname_test;
12730       } else {
12731          /* no greeting already exists as expected, let's create one to fully test sayname */
12732          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12733             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12734             goto exit_vmsayname_test;
12735          }
12736          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12737          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12738          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12739          if ((res = symlink(dir, dir2))) {
12740             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12741             goto exit_vmsayname_test;
12742          }
12743          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12744          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12745          res = vmsayname_exec(test_channel1, dir);
12746 
12747          /* TODO: there may be a better way to do this */
12748          unlink(dir2);
12749          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12750          rmdir(dir2);
12751          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12752          rmdir(dir2);
12753       }
12754    }
12755 
12756 exit_vmsayname_test:
12757 
12758    if (test_channel1) {
12759       ast_hangup(test_channel1);
12760    }
12761 
12762    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12763 }
12764 
12765 AST_TEST_DEFINE(test_voicemail_msgcount)
12766 {
12767    int i, j, res = AST_TEST_PASS, syserr;
12768    struct ast_vm_user *vmu;
12769    struct ast_vm_user svm;
12770    struct vm_state vms;
12771 #ifdef IMAP_STORAGE
12772    struct ast_channel *chan = NULL;
12773 #endif
12774    struct {
12775       char dir[256];
12776       char file[256];
12777       char txtfile[256];
12778    } tmp[3];
12779    char syscmd[256];
12780    const char origweasels[] = "tt-weasels";
12781    const char testcontext[] = "test";
12782    const char testmailbox[] = "00000000";
12783    const char testspec[] = "00000000@test";
12784    FILE *txt;
12785    int new, old, urgent;
12786    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12787    const int folder2mbox[3] = { 1, 11, 0 };
12788    const int expected_results[3][12] = {
12789       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12790       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12791       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12792       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12793    };
12794 
12795    switch (cmd) {
12796    case TEST_INIT:
12797       info->name = "test_voicemail_msgcount";
12798       info->category = "/apps/app_voicemail/";
12799       info->summary = "Test Voicemail status checks";
12800       info->description =
12801          "Verify that message counts are correct when retrieved through the public API";
12802       return AST_TEST_NOT_RUN;
12803    case TEST_EXECUTE:
12804       break;
12805    }
12806 
12807    /* Make sure the original path was completely empty */
12808    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12809    if ((syserr = ast_safe_system(syscmd))) {
12810       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12811          syserr > 0 ? strerror(syserr) : "unable to fork()");
12812       return AST_TEST_FAIL;
12813    }
12814 
12815 #ifdef IMAP_STORAGE
12816    if (!(chan = ast_dummy_channel_alloc())) {
12817       ast_test_status_update(test, "Unable to create dummy channel\n");
12818       return AST_TEST_FAIL;
12819    }
12820 #endif
12821 
12822    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12823       !(vmu = find_or_create(testcontext, testmailbox))) {
12824       ast_test_status_update(test, "Cannot create vmu structure\n");
12825       ast_unreplace_sigchld();
12826 #ifdef IMAP_STORAGE
12827       chan = ast_channel_unref(chan);
12828 #endif
12829       return AST_TEST_FAIL;
12830    }
12831 
12832    populate_defaults(vmu);
12833    memset(&vms, 0, sizeof(vms));
12834 
12835    /* Create temporary voicemail */
12836    for (i = 0; i < 3; i++) {
12837       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12838       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12839       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12840 
12841       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12842          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12843             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12844          if ((syserr = ast_safe_system(syscmd))) {
12845             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12846                syserr > 0 ? strerror(syserr) : "unable to fork()");
12847             ast_unreplace_sigchld();
12848 #ifdef IMAP_STORAGE
12849             chan = ast_channel_unref(chan);
12850 #endif
12851             return AST_TEST_FAIL;
12852          }
12853       }
12854 
12855       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12856          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12857          fclose(txt);
12858       } else {
12859          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12860          res = AST_TEST_FAIL;
12861          break;
12862       }
12863       open_mailbox(&vms, vmu, folder2mbox[i]);
12864       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12865 
12866       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12867       for (j = 0; j < 3; j++) {
12868          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12869          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12870             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12871                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12872             res = AST_TEST_FAIL;
12873          }
12874       }
12875 
12876       new = old = urgent = 0;
12877       if (ast_app_inboxcount(testspec, &new, &old)) {
12878          ast_test_status_update(test, "inboxcount returned failure\n");
12879          res = AST_TEST_FAIL;
12880       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12881          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12882             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12883          res = AST_TEST_FAIL;
12884       }
12885 
12886       new = old = urgent = 0;
12887       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12888          ast_test_status_update(test, "inboxcount2 returned failure\n");
12889          res = AST_TEST_FAIL;
12890       } else if (old != expected_results[i][6 + 0] ||
12891             urgent != expected_results[i][6 + 1] ||
12892                new != expected_results[i][6 + 2]    ) {
12893          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12894             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12895          res = AST_TEST_FAIL;
12896       }
12897 
12898       new = old = urgent = 0;
12899       for (j = 0; j < 3; j++) {
12900          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12901             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12902                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12903             res = AST_TEST_FAIL;
12904          }
12905       }
12906    }
12907 
12908    for (i = 0; i < 3; i++) {
12909       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12910        * server, in which case, the rm below will not affect the
12911        * voicemails. */
12912       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12913       DISPOSE(tmp[i].dir, 0);
12914    }
12915 
12916    if (vms.deleted) {
12917       ast_free(vms.deleted);
12918    }
12919    if (vms.heard) {
12920       ast_free(vms.heard);
12921    }
12922 
12923 #ifdef IMAP_STORAGE
12924    chan = ast_channel_unref(chan);
12925 #endif
12926 
12927    /* And remove test directory */
12928    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12929    if ((syserr = ast_safe_system(syscmd))) {
12930       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12931          syserr > 0 ? strerror(syserr) : "unable to fork()");
12932    }
12933 
12934    return res;
12935 }
12936 
12937 AST_TEST_DEFINE(test_voicemail_notify_endl)
12938 {
12939    int res = AST_TEST_PASS;
12940    char testcontext[] = "test";
12941    char testmailbox[] = "00000000";
12942    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12943    char attach[256], attach2[256];
12944    char buf[256] = ""; /* No line should actually be longer than 80 */
12945    struct ast_channel *chan = NULL;
12946    struct ast_vm_user *vmu, vmus = {
12947       .flags = 0,
12948    };
12949    FILE *file;
12950    struct {
12951       char *name;
12952       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12953       void *location;
12954       union {
12955          int intval;
12956          char *strval;
12957       } u;
12958    } test_items[] = {
12959       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12960       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12961       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12962       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12963       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12964       { "attach2", STRPTR, attach2, .u.strval = "" },
12965       { "attach", STRPTR, attach, .u.strval = "" },
12966    };
12967    int which;
12968 
12969    switch (cmd) {
12970    case TEST_INIT:
12971       info->name = "test_voicemail_notify_endl";
12972       info->category = "/apps/app_voicemail/";
12973       info->summary = "Test Voicemail notification end-of-line";
12974       info->description =
12975          "Verify that notification emails use a consistent end-of-line character";
12976       return AST_TEST_NOT_RUN;
12977    case TEST_EXECUTE:
12978       break;
12979    }
12980 
12981    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12982    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12983 
12984    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12985       !(vmu = find_or_create(testcontext, testmailbox))) {
12986       ast_test_status_update(test, "Cannot create vmu structure\n");
12987       return AST_TEST_NOT_RUN;
12988    }
12989 
12990    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12991       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12992       return AST_TEST_NOT_RUN;
12993    }
12994 
12995    populate_defaults(vmu);
12996    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12997 #ifdef IMAP_STORAGE
12998    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12999 #endif
13000 
13001    file = tmpfile();
13002    for (which = 0; which < ARRAY_LEN(test_items); which++) {
13003       /* Kill previous test, if any */
13004       rewind(file);
13005       if (ftruncate(fileno(file), 0)) {
13006          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
13007          res = AST_TEST_FAIL;
13008          break;
13009       }
13010 
13011       /* Make each change, in order, to the test mailbox */
13012       if (test_items[which].type == INT) {
13013          *((int *) test_items[which].location) = test_items[which].u.intval;
13014       } else if (test_items[which].type == FLAGVAL) {
13015          if (ast_test_flag(vmu, test_items[which].u.intval)) {
13016             ast_clear_flag(vmu, test_items[which].u.intval);
13017          } else {
13018             ast_set_flag(vmu, test_items[which].u.intval);
13019          }
13020       } else if (test_items[which].type == STATIC) {
13021          strcpy(test_items[which].location, test_items[which].u.strval);
13022       } else if (test_items[which].type == STRPTR) {
13023          test_items[which].location = test_items[which].u.strval;
13024       }
13025 
13026       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13027       rewind(file);
13028       while (fgets(buf, sizeof(buf), file)) {
13029          if (
13030 #ifdef IMAP_STORAGE
13031          buf[strlen(buf) - 2] != '\r'
13032 #else
13033          buf[strlen(buf) - 2] == '\r'
13034 #endif
13035          || buf[strlen(buf) - 1] != '\n') {
13036             res = AST_TEST_FAIL;
13037          }
13038       }
13039    }
13040    fclose(file);
13041    return res;
13042 }
13043 
13044 AST_TEST_DEFINE(test_voicemail_load_config)
13045 {
13046    int res = AST_TEST_PASS;
13047    struct ast_vm_user *vmu;
13048    struct ast_config *cfg;
13049    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13050    int fd;
13051    FILE *file;
13052    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13053 
13054    switch (cmd) {
13055    case TEST_INIT:
13056       info->name = "test_voicemail_load_config";
13057       info->category = "/apps/app_voicemail/";
13058       info->summary = "Test loading Voicemail config";
13059       info->description =
13060          "Verify that configuration is loaded consistently. "
13061          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13062          "some options were loaded after the mailboxes were instantiated, causing "
13063          "those options not to be set correctly.";
13064       return AST_TEST_NOT_RUN;
13065    case TEST_EXECUTE:
13066       break;
13067    }
13068 
13069    /* build a config file by hand... */
13070    if ((fd = mkstemp(config_filename)) < 0) {
13071       return AST_TEST_FAIL;
13072    }
13073    if (!(file = fdopen(fd, "w"))) {
13074       close(fd);
13075       unlink(config_filename);
13076       return AST_TEST_FAIL;
13077    }
13078    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13079    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13080    fputs("00000002 => 9999,Mrs. Test\n", file);
13081    fclose(file);
13082 
13083    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13084       res = AST_TEST_FAIL;
13085       goto cleanup;
13086    }
13087 
13088    load_config_from_memory(1, cfg, NULL);
13089    ast_config_destroy(cfg);
13090 
13091 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13092    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13093    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13094 
13095    AST_LIST_LOCK(&users);
13096    AST_LIST_TRAVERSE(&users, vmu, list) {
13097       if (!strcmp(vmu->mailbox, "00000001")) {
13098          if (0); /* trick to get CHECK to work */
13099          CHECK(vmu, callback, "othercontext")
13100          CHECK(vmu, locale, "nl_NL.UTF-8")
13101          CHECK(vmu, zonetag, "central")
13102       } else if (!strcmp(vmu->mailbox, "00000002")) {
13103          if (0); /* trick to get CHECK to work */
13104          CHECK(vmu, callback, "somecontext")
13105          CHECK(vmu, locale, "de_DE.UTF-8")
13106          CHECK(vmu, zonetag, "european")
13107       }
13108    }
13109    AST_LIST_UNLOCK(&users);
13110 
13111 #undef CHECK
13112 
13113    /* restore config */
13114    load_config(1); /* this might say "Failed to load configuration file." */
13115 
13116 cleanup:
13117    unlink(config_filename);
13118    return res;
13119 }
13120 
13121 #endif /* defined(TEST_FRAMEWORK) */
13122 
13123 static int reload(void)
13124 {
13125    return load_config(1);
13126 }
13127 
13128 static int unload_module(void)
13129 {
13130    int res;
13131 
13132    res = ast_unregister_application(app);
13133    res |= ast_unregister_application(app2);
13134    res |= ast_unregister_application(app3);
13135    res |= ast_unregister_application(app4);
13136    res |= ast_unregister_application(sayname_app);
13137    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13138    res |= ast_manager_unregister("VoicemailUsersList");
13139    res |= ast_data_unregister(NULL);
13140 #ifdef TEST_FRAMEWORK
13141    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13142    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13143    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13144    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13145    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13146 #endif
13147    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13148    ast_uninstall_vm_functions();
13149    ao2_ref(inprocess_container, -1);
13150 
13151    if (poll_thread != AST_PTHREADT_NULL)
13152       stop_poll_thread();
13153 
13154    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13155    ast_unload_realtime("voicemail");
13156    ast_unload_realtime("voicemail_data");
13157 
13158    free_vm_users();
13159    free_vm_zones();
13160    return res;
13161 }
13162 
13163 static int load_module(void)
13164 {
13165    int res;
13166    my_umask = umask(0);
13167    umask(my_umask);
13168 
13169    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13170       return AST_MODULE_LOAD_DECLINE;
13171    }
13172 
13173    /* compute the location of the voicemail spool directory */
13174    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13175    
13176    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13177       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13178    }
13179 
13180    if ((res = load_config(0)))
13181       return res;
13182 
13183    res = ast_register_application_xml(app, vm_exec);
13184    res |= ast_register_application_xml(app2, vm_execmain);
13185    res |= ast_register_application_xml(app3, vm_box_exists);
13186    res |= ast_register_application_xml(app4, vmauthenticate);
13187    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13188    res |= ast_custom_function_register(&mailbox_exists_acf);
13189    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13190 #ifdef TEST_FRAMEWORK
13191    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13192    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13193    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13194    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13195    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13196 #endif
13197 
13198    if (res)
13199       return res;
13200 
13201    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13202    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13203 
13204    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13205    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13206    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13207 
13208    return res;
13209 }
13210 
13211 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13212 {
13213    int cmd = 0;
13214    char destination[80] = "";
13215    int retries = 0;
13216 
13217    if (!num) {
13218       ast_verb(3, "Destination number will be entered manually\n");
13219       while (retries < 3 && cmd != 't') {
13220          destination[1] = '\0';
13221          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13222          if (!cmd)
13223             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13224          if (!cmd)
13225             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13226          if (!cmd) {
13227             cmd = ast_waitfordigit(chan, 6000);
13228             if (cmd)
13229                destination[0] = cmd;
13230          }
13231          if (!cmd) {
13232             retries++;
13233          } else {
13234 
13235             if (cmd < 0)
13236                return 0;
13237             if (cmd == '*') {
13238                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13239                return 0;
13240             }
13241             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13242                retries++;
13243             else
13244                cmd = 't';
13245          }
13246          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13247       }
13248       if (retries >= 3) {
13249          return 0;
13250       }
13251       
13252    } else {
13253       if (option_verbose > 2)
13254          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13255       ast_copy_string(destination, num, sizeof(destination));
13256    }
13257 
13258    if (!ast_strlen_zero(destination)) {
13259       if (destination[strlen(destination) -1 ] == '*')
13260          return 0; 
13261       if (option_verbose > 2)
13262          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13263       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13264       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13265       chan->priority = 0;
13266       return 9;
13267    }
13268    return 0;
13269 }
13270 
13271 /*!
13272  * \brief The advanced options within a message.
13273  * \param chan
13274  * \param vmu 
13275  * \param vms
13276  * \param msg
13277  * \param option
13278  * \param record_gain
13279  *
13280  * Provides handling for the play message envelope, call the person back, or reply to message. 
13281  *
13282  * \return zero on success, -1 on error.
13283  */
13284 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)
13285 {
13286    int res = 0;
13287    char filename[PATH_MAX];
13288    struct ast_config *msg_cfg = NULL;
13289    const char *origtime, *context;
13290    char *name, *num;
13291    int retries = 0;
13292    char *cid;
13293    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13294 
13295    vms->starting = 0; 
13296 
13297    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13298 
13299    /* Retrieve info from VM attribute file */
13300    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13301    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13302    msg_cfg = ast_config_load(filename, config_flags);
13303    DISPOSE(vms->curdir, vms->curmsg);
13304    if (!valid_config(msg_cfg)) {
13305       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13306       return 0;
13307    }
13308 
13309    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13310       ast_config_destroy(msg_cfg);
13311       return 0;
13312    }
13313 
13314    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13315 
13316    context = ast_variable_retrieve(msg_cfg, "message", "context");
13317    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13318       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13319    switch (option) {
13320    case 3: /* Play message envelope */
13321       if (!res)
13322          res = play_message_datetime(chan, vmu, origtime, filename);
13323       if (!res)
13324          res = play_message_callerid(chan, vms, cid, context, 0);
13325 
13326       res = 't';
13327       break;
13328 
13329    case 2:  /* Call back */
13330 
13331       if (ast_strlen_zero(cid))
13332          break;
13333 
13334       ast_callerid_parse(cid, &name, &num);
13335       while ((res > -1) && (res != 't')) {
13336          switch (res) {
13337          case '1':
13338             if (num) {
13339                /* Dial the CID number */
13340                res = dialout(chan, vmu, num, vmu->callback);
13341                if (res) {
13342                   ast_config_destroy(msg_cfg);
13343                   return 9;
13344                }
13345             } else {
13346                res = '2';
13347             }
13348             break;
13349 
13350          case '2':
13351             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13352             if (!ast_strlen_zero(vmu->dialout)) {
13353                res = dialout(chan, vmu, NULL, vmu->dialout);
13354                if (res) {
13355                   ast_config_destroy(msg_cfg);
13356                   return 9;
13357                }
13358             } else {
13359                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13360                res = ast_play_and_wait(chan, "vm-sorry");
13361             }
13362             ast_config_destroy(msg_cfg);
13363             return res;
13364          case '*':
13365             res = 't';
13366             break;
13367          case '3':
13368          case '4':
13369          case '5':
13370          case '6':
13371          case '7':
13372          case '8':
13373          case '9':
13374          case '0':
13375 
13376             res = ast_play_and_wait(chan, "vm-sorry");
13377             retries++;
13378             break;
13379          default:
13380             if (num) {
13381                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13382                res = ast_play_and_wait(chan, "vm-num-i-have");
13383                if (!res)
13384                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13385                if (!res)
13386                   res = ast_play_and_wait(chan, "vm-tocallnum");
13387                /* Only prompt for a caller-specified number if there is a dialout context specified */
13388                if (!ast_strlen_zero(vmu->dialout)) {
13389                   if (!res)
13390                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13391                }
13392             } else {
13393                res = ast_play_and_wait(chan, "vm-nonumber");
13394                if (!ast_strlen_zero(vmu->dialout)) {
13395                   if (!res)
13396                      res = ast_play_and_wait(chan, "vm-toenternumber");
13397                }
13398             }
13399             if (!res) {
13400                res = ast_play_and_wait(chan, "vm-star-cancel");
13401             }
13402             if (!res) {
13403                res = ast_waitfordigit(chan, 6000);
13404             }
13405             if (!res) {
13406                retries++;
13407                if (retries > 3) {
13408                   res = 't';
13409                }
13410             }
13411             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13412             break; 
13413             
13414          }
13415          if (res == 't')
13416             res = 0;
13417          else if (res == '*')
13418             res = -1;
13419       }
13420       break;
13421       
13422    case 1:  /* Reply */
13423       /* Send reply directly to sender */
13424       if (ast_strlen_zero(cid))
13425          break;
13426 
13427       ast_callerid_parse(cid, &name, &num);
13428       if (!num) {
13429          ast_verb(3, "No CID number available, no reply sent\n");
13430          if (!res)
13431             res = ast_play_and_wait(chan, "vm-nonumber");
13432          ast_config_destroy(msg_cfg);
13433          return res;
13434       } else {
13435          struct ast_vm_user vmu2;
13436          if (find_user(&vmu2, vmu->context, num)) {
13437             struct leave_vm_options leave_options;
13438             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13439             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13440 
13441             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13442             
13443             memset(&leave_options, 0, sizeof(leave_options));
13444             leave_options.record_gain = record_gain;
13445             res = leave_voicemail(chan, mailbox, &leave_options);
13446             if (!res)
13447                res = 't';
13448             ast_config_destroy(msg_cfg);
13449             return res;
13450          } else {
13451             /* Sender has no mailbox, can't reply */
13452             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13453             ast_play_and_wait(chan, "vm-nobox");
13454             res = 't';
13455             ast_config_destroy(msg_cfg);
13456             return res;
13457          }
13458       } 
13459       res = 0;
13460 
13461       break;
13462    }
13463 
13464    ast_config_destroy(msg_cfg);
13465 
13466 #ifndef IMAP_STORAGE
13467    if (!res) {
13468       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13469       vms->heard[msg] = 1;
13470       res = wait_file(chan, vms, vms->fn);
13471    }
13472 #endif
13473    return res;
13474 }
13475 
13476 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13477          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13478          signed char record_gain, struct vm_state *vms, char *flag)
13479 {
13480    /* Record message & let caller review or re-record it, or set options if applicable */
13481    int res = 0;
13482    int cmd = 0;
13483    int max_attempts = 3;
13484    int attempts = 0;
13485    int recorded = 0;
13486    int msg_exists = 0;
13487    signed char zero_gain = 0;
13488    char tempfile[PATH_MAX];
13489    char *acceptdtmf = "#";
13490    char *canceldtmf = "";
13491    int canceleddtmf = 0;
13492 
13493    /* Note that urgent and private are for flagging messages as such in the future */
13494 
13495    /* barf if no pointer passed to store duration in */
13496    if (duration == NULL) {
13497       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13498       return -1;
13499    }
13500 
13501    if (!outsidecaller)
13502       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13503    else
13504       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13505 
13506    cmd = '3';  /* Want to start by recording */
13507 
13508    while ((cmd >= 0) && (cmd != 't')) {
13509       switch (cmd) {
13510       case '1':
13511          if (!msg_exists) {
13512             /* In this case, 1 is to record a message */
13513             cmd = '3';
13514             break;
13515          } else {
13516             /* Otherwise 1 is to save the existing message */
13517             ast_verb(3, "Saving message as is\n");
13518             if (!outsidecaller) 
13519                ast_filerename(tempfile, recordfile, NULL);
13520             ast_stream_and_wait(chan, "vm-msgsaved", "");
13521             if (!outsidecaller) {
13522                /* Saves to IMAP server only if imapgreeting=yes */
13523                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13524                DISPOSE(recordfile, -1);
13525             }
13526             cmd = 't';
13527             return res;
13528          }
13529       case '2':
13530          /* Review */
13531          ast_verb(3, "Reviewing the message\n");
13532          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13533          break;
13534       case '3':
13535          msg_exists = 0;
13536          /* Record */
13537          if (recorded == 1) 
13538             ast_verb(3, "Re-recording the message\n");
13539          else  
13540             ast_verb(3, "Recording the message\n");
13541          
13542          if (recorded && outsidecaller) {
13543             cmd = ast_play_and_wait(chan, INTRO);
13544             cmd = ast_play_and_wait(chan, "beep");
13545          }
13546          recorded = 1;
13547          /* 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 */
13548          if (record_gain)
13549             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13550          if (ast_test_flag(vmu, VM_OPERATOR))
13551             canceldtmf = "0";
13552          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13553          if (strchr(canceldtmf, cmd)) {
13554          /* need this flag here to distinguish between pressing '0' during message recording or after */
13555             canceleddtmf = 1;
13556          }
13557          if (record_gain)
13558             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13559          if (cmd == -1) {
13560             /* User has hung up, no options to give */
13561             if (!outsidecaller) {
13562                /* user was recording a greeting and they hung up, so let's delete the recording. */
13563                ast_filedelete(tempfile, NULL);
13564             }     
13565             return cmd;
13566          }
13567          if (cmd == '0') {
13568             break;
13569          } else if (cmd == '*') {
13570             break;
13571 #if 0
13572          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13573             /* Message is too short */
13574             ast_verb(3, "Message too short\n");
13575             cmd = ast_play_and_wait(chan, "vm-tooshort");
13576             cmd = ast_filedelete(tempfile, NULL);
13577             break;
13578          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13579             /* Message is all silence */
13580             ast_verb(3, "Nothing recorded\n");
13581             cmd = ast_filedelete(tempfile, NULL);
13582             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13583             if (!cmd)
13584                cmd = ast_play_and_wait(chan, "vm-speakup");
13585             break;
13586 #endif
13587          } else {
13588             /* If all is well, a message exists */
13589             msg_exists = 1;
13590             cmd = 0;
13591          }
13592          break;
13593       case '4':
13594          if (outsidecaller) {  /* only mark vm messages */
13595             /* Mark Urgent */
13596             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13597                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13598                res = ast_play_and_wait(chan, "vm-marked-urgent");
13599                strcpy(flag, "Urgent");
13600             } else if (flag) {
13601                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13602                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13603                strcpy(flag, "");
13604             } else {
13605                ast_play_and_wait(chan, "vm-sorry");
13606             }
13607             cmd = 0;
13608          } else {
13609             cmd = ast_play_and_wait(chan, "vm-sorry");
13610          }
13611          break;
13612       case '5':
13613       case '6':
13614       case '7':
13615       case '8':
13616       case '9':
13617       case '*':
13618       case '#':
13619          cmd = ast_play_and_wait(chan, "vm-sorry");
13620          break;
13621 #if 0 
13622 /*  XXX Commented out for the moment because of the dangers of deleting
13623     a message while recording (can put the message numbers out of sync) */
13624       case '*':
13625          /* Cancel recording, delete message, offer to take another message*/
13626          cmd = ast_play_and_wait(chan, "vm-deleted");
13627          cmd = ast_filedelete(tempfile, NULL);
13628          if (outsidecaller) {
13629             res = vm_exec(chan, NULL);
13630             return res;
13631          }
13632          else
13633             return 1;
13634 #endif
13635       case '0':
13636          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13637             cmd = ast_play_and_wait(chan, "vm-sorry");
13638             break;
13639          }
13640          if (msg_exists || recorded) {
13641             cmd = ast_play_and_wait(chan, "vm-saveoper");
13642             if (!cmd)
13643                cmd = ast_waitfordigit(chan, 3000);
13644             if (cmd == '1') {
13645                ast_filerename(tempfile, recordfile, NULL);
13646                ast_play_and_wait(chan, "vm-msgsaved");
13647                cmd = '0';
13648             } else if (cmd == '4') {
13649                if (flag) {
13650                   ast_play_and_wait(chan, "vm-marked-urgent");
13651                   strcpy(flag, "Urgent");
13652                }
13653                ast_play_and_wait(chan, "vm-msgsaved");
13654                cmd = '0';
13655             } else {
13656                ast_play_and_wait(chan, "vm-deleted");
13657                DELETE(tempfile, -1, tempfile, vmu);
13658                cmd = '0';
13659             }
13660          }
13661          return cmd;
13662       default:
13663          /* If the caller is an ouside caller, and the review option is enabled,
13664             allow them to review the message, but let the owner of the box review
13665             their OGM's */
13666          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13667             return cmd;
13668          if (msg_exists) {
13669             cmd = ast_play_and_wait(chan, "vm-review");
13670             if (!cmd && outsidecaller) {
13671                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13672                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13673                } else if (flag) {
13674                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13675                }
13676             }
13677          } else {
13678             cmd = ast_play_and_wait(chan, "vm-torerecord");
13679             if (!cmd)
13680                cmd = ast_waitfordigit(chan, 600);
13681          }
13682          
13683          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13684             cmd = ast_play_and_wait(chan, "vm-reachoper");
13685             if (!cmd)
13686                cmd = ast_waitfordigit(chan, 600);
13687          }
13688 #if 0
13689          if (!cmd)
13690             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13691 #endif
13692          if (!cmd)
13693             cmd = ast_waitfordigit(chan, 6000);
13694          if (!cmd) {
13695             attempts++;
13696          }
13697          if (attempts > max_attempts) {
13698             cmd = 't';
13699          }
13700       }
13701    }
13702    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13703       /* Hang up or timeout, so delete the recording. */
13704       ast_filedelete(tempfile, NULL);
13705    }
13706 
13707    if (cmd != 't' && outsidecaller)
13708       ast_play_and_wait(chan, "vm-goodbye");
13709 
13710    return cmd;
13711 }
13712 
13713 /* This is a workaround so that menuselect displays a proper description
13714  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13715  */
13716 
13717 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13718       .load = load_module,
13719       .unload = unload_module,
13720       .reload = reload,
13721       .nonoptreq = "res_adsi,res_smdi",
13722       );

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