Wed Aug 7 17:15:34 2019

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, Greek, 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 Greek, Portuguese & 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/1");
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/1");
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 /* Hebrew Syntax */
09583 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09584 {
09585    int cmd = 0;
09586 
09587    if (vms->lastmsg > -1) {
09588       cmd = play_message(chan, vmu, vms);
09589    } else {
09590       if (!strcasecmp(vms->fn, "INBOX")) {
09591          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09592       } else {
09593          cmd = ast_play_and_wait(chan, "vm-nomessages");
09594       }
09595    }
09596    return cmd;
09597 }
09598 
09599 /*! 
09600  * \brief Default English syntax for 'You have N messages' greeting.
09601  * \param chan
09602  * \param vms
09603  * \param vmu
09604  *
09605  * \return zero on success, -1 on error.
09606  */
09607 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09608 {
09609    int cmd = 0;
09610 
09611    if (vms->lastmsg > -1) {
09612       cmd = play_message(chan, vmu, vms);
09613    } else {
09614       cmd = ast_play_and_wait(chan, "vm-youhave");
09615       if (!cmd) 
09616          cmd = ast_play_and_wait(chan, "vm-no");
09617       if (!cmd) {
09618          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09619          cmd = ast_play_and_wait(chan, vms->fn);
09620       }
09621       if (!cmd)
09622          cmd = ast_play_and_wait(chan, "vm-messages");
09623    }
09624    return cmd;
09625 }
09626 
09627 
09628 /*! 
09629  * \brief Common LATIN languages syntax for 'You have N messages' greeting.
09630  * \param chan
09631  * \param vms
09632  * \param vmu
09633  *
09634  * \return zero on success, -1 on error.
09635  */
09636 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09637 {
09638    int cmd;
09639 
09640    if (vms->lastmsg > -1) {
09641       cmd = play_message(chan, vmu, vms);
09642    } else {
09643       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09644       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09645          if (!cmd) {
09646             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09647             cmd = ast_play_and_wait(chan, vms->fn);
09648          }
09649          if (!cmd)
09650             cmd = ast_play_and_wait(chan, "vm-messages");
09651       } else {
09652          if (!cmd)
09653             cmd = ast_play_and_wait(chan, "vm-messages");
09654          if (!cmd) {
09655             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09656             cmd = ast_play_and_wait(chan, vms->fn);
09657          }
09658       }
09659    }
09660    return cmd;
09661 }
09662 
09663 /*! 
09664  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09665  * \param chan
09666  * \param vms
09667  * \param vmu
09668  *
09669  * \return zero on success, -1 on error.
09670  */
09671 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09672 {
09673    int cmd;
09674 
09675    if (vms->lastmsg > -1) {
09676       cmd = play_message(chan, vmu, vms);
09677    } else {
09678       cmd = ast_play_and_wait(chan, "vm-you");
09679       if (!cmd) 
09680          cmd = ast_play_and_wait(chan, "vm-haveno");
09681       if (!cmd)
09682          cmd = ast_play_and_wait(chan, "vm-messages");
09683       if (!cmd) {
09684          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09685          cmd = ast_play_and_wait(chan, vms->fn);
09686       }
09687    }
09688    return cmd;
09689 }
09690 
09691 /*! 
09692  * \brief Vietnamese syntax for 'You have N messages' greeting.
09693  * \param chan
09694  * \param vms
09695  * \param vmu
09696  *
09697  * \return zero on success, -1 on error.
09698  */
09699 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09700 {
09701    int cmd = 0;
09702 
09703    if (vms->lastmsg > -1) {
09704       cmd = play_message(chan, vmu, vms);
09705    } else {
09706       cmd = ast_play_and_wait(chan, "vm-no");
09707       if (!cmd) {
09708          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09709          cmd = ast_play_and_wait(chan, vms->fn);
09710       }
09711    }
09712    return cmd;
09713 }
09714 
09715 /*!
09716  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09717  * \param chan The channel for the current user. We read the language property from this.
09718  * \param vms passed into the language-specific vm_browse_messages function.
09719  * \param vmu passed into the language-specific vm_browse_messages function.
09720  * 
09721  * The method to be invoked is determined by the value of language code property in the user's channel.
09722  * The default (when unable to match) is to use english.
09723  *
09724  * \return zero on success, -1 on error.
09725  */
09726 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09727 {
09728    if (!strncasecmp(chan->language, "es", 2) ||
09729          !strncasecmp(chan->language, "it", 2) ||
09730          !strncasecmp(chan->language, "pt", 2) ||
09731          !strncasecmp(chan->language, "gr", 2)) {         /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
09732       return vm_browse_messages_latin(chan, vms, vmu);
09733    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09734       return vm_browse_messages_he(chan, vms, vmu);
09735    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09736       return vm_browse_messages_vi(chan, vms, vmu);
09737    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09738       return vm_browse_messages_zh(chan, vms, vmu);
09739    } else {                                             /* Default to English syntax */
09740       return vm_browse_messages_en(chan, vms, vmu);
09741    }
09742 }
09743 
09744 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09745          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09746          int skipuser, int max_logins, int silent)
09747 {
09748    int useadsi = 0, valid = 0, logretries = 0;
09749    char password[AST_MAX_EXTENSION]="", *passptr;
09750    struct ast_vm_user vmus, *vmu = NULL;
09751 
09752    /* If ADSI is supported, setup login screen */
09753    adsi_begin(chan, &useadsi);
09754    if (!skipuser && useadsi)
09755       adsi_login(chan);
09756    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09757       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09758       return -1;
09759    }
09760 
09761    /* Authenticate them and get their mailbox/password */
09762 
09763    while (!valid && (logretries < max_logins)) {
09764       /* Prompt for, and read in the username */
09765       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09766          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09767          return -1;
09768       }
09769       if (ast_strlen_zero(mailbox)) {
09770          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09771             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09772          } else {
09773             ast_verb(3, "Username not entered\n"); 
09774             return -1;
09775          }
09776       } else if (mailbox[0] == '*') {
09777          /* user entered '*' */
09778          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09779          if (ast_exists_extension(chan, chan->context, "a", 1,
09780             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09781             return -1;
09782          }
09783          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09784          mailbox[0] = '\0';
09785       }
09786 
09787       if (useadsi)
09788          adsi_password(chan);
09789 
09790       if (!ast_strlen_zero(prefix)) {
09791          char fullusername[80] = "";
09792          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09793          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09794          ast_copy_string(mailbox, fullusername, mailbox_size);
09795       }
09796 
09797       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09798       vmu = find_user(&vmus, context, mailbox);
09799       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09800          /* saved password is blank, so don't bother asking */
09801          password[0] = '\0';
09802       } else {
09803          if (ast_streamfile(chan, vm_password, chan->language)) {
09804             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09805             return -1;
09806          }
09807          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09808             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09809             return -1;
09810          } else if (password[0] == '*') {
09811             /* user entered '*' */
09812             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09813             if (ast_exists_extension(chan, chan->context, "a", 1,
09814                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09815                mailbox[0] = '*';
09816                return -1;
09817             }
09818             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09819             mailbox[0] = '\0';
09820             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09821             vmu = NULL;
09822          }
09823       }
09824 
09825       if (vmu) {
09826          passptr = vmu->password;
09827          if (passptr[0] == '-') passptr++;
09828       }
09829       if (vmu && !strcmp(passptr, password))
09830          valid++;
09831       else {
09832          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09833          if (!ast_strlen_zero(prefix))
09834             mailbox[0] = '\0';
09835       }
09836       logretries++;
09837       if (!valid) {
09838          if (skipuser || logretries >= max_logins) {
09839             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09840                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09841                return -1;
09842             }
09843          } else {
09844             if (useadsi)
09845                adsi_login(chan);
09846             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09847                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09848                return -1;
09849             }
09850          }
09851          if (ast_waitstream(chan, "")) /* Channel is hung up */
09852             return -1;
09853       }
09854    }
09855    if (!valid && (logretries >= max_logins)) {
09856       ast_stopstream(chan);
09857       ast_play_and_wait(chan, "vm-goodbye");
09858       return -1;
09859    }
09860    if (vmu && !skipuser) {
09861       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09862    }
09863    return 0;
09864 }
09865 
09866 static int vm_execmain(struct ast_channel *chan, const char *data)
09867 {
09868    /* XXX This is, admittedly, some pretty horrendous code.  For some
09869       reason it just seemed a lot easier to do with GOTO's.  I feel
09870       like I'm back in my GWBASIC days. XXX */
09871    int res = -1;
09872    int cmd = 0;
09873    int valid = 0;
09874    char prefixstr[80] ="";
09875    char ext_context[256]="";
09876    int box;
09877    int useadsi = 0;
09878    int skipuser = 0;
09879    struct vm_state vms;
09880    struct ast_vm_user *vmu = NULL, vmus;
09881    char *context = NULL;
09882    int silentexit = 0;
09883    struct ast_flags flags = { 0 };
09884    signed char record_gain = 0;
09885    int play_auto = 0;
09886    int play_folder = 0;
09887    int in_urgent = 0;
09888 #ifdef IMAP_STORAGE
09889    int deleted = 0;
09890 #endif
09891 
09892    /* Add the vm_state to the active list and keep it active */
09893    memset(&vms, 0, sizeof(vms));
09894 
09895    vms.lastmsg = -1;
09896 
09897    memset(&vmus, 0, sizeof(vmus));
09898 
09899    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09900    if (chan->_state != AST_STATE_UP) {
09901       ast_debug(1, "Before ast_answer\n");
09902       ast_answer(chan);
09903    }
09904 
09905    if (!ast_strlen_zero(data)) {
09906       char *opts[OPT_ARG_ARRAY_SIZE];
09907       char *parse;
09908       AST_DECLARE_APP_ARGS(args,
09909          AST_APP_ARG(argv0);
09910          AST_APP_ARG(argv1);
09911       );
09912 
09913       parse = ast_strdupa(data);
09914 
09915       AST_STANDARD_APP_ARGS(args, parse);
09916 
09917       if (args.argc == 2) {
09918          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09919             return -1;
09920          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09921             int gain;
09922             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09923                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09924                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09925                   return -1;
09926                } else {
09927                   record_gain = (signed char) gain;
09928                }
09929             } else {
09930                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09931             }
09932          }
09933          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09934             play_auto = 1;
09935             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09936                /* See if it is a folder name first */
09937                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09938                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09939                      play_folder = -1;
09940                   }
09941                } else {
09942                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09943                }
09944             } else {
09945                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09946             }
09947             if (play_folder > 9 || play_folder < 0) {
09948                ast_log(AST_LOG_WARNING,
09949                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09950                   opts[OPT_ARG_PLAYFOLDER]);
09951                play_folder = 0;
09952             }
09953          }
09954       } else {
09955          /* old style options parsing */
09956          while (*(args.argv0)) {
09957             if (*(args.argv0) == 's')
09958                ast_set_flag(&flags, OPT_SILENT);
09959             else if (*(args.argv0) == 'p')
09960                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09961             else 
09962                break;
09963             (args.argv0)++;
09964          }
09965 
09966       }
09967 
09968       valid = ast_test_flag(&flags, OPT_SILENT);
09969 
09970       if ((context = strchr(args.argv0, '@')))
09971          *context++ = '\0';
09972 
09973       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09974          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09975       else
09976          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09977 
09978       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09979          skipuser++;
09980       else
09981          valid = 0;
09982    }
09983 
09984    if (!valid)
09985       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09986 
09987    ast_debug(1, "After vm_authenticate\n");
09988 
09989    if (vms.username[0] == '*') {
09990       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09991 
09992       /* user entered '*' */
09993       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09994          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09995          res = 0; /* prevent hangup */
09996          goto out;
09997       }
09998    }
09999 
10000    if (!res) {
10001       valid = 1;
10002       if (!skipuser)
10003          vmu = &vmus;
10004    } else {
10005       res = 0;
10006    }
10007 
10008    /* If ADSI is supported, setup login screen */
10009    adsi_begin(chan, &useadsi);
10010 
10011    ast_test_suite_assert(valid);
10012    if (!valid) {
10013       goto out;
10014    }
10015    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10016 
10017 #ifdef IMAP_STORAGE
10018    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10019    pthread_setspecific(ts_vmstate.key, &vms);
10020 
10021    vms.interactive = 1;
10022    vms.updated = 1;
10023    if (vmu)
10024       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10025    vmstate_insert(&vms);
10026    init_vm_state(&vms);
10027 #endif
10028    
10029    /* Set language from config to override channel language */
10030    if (!ast_strlen_zero(vmu->language))
10031       ast_string_field_set(chan, language, vmu->language);
10032 
10033    /* Retrieve urgent, old and new message counts */
10034    ast_debug(1, "Before open_mailbox\n");
10035    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10036    if (res < 0)
10037       goto out;
10038    vms.oldmessages = vms.lastmsg + 1;
10039    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10040    /* check INBOX */
10041    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10042    if (res < 0)
10043       goto out;
10044    vms.newmessages = vms.lastmsg + 1;
10045    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10046    /* Start in Urgent */
10047    in_urgent = 1;
10048    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10049    if (res < 0)
10050       goto out;
10051    vms.urgentmessages = vms.lastmsg + 1;
10052    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10053 
10054    /* Select proper mailbox FIRST!! */
10055    if (play_auto) {
10056       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10057       if (vms.urgentmessages) {
10058          in_urgent = 1;
10059          res = open_mailbox(&vms, vmu, 11);
10060       } else {
10061          in_urgent = 0;
10062          res = open_mailbox(&vms, vmu, play_folder);
10063       }
10064       if (res < 0)
10065          goto out;
10066 
10067       /* If there are no new messages, inform the user and hangup */
10068       if (vms.lastmsg == -1) {
10069          in_urgent = 0;
10070          cmd = vm_browse_messages(chan, &vms, vmu);
10071          res = 0;
10072          goto out;
10073       }
10074    } else {
10075       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10076          /* If we only have old messages start here */
10077          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10078          in_urgent = 0;
10079          play_folder = 1;
10080          if (res < 0)
10081             goto out;
10082       } else if (!vms.urgentmessages && vms.newmessages) {
10083          /* If we have new messages but none are urgent */
10084          in_urgent = 0;
10085          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10086          if (res < 0)
10087             goto out;
10088       }
10089    }
10090 
10091    if (useadsi)
10092       adsi_status(chan, &vms);
10093    res = 0;
10094 
10095    /* Check to see if this is a new user */
10096    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10097       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10098       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10099          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10100       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10101       if ((cmd == 't') || (cmd == '#')) {
10102          /* Timeout */
10103          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10104          res = 0;
10105          goto out;
10106       } else if (cmd < 0) {
10107          /* Hangup */
10108          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10109          res = -1;
10110          goto out;
10111       }
10112    }
10113 #ifdef IMAP_STORAGE
10114       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10115       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10116          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10117          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10118       }
10119       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10120       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10121          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10122          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10123       }
10124 #endif
10125 
10126    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10127    if (play_auto) {
10128       cmd = '1';
10129    } else {
10130       cmd = vm_intro(chan, vmu, &vms);
10131    }
10132    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10133 
10134    vms.repeats = 0;
10135    vms.starting = 1;
10136    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10137       /* Run main menu */
10138       switch (cmd) {
10139       case '1': /* First message */
10140          vms.curmsg = 0;
10141          /* Fall through */
10142       case '5': /* Play current message */
10143          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10144          cmd = vm_browse_messages(chan, &vms, vmu);
10145          break;
10146       case '2': /* Change folders */
10147          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10148          if (useadsi)
10149             adsi_folders(chan, 0, "Change to folder...");
10150 
10151          cmd = get_folder2(chan, "vm-changeto", 0);
10152          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10153          if (cmd == '#') {
10154             cmd = 0;
10155          } else if (cmd > 0) {
10156             cmd = cmd - '0';
10157             res = close_mailbox(&vms, vmu);
10158             if (res == ERROR_LOCK_PATH)
10159                goto out;
10160             /* If folder is not urgent, set in_urgent to zero! */
10161             if (cmd != 11) in_urgent = 0;
10162             res = open_mailbox(&vms, vmu, cmd);
10163             if (res < 0)
10164                goto out;
10165             play_folder = cmd;
10166             cmd = 0;
10167          }
10168          if (useadsi)
10169             adsi_status2(chan, &vms);
10170 
10171          if (!cmd) {
10172             cmd = vm_play_folder_name(chan, vms.vmbox);
10173          }
10174 
10175          vms.starting = 1;
10176          vms.curmsg = 0;
10177          break;
10178       case '3': /* Advanced options */
10179          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10180          cmd = 0;
10181          vms.repeats = 0;
10182          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10183             switch (cmd) {
10184             case '1': /* Reply */
10185                if (vms.lastmsg > -1 && !vms.starting) {
10186                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10187                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10188                      res = cmd;
10189                      goto out;
10190                   }
10191                } else {
10192                   cmd = ast_play_and_wait(chan, "vm-sorry");
10193                }
10194                cmd = 't';
10195                break;
10196             case '2': /* Callback */
10197                if (!vms.starting)
10198                   ast_verb(3, "Callback Requested\n");
10199                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10200                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10201                   if (cmd == 9) {
10202                      silentexit = 1;
10203                      goto out;
10204                   } else if (cmd == ERROR_LOCK_PATH) {
10205                      res = cmd;
10206                      goto out;
10207                   }
10208                } else {
10209                   cmd = ast_play_and_wait(chan, "vm-sorry");
10210                }
10211                cmd = 't';
10212                break;
10213             case '3': /* Envelope */
10214                if (vms.lastmsg > -1 && !vms.starting) {
10215                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10216                   if (cmd == ERROR_LOCK_PATH) {
10217                      res = cmd;
10218                      goto out;
10219                   }
10220                } else {
10221                   cmd = ast_play_and_wait(chan, "vm-sorry");
10222                }
10223                cmd = 't';
10224                break;
10225             case '4': /* Dialout */
10226                if (!ast_strlen_zero(vmu->dialout)) {
10227                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10228                   if (cmd == 9) {
10229                      silentexit = 1;
10230                      goto out;
10231                   }
10232                } else {
10233                   cmd = ast_play_and_wait(chan, "vm-sorry");
10234                }
10235                cmd = 't';
10236                break;
10237 
10238             case '5': /* Leave VoiceMail */
10239                if (ast_test_flag(vmu, VM_SVMAIL)) {
10240                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10241                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10242                      res = cmd;
10243                      goto out;
10244                   }
10245                } else {
10246                   cmd = ast_play_and_wait(chan, "vm-sorry");
10247                }
10248                cmd = 't';
10249                break;
10250 
10251             case '*': /* Return to main menu */
10252                cmd = 't';
10253                break;
10254 
10255             default:
10256                cmd = 0;
10257                if (!vms.starting) {
10258                   cmd = ast_play_and_wait(chan, "vm-toreply");
10259                }
10260                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10261                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10262                }
10263                if (!cmd && !vms.starting) {
10264                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10265                }
10266                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10267                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10268                }
10269                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10270                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10271                }
10272                if (!cmd) {
10273                   cmd = ast_play_and_wait(chan, "vm-starmain");
10274                }
10275                if (!cmd) {
10276                   cmd = ast_waitfordigit(chan, 6000);
10277                }
10278                if (!cmd) {
10279                   vms.repeats++;
10280                }
10281                if (vms.repeats > 3) {
10282                   cmd = 't';
10283                }
10284                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10285             }
10286          }
10287          if (cmd == 't') {
10288             cmd = 0;
10289             vms.repeats = 0;
10290          }
10291          break;
10292       case '4': /* Go to the previous message */
10293          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10294          if (vms.curmsg > 0) {
10295             vms.curmsg--;
10296             cmd = play_message(chan, vmu, &vms);
10297          } else {
10298             /* Check if we were listening to new
10299                messages.  If so, go to Urgent messages
10300                instead of saying "no more messages"
10301             */
10302             if (in_urgent == 0 && vms.urgentmessages > 0) {
10303                /* Check for Urgent messages */
10304                in_urgent = 1;
10305                res = close_mailbox(&vms, vmu);
10306                if (res == ERROR_LOCK_PATH)
10307                   goto out;
10308                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10309                if (res < 0)
10310                   goto out;
10311                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10312                vms.curmsg = vms.lastmsg;
10313                if (vms.lastmsg < 0) {
10314                   cmd = ast_play_and_wait(chan, "vm-nomore");
10315                }
10316             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10317                vms.curmsg = vms.lastmsg;
10318                cmd = play_message(chan, vmu, &vms);
10319             } else {
10320                cmd = ast_play_and_wait(chan, "vm-nomore");
10321             }
10322          }
10323          break;
10324       case '6': /* Go to the next message */
10325          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10326          if (vms.curmsg < vms.lastmsg) {
10327             vms.curmsg++;
10328             cmd = play_message(chan, vmu, &vms);
10329          } else {
10330             if (in_urgent && vms.newmessages > 0) {
10331                /* Check if we were listening to urgent
10332                 * messages.  If so, go to regular new messages
10333                 * instead of saying "no more messages"
10334                 */
10335                in_urgent = 0;
10336                res = close_mailbox(&vms, vmu);
10337                if (res == ERROR_LOCK_PATH)
10338                   goto out;
10339                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10340                if (res < 0)
10341                   goto out;
10342                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10343                vms.curmsg = -1;
10344                if (vms.lastmsg < 0) {
10345                   cmd = ast_play_and_wait(chan, "vm-nomore");
10346                }
10347             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10348                vms.curmsg = 0;
10349                cmd = play_message(chan, vmu, &vms);
10350             } else {
10351                cmd = ast_play_and_wait(chan, "vm-nomore");
10352             }
10353          }
10354          break;
10355       case '7': /* Delete the current message */
10356          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10357             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10358             if (useadsi)
10359                adsi_delete(chan, &vms);
10360             if (vms.deleted[vms.curmsg]) {
10361                if (play_folder == 0) {
10362                   if (in_urgent) {
10363                      vms.urgentmessages--;
10364                   } else {
10365                      vms.newmessages--;
10366                   }
10367                }
10368                else if (play_folder == 1)
10369                   vms.oldmessages--;
10370                cmd = ast_play_and_wait(chan, "vm-deleted");
10371             } else {
10372                if (play_folder == 0) {
10373                   if (in_urgent) {
10374                      vms.urgentmessages++;
10375                   } else {
10376                      vms.newmessages++;
10377                   }
10378                }
10379                else if (play_folder == 1)
10380                   vms.oldmessages++;
10381                cmd = ast_play_and_wait(chan, "vm-undeleted");
10382             }
10383             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10384                if (vms.curmsg < vms.lastmsg) {
10385                   vms.curmsg++;
10386                   cmd = play_message(chan, vmu, &vms);
10387                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10388                   vms.curmsg = 0;
10389                   cmd = play_message(chan, vmu, &vms);
10390                } else {
10391                   /* Check if we were listening to urgent
10392                      messages.  If so, go to regular new messages
10393                      instead of saying "no more messages"
10394                   */
10395                   if (in_urgent == 1) {
10396                      /* Check for new messages */
10397                      in_urgent = 0;
10398                      res = close_mailbox(&vms, vmu);
10399                      if (res == ERROR_LOCK_PATH)
10400                         goto out;
10401                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10402                      if (res < 0)
10403                         goto out;
10404                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10405                      vms.curmsg = -1;
10406                      if (vms.lastmsg < 0) {
10407                         cmd = ast_play_and_wait(chan, "vm-nomore");
10408                      }
10409                   } else {
10410                      cmd = ast_play_and_wait(chan, "vm-nomore");
10411                   }
10412                }
10413             }
10414          } else /* Delete not valid if we haven't selected a message */
10415             cmd = 0;
10416 #ifdef IMAP_STORAGE
10417          deleted = 1;
10418 #endif
10419          break;
10420    
10421       case '8': /* Forward the current message */
10422          if (vms.lastmsg > -1) {
10423             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10424             if (cmd == ERROR_LOCK_PATH) {
10425                res = cmd;
10426                goto out;
10427             }
10428          } else {
10429             /* Check if we were listening to urgent
10430                messages.  If so, go to regular new messages
10431                instead of saying "no more messages"
10432             */
10433             if (in_urgent == 1 && vms.newmessages > 0) {
10434                /* Check for new messages */
10435                in_urgent = 0;
10436                res = close_mailbox(&vms, vmu);
10437                if (res == ERROR_LOCK_PATH)
10438                   goto out;
10439                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10440                if (res < 0)
10441                   goto out;
10442                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10443                vms.curmsg = -1;
10444                if (vms.lastmsg < 0) {
10445                   cmd = ast_play_and_wait(chan, "vm-nomore");
10446                }
10447             } else {
10448                cmd = ast_play_and_wait(chan, "vm-nomore");
10449             }
10450          }
10451          break;
10452       case '9': /* Save message to folder */
10453          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10454          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10455             /* No message selected */
10456             cmd = 0;
10457             break;
10458          }
10459          if (useadsi)
10460             adsi_folders(chan, 1, "Save to folder...");
10461          cmd = get_folder2(chan, "vm-savefolder", 1);
10462          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10463          box = 0; /* Shut up compiler */
10464          if (cmd == '#') {
10465             cmd = 0;
10466             break;
10467          } else if (cmd > 0) {
10468             box = cmd = cmd - '0';
10469             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10470             if (cmd == ERROR_LOCK_PATH) {
10471                res = cmd;
10472                goto out;
10473 #ifndef IMAP_STORAGE
10474             } else if (!cmd) {
10475                vms.deleted[vms.curmsg] = 1;
10476 #endif
10477             } else {
10478                vms.deleted[vms.curmsg] = 0;
10479                vms.heard[vms.curmsg] = 0;
10480             }
10481          }
10482          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10483          if (useadsi)
10484             adsi_message(chan, &vms);
10485          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10486          if (!cmd) {
10487             cmd = ast_play_and_wait(chan, "vm-message");
10488             if (!cmd) 
10489                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10490             if (!cmd)
10491                cmd = ast_play_and_wait(chan, "vm-savedto");
10492             if (!cmd)
10493                cmd = vm_play_folder_name(chan, vms.fn);
10494          } else {
10495             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10496          }
10497          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10498             if (vms.curmsg < vms.lastmsg) {
10499                vms.curmsg++;
10500                cmd = play_message(chan, vmu, &vms);
10501             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10502                vms.curmsg = 0;
10503                cmd = play_message(chan, vmu, &vms);
10504             } else {
10505                /* Check if we were listening to urgent
10506                   messages.  If so, go to regular new messages
10507                   instead of saying "no more messages"
10508                */
10509                if (in_urgent == 1 && vms.newmessages > 0) {
10510                   /* Check for new messages */
10511                   in_urgent = 0;
10512                   res = close_mailbox(&vms, vmu);
10513                   if (res == ERROR_LOCK_PATH)
10514                      goto out;
10515                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10516                   if (res < 0)
10517                      goto out;
10518                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10519                   vms.curmsg = -1;
10520                   if (vms.lastmsg < 0) {
10521                      cmd = ast_play_and_wait(chan, "vm-nomore");
10522                   }
10523                } else {
10524                   cmd = ast_play_and_wait(chan, "vm-nomore");
10525                }
10526             }
10527          }
10528          break;
10529       case '*': /* Help */
10530          if (!vms.starting) {
10531             cmd = ast_play_and_wait(chan, "vm-onefor");
10532             if (!strncasecmp(chan->language, "he", 2)) {
10533                cmd = ast_play_and_wait(chan, "vm-for");
10534             }
10535             if (!cmd)
10536                cmd = vm_play_folder_name(chan, vms.vmbox);
10537             if (!cmd)
10538                cmd = ast_play_and_wait(chan, "vm-opts");
10539             if (!cmd)
10540                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10541          } else
10542             cmd = 0;
10543          break;
10544       case '0': /* Mailbox options */
10545          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10546          if (useadsi)
10547             adsi_status(chan, &vms);
10548          break;
10549       default: /* Nothing */
10550          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10551          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10552          break;
10553       }
10554    }
10555    if ((cmd == 't') || (cmd == '#')) {
10556       /* Timeout */
10557       res = 0;
10558    } else {
10559       /* Hangup */
10560       res = -1;
10561    }
10562 
10563 out:
10564    if (res > -1) {
10565       ast_stopstream(chan);
10566       adsi_goodbye(chan);
10567       if (valid && res != OPERATOR_EXIT) {
10568          if (silentexit)
10569             res = ast_play_and_wait(chan, "vm-dialout");
10570          else 
10571             res = ast_play_and_wait(chan, "vm-goodbye");
10572       }
10573       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10574          res = 0;
10575       }
10576       if (useadsi)
10577          ast_adsi_unload_session(chan);
10578    }
10579    if (vmu)
10580       close_mailbox(&vms, vmu);
10581    if (valid) {
10582       int new = 0, old = 0, urgent = 0;
10583       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10584       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10585       /* Urgent flag not passwd to externnotify here */
10586       run_externnotify(vmu->context, vmu->mailbox, NULL);
10587       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10588       queue_mwi_event(ext_context, urgent, new, old);
10589    }
10590 #ifdef IMAP_STORAGE
10591    /* expunge message - use UID Expunge if supported on IMAP server*/
10592    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10593    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10594       ast_mutex_lock(&vms.lock);
10595 #ifdef HAVE_IMAP_TK2006
10596       if (LEVELUIDPLUS (vms.mailstream)) {
10597          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10598       } else 
10599 #endif
10600          mail_expunge(vms.mailstream);
10601       ast_mutex_unlock(&vms.lock);
10602    }
10603    /*  before we delete the state, we should copy pertinent info
10604     *  back to the persistent model */
10605    if (vmu) {
10606       vmstate_delete(&vms);
10607    }
10608 #endif
10609    if (vmu)
10610       free_user(vmu);
10611 
10612 #ifdef IMAP_STORAGE
10613    pthread_setspecific(ts_vmstate.key, NULL);
10614 #endif
10615    return res;
10616 }
10617 
10618 static int vm_exec(struct ast_channel *chan, const char *data)
10619 {
10620    int res = 0;
10621    char *tmp;
10622    struct leave_vm_options leave_options;
10623    struct ast_flags flags = { 0 };
10624    char *opts[OPT_ARG_ARRAY_SIZE];
10625    AST_DECLARE_APP_ARGS(args,
10626       AST_APP_ARG(argv0);
10627       AST_APP_ARG(argv1);
10628    );
10629    
10630    memset(&leave_options, 0, sizeof(leave_options));
10631 
10632    if (chan->_state != AST_STATE_UP)
10633       ast_answer(chan);
10634 
10635    if (!ast_strlen_zero(data)) {
10636       tmp = ast_strdupa(data);
10637       AST_STANDARD_APP_ARGS(args, tmp);
10638       if (args.argc == 2) {
10639          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10640             return -1;
10641          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10642          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10643             int gain;
10644 
10645             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10646                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10647                return -1;
10648             } else {
10649                leave_options.record_gain = (signed char) gain;
10650             }
10651          }
10652          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10653             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10654                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10655          }
10656       }
10657    } else {
10658       char temp[256];
10659       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10660       if (res < 0)
10661          return res;
10662       if (ast_strlen_zero(temp))
10663          return 0;
10664       args.argv0 = ast_strdupa(temp);
10665    }
10666 
10667    res = leave_voicemail(chan, args.argv0, &leave_options);
10668    if (res == 't') {
10669       ast_play_and_wait(chan, "vm-goodbye");
10670       res = 0;
10671    }
10672 
10673    if (res == OPERATOR_EXIT) {
10674       res = 0;
10675    }
10676 
10677    if (res == ERROR_LOCK_PATH) {
10678       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10679       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10680       res = 0;
10681    }
10682 
10683    return res;
10684 }
10685 
10686 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10687 {
10688    struct ast_vm_user *vmu;
10689 
10690    if (!ast_strlen_zero(box) && box[0] == '*') {
10691       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10692             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10693             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10694             "\n\tand will be ignored.\n", box, context);
10695       return NULL;
10696    }
10697 
10698    AST_LIST_TRAVERSE(&users, vmu, list) {
10699       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10700          if (strcasecmp(vmu->context, context)) {
10701             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10702                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10703                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10704                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10705          }
10706          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10707          return NULL;
10708       }
10709       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10710          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10711          return NULL;
10712       }
10713    }
10714    
10715    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10716       return NULL;
10717    
10718    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10719    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10720 
10721    AST_LIST_INSERT_TAIL(&users, vmu, list);
10722    
10723    return vmu;
10724 }
10725 
10726 static int append_mailbox(const char *context, const char *box, const char *data)
10727 {
10728    /* Assumes lock is already held */
10729    char *tmp;
10730    char *stringp;
10731    char *s;
10732    struct ast_vm_user *vmu;
10733    char *mailbox_full;
10734    int new = 0, old = 0, urgent = 0;
10735    char secretfn[PATH_MAX] = "";
10736 
10737    tmp = ast_strdupa(data);
10738 
10739    if (!(vmu = find_or_create(context, box)))
10740       return -1;
10741 
10742    populate_defaults(vmu);
10743 
10744    stringp = tmp;
10745    if ((s = strsep(&stringp, ","))) {
10746       if (!ast_strlen_zero(s) && s[0] == '*') {
10747          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10748             "\n\tmust be reset in voicemail.conf.\n", box);
10749       }
10750       /* assign password regardless of validity to prevent NULL password from being assigned */
10751       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10752    }
10753    if (stringp && (s = strsep(&stringp, ","))) {
10754       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10755    }
10756    if (stringp && (s = strsep(&stringp, ","))) {
10757       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10758    }
10759    if (stringp && (s = strsep(&stringp, ","))) {
10760       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10761    }
10762    if (stringp && (s = strsep(&stringp, ","))) {
10763       apply_options(vmu, s);
10764    }
10765 
10766    switch (vmu->passwordlocation) {
10767    case OPT_PWLOC_SPOOLDIR:
10768       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10769       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10770    }
10771 
10772    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10773    strcpy(mailbox_full, box);
10774    strcat(mailbox_full, "@");
10775    strcat(mailbox_full, context);
10776 
10777    inboxcount2(mailbox_full, &urgent, &new, &old);
10778    queue_mwi_event(mailbox_full, urgent, new, old);
10779 
10780    return 0;
10781 }
10782 
10783 AST_TEST_DEFINE(test_voicemail_vmuser)
10784 {
10785    int res = 0;
10786    struct ast_vm_user *vmu;
10787    /* language parameter seems to only be used for display in manager action */
10788    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10789       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10790       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10791       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10792       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10793       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10794       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10795       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10796       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10797 #ifdef IMAP_STORAGE
10798    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10799       "imapfolder=INBOX|imapvmshareid=6000";
10800 #endif
10801 
10802    switch (cmd) {
10803    case TEST_INIT:
10804       info->name = "vmuser";
10805       info->category = "/apps/app_voicemail/";
10806       info->summary = "Vmuser unit test";
10807       info->description =
10808          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10809       return AST_TEST_NOT_RUN;
10810    case TEST_EXECUTE:
10811       break;
10812    }
10813 
10814    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10815       return AST_TEST_NOT_RUN;
10816    }
10817    populate_defaults(vmu);
10818    ast_set_flag(vmu, VM_ALLOCED);
10819 
10820    apply_options(vmu, options_string);
10821 
10822    if (!ast_test_flag(vmu, VM_ATTACH)) {
10823       ast_test_status_update(test, "Parse failure for attach option\n");
10824       res = 1;
10825    }
10826    if (strcasecmp(vmu->attachfmt, "wav49")) {
10827       ast_test_status_update(test, "Parse failure for attachftm option\n");
10828       res = 1;
10829    }
10830    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10831       ast_test_status_update(test, "Parse failure for serveremail option\n");
10832       res = 1;
10833    }
10834    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10835       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10836       res = 1;
10837    }
10838    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10839       ast_test_status_update(test, "Parse failure for emailbody option\n");
10840       res = 1;
10841    }
10842    if (strcasecmp(vmu->zonetag, "central")) {
10843       ast_test_status_update(test, "Parse failure for tz option\n");
10844       res = 1;
10845    }
10846    if (!ast_test_flag(vmu, VM_DELETE)) {
10847       ast_test_status_update(test, "Parse failure for delete option\n");
10848       res = 1;
10849    }
10850    if (!ast_test_flag(vmu, VM_SAYCID)) {
10851       ast_test_status_update(test, "Parse failure for saycid option\n");
10852       res = 1;
10853    }
10854    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10855       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10856       res = 1;
10857    }
10858    if (!ast_test_flag(vmu, VM_REVIEW)) {
10859       ast_test_status_update(test, "Parse failure for review option\n");
10860       res = 1;
10861    }
10862    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10863       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10864       res = 1;
10865    }
10866    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10867       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10868       res = 1;
10869    }
10870    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10871       ast_test_status_update(test, "Parse failure for operator option\n");
10872       res = 1;
10873    }
10874    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10875       ast_test_status_update(test, "Parse failure for envelope option\n");
10876       res = 1;
10877    }
10878    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10879       ast_test_status_update(test, "Parse failure for moveheard option\n");
10880       res = 1;
10881    }
10882    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10883       ast_test_status_update(test, "Parse failure for sayduration option\n");
10884       res = 1;
10885    }
10886    if (vmu->saydurationm != 5) {
10887       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10888       res = 1;
10889    }
10890    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10891       ast_test_status_update(test, "Parse failure for forcename option\n");
10892       res = 1;
10893    }
10894    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10895       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10896       res = 1;
10897    }
10898    if (strcasecmp(vmu->callback, "somecontext")) {
10899       ast_test_status_update(test, "Parse failure for callbacks option\n");
10900       res = 1;
10901    }
10902    if (strcasecmp(vmu->dialout, "somecontext2")) {
10903       ast_test_status_update(test, "Parse failure for dialout option\n");
10904       res = 1;
10905    }
10906    if (strcasecmp(vmu->exit, "somecontext3")) {
10907       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10908       res = 1;
10909    }
10910    if (vmu->minsecs != 10) {
10911       ast_test_status_update(test, "Parse failure for minsecs option\n");
10912       res = 1;
10913    }
10914    if (vmu->maxsecs != 100) {
10915       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10916       res = 1;
10917    }
10918    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10919       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10920       res = 1;
10921    }
10922    if (vmu->maxdeletedmsg != 50) {
10923       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10924       res = 1;
10925    }
10926    if (vmu->volgain != 1.3) {
10927       ast_test_status_update(test, "Parse failure for volgain option\n");
10928       res = 1;
10929    }
10930    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10931       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10932       res = 1;
10933    }
10934 #ifdef IMAP_STORAGE
10935    apply_options(vmu, option_string2);
10936 
10937    if (strcasecmp(vmu->imapuser, "imapuser")) {
10938       ast_test_status_update(test, "Parse failure for imapuser option\n");
10939       res = 1;
10940    }
10941    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10942       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10943       res = 1;
10944    }
10945    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10946       ast_test_status_update(test, "Parse failure for imapfolder option\n");
10947       res = 1;
10948    }
10949    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10950       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10951       res = 1;
10952    }
10953 #endif
10954 
10955    free_user(vmu);
10956    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10957 }
10958 
10959 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10960 {
10961    struct ast_vm_user svm;
10962    char *context, *box;
10963    AST_DECLARE_APP_ARGS(args,
10964       AST_APP_ARG(mbox);
10965       AST_APP_ARG(options);
10966    );
10967    static int dep_warning = 0;
10968 
10969    if (ast_strlen_zero(data)) {
10970       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10971       return -1;
10972    }
10973 
10974    if (!dep_warning) {
10975       dep_warning = 1;
10976       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10977    }
10978 
10979    box = ast_strdupa(data);
10980 
10981    AST_STANDARD_APP_ARGS(args, box);
10982 
10983    if (args.options) {
10984    }
10985 
10986    if ((context = strchr(args.mbox, '@'))) {
10987       *context = '\0';
10988       context++;
10989    }
10990 
10991    if (find_user(&svm, context, args.mbox)) {
10992       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10993    } else
10994       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10995 
10996    return 0;
10997 }
10998 
10999 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11000 {
11001    struct ast_vm_user svm;
11002    AST_DECLARE_APP_ARGS(arg,
11003       AST_APP_ARG(mbox);
11004       AST_APP_ARG(context);
11005    );
11006 
11007    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11008 
11009    if (ast_strlen_zero(arg.mbox)) {
11010       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11011       return -1;
11012    }
11013 
11014    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11015    return 0;
11016 }
11017 
11018 static struct ast_custom_function mailbox_exists_acf = {
11019    .name = "MAILBOX_EXISTS",
11020    .read = acf_mailbox_exists,
11021 };
11022 
11023 static int vmauthenticate(struct ast_channel *chan, const char *data)
11024 {
11025    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11026    struct ast_vm_user vmus;
11027    char *options = NULL;
11028    int silent = 0, skipuser = 0;
11029    int res = -1;
11030    
11031    if (data) {
11032       s = ast_strdupa(data);
11033       user = strsep(&s, ",");
11034       options = strsep(&s, ",");
11035       if (user) {
11036          s = user;
11037          user = strsep(&s, "@");
11038          context = strsep(&s, "");
11039          if (!ast_strlen_zero(user))
11040             skipuser++;
11041          ast_copy_string(mailbox, user, sizeof(mailbox));
11042       }
11043    }
11044 
11045    if (options) {
11046       silent = (strchr(options, 's')) != NULL;
11047    }
11048 
11049    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11050       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11051       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11052       ast_play_and_wait(chan, "auth-thankyou");
11053       res = 0;
11054    } else if (mailbox[0] == '*') {
11055       /* user entered '*' */
11056       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11057          res = 0; /* prevent hangup */
11058       }
11059    }
11060 
11061    return res;
11062 }
11063 
11064 static char *show_users_realtime(int fd, const char *context)
11065 {
11066    struct ast_config *cfg;
11067    const char *cat = NULL;
11068 
11069    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11070       "context", context, SENTINEL))) {
11071       return CLI_FAILURE;
11072    }
11073 
11074    ast_cli(fd,
11075       "\n"
11076       "=============================================================\n"
11077       "=== Configured Voicemail Users ==============================\n"
11078       "=============================================================\n"
11079       "===\n");
11080 
11081    while ((cat = ast_category_browse(cfg, cat))) {
11082       struct ast_variable *var = NULL;
11083       ast_cli(fd,
11084          "=== Mailbox ...\n"
11085          "===\n");
11086       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11087          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11088       ast_cli(fd,
11089          "===\n"
11090          "=== ---------------------------------------------------------\n"
11091          "===\n");
11092    }
11093 
11094    ast_cli(fd,
11095       "=============================================================\n"
11096       "\n");
11097 
11098    ast_config_destroy(cfg);
11099 
11100    return CLI_SUCCESS;
11101 }
11102 
11103 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11104 {
11105    int which = 0;
11106    int wordlen;
11107    struct ast_vm_user *vmu;
11108    const char *context = "";
11109 
11110    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11111    if (pos > 4)
11112       return NULL;
11113    if (pos == 3)
11114       return (state == 0) ? ast_strdup("for") : NULL;
11115    wordlen = strlen(word);
11116    AST_LIST_TRAVERSE(&users, vmu, list) {
11117       if (!strncasecmp(word, vmu->context, wordlen)) {
11118          if (context && strcmp(context, vmu->context) && ++which > state)
11119             return ast_strdup(vmu->context);
11120          /* ignore repeated contexts ? */
11121          context = vmu->context;
11122       }
11123    }
11124    return NULL;
11125 }
11126 
11127 /*! \brief Show a list of voicemail users in the CLI */
11128 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11129 {
11130    struct ast_vm_user *vmu;
11131 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11132    const char *context = NULL;
11133    int users_counter = 0;
11134 
11135    switch (cmd) {
11136    case CLI_INIT:
11137       e->command = "voicemail show users";
11138       e->usage =
11139          "Usage: voicemail show users [for <context>]\n"
11140          "       Lists all mailboxes currently set up\n";
11141       return NULL;
11142    case CLI_GENERATE:
11143       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11144    }  
11145 
11146    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11147       return CLI_SHOWUSAGE;
11148    if (a->argc == 5) {
11149       if (strcmp(a->argv[3],"for"))
11150          return CLI_SHOWUSAGE;
11151       context = a->argv[4];
11152    }
11153 
11154    if (ast_check_realtime("voicemail")) {
11155       if (!context) {
11156          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11157          return CLI_SHOWUSAGE;
11158       }
11159       return show_users_realtime(a->fd, context);
11160    }
11161 
11162    AST_LIST_LOCK(&users);
11163    if (AST_LIST_EMPTY(&users)) {
11164       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11165       AST_LIST_UNLOCK(&users);
11166       return CLI_FAILURE;
11167    }
11168    if (!context) {
11169       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11170    } else {
11171       int count = 0;
11172       AST_LIST_TRAVERSE(&users, vmu, list) {
11173          if (!strcmp(context, vmu->context)) {
11174             count++;
11175             break;
11176          }
11177       }
11178       if (count) {
11179          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11180       } else {
11181          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11182          AST_LIST_UNLOCK(&users);
11183          return CLI_FAILURE;
11184       }
11185    }
11186    AST_LIST_TRAVERSE(&users, vmu, list) {
11187       int newmsgs = 0, oldmsgs = 0;
11188       char count[12], tmp[256] = "";
11189 
11190       if (!context || !strcmp(context, vmu->context)) {
11191          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11192          inboxcount(tmp, &newmsgs, &oldmsgs);
11193          snprintf(count, sizeof(count), "%d", newmsgs);
11194          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11195          users_counter++;
11196       }
11197    }
11198    AST_LIST_UNLOCK(&users);
11199    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11200    return CLI_SUCCESS;
11201 }
11202 
11203 /*! \brief Show a list of voicemail zones in the CLI */
11204 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11205 {
11206    struct vm_zone *zone;
11207 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11208    char *res = CLI_SUCCESS;
11209 
11210    switch (cmd) {
11211    case CLI_INIT:
11212       e->command = "voicemail show zones";
11213       e->usage =
11214          "Usage: voicemail show zones\n"
11215          "       Lists zone message formats\n";
11216       return NULL;
11217    case CLI_GENERATE:
11218       return NULL;
11219    }
11220 
11221    if (a->argc != 3)
11222       return CLI_SHOWUSAGE;
11223 
11224    AST_LIST_LOCK(&zones);
11225    if (!AST_LIST_EMPTY(&zones)) {
11226       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11227       AST_LIST_TRAVERSE(&zones, zone, list) {
11228          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11229       }
11230    } else {
11231       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11232       res = CLI_FAILURE;
11233    }
11234    AST_LIST_UNLOCK(&zones);
11235 
11236    return res;
11237 }
11238 
11239 /*! \brief Reload voicemail configuration from the CLI */
11240 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11241 {
11242    switch (cmd) {
11243    case CLI_INIT:
11244       e->command = "voicemail reload";
11245       e->usage =
11246          "Usage: voicemail reload\n"
11247          "       Reload voicemail configuration\n";
11248       return NULL;
11249    case CLI_GENERATE:
11250       return NULL;
11251    }
11252 
11253    if (a->argc != 2)
11254       return CLI_SHOWUSAGE;
11255 
11256    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11257    load_config(1);
11258    
11259    return CLI_SUCCESS;
11260 }
11261 
11262 static struct ast_cli_entry cli_voicemail[] = {
11263    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11264    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11265    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11266 };
11267 
11268 #ifdef IMAP_STORAGE
11269    #define DATA_EXPORT_VM_USERS(USER)              \
11270       USER(ast_vm_user, context, AST_DATA_STRING)        \
11271       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11272       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11273       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11274       USER(ast_vm_user, email, AST_DATA_STRING)       \
11275       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11276       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11277       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11278       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11279       USER(ast_vm_user, language, AST_DATA_STRING)       \
11280       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11281       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11282       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11283       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11284       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11285       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11286       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11287       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11288       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11289       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11290       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11291       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11292       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11293       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11294       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11295 #else
11296    #define DATA_EXPORT_VM_USERS(USER)              \
11297       USER(ast_vm_user, context, AST_DATA_STRING)        \
11298       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11299       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11300       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11301       USER(ast_vm_user, email, AST_DATA_STRING)       \
11302       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11303       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11304       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11305       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11306       USER(ast_vm_user, language, AST_DATA_STRING)       \
11307       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11308       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11309       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11310       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11311       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11312       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11313       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11314       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11315       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11316       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11317       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11318       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11319 #endif
11320 
11321 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11322 
11323 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11324    ZONE(vm_zone, name, AST_DATA_STRING)      \
11325    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11326    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11327 
11328 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11329 
11330 /*!
11331  * \internal
11332  * \brief Add voicemail user to the data_root.
11333  * \param[in] search The search tree.
11334  * \param[in] data_root The main result node.
11335  * \param[in] user The voicemail user.
11336  */
11337 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11338     struct ast_data *data_root, struct ast_vm_user *user)
11339 {
11340    struct ast_data *data_user, *data_zone;
11341    struct ast_data *data_state;
11342    struct vm_zone *zone = NULL;
11343    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11344    char ext_context[256] = "";
11345 
11346    data_user = ast_data_add_node(data_root, "user");
11347    if (!data_user) {
11348       return -1;
11349    }
11350 
11351    ast_data_add_structure(ast_vm_user, data_user, user);
11352 
11353    AST_LIST_LOCK(&zones);
11354    AST_LIST_TRAVERSE(&zones, zone, list) {
11355       if (!strcmp(zone->name, user->zonetag)) {
11356          break;
11357       }
11358    }
11359    AST_LIST_UNLOCK(&zones);
11360 
11361    /* state */
11362    data_state = ast_data_add_node(data_user, "state");
11363    if (!data_state) {
11364       return -1;
11365    }
11366    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11367    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11368    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11369    ast_data_add_int(data_state, "newmsg", newmsg);
11370    ast_data_add_int(data_state, "oldmsg", oldmsg);
11371 
11372    if (zone) {
11373       data_zone = ast_data_add_node(data_user, "zone");
11374       ast_data_add_structure(vm_zone, data_zone, zone);
11375    }
11376 
11377    if (!ast_data_search_match(search, data_user)) {
11378       ast_data_remove_node(data_root, data_user);
11379    }
11380 
11381    return 0;
11382 }
11383 
11384 static int vm_users_data_provider_get(const struct ast_data_search *search,
11385    struct ast_data *data_root)
11386 {
11387    struct ast_vm_user *user;
11388 
11389    AST_LIST_LOCK(&users);
11390    AST_LIST_TRAVERSE(&users, user, list) {
11391       vm_users_data_provider_get_helper(search, data_root, user);
11392    }
11393    AST_LIST_UNLOCK(&users);
11394 
11395    return 0;
11396 }
11397 
11398 static const struct ast_data_handler vm_users_data_provider = {
11399    .version = AST_DATA_HANDLER_VERSION,
11400    .get = vm_users_data_provider_get
11401 };
11402 
11403 static const struct ast_data_entry vm_data_providers[] = {
11404    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11405 };
11406 
11407 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11408 {
11409    int new = 0, old = 0, urgent = 0;
11410 
11411    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11412 
11413    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11414       mwi_sub->old_urgent = urgent;
11415       mwi_sub->old_new = new;
11416       mwi_sub->old_old = old;
11417       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11418       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11419    }
11420 }
11421 
11422 static void poll_subscribed_mailboxes(void)
11423 {
11424    struct mwi_sub *mwi_sub;
11425 
11426    AST_RWLIST_RDLOCK(&mwi_subs);
11427    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11428       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11429          poll_subscribed_mailbox(mwi_sub);
11430       }
11431    }
11432    AST_RWLIST_UNLOCK(&mwi_subs);
11433 }
11434 
11435 static void *mb_poll_thread(void *data)
11436 {
11437    while (poll_thread_run) {
11438       struct timespec ts = { 0, };
11439       struct timeval wait;
11440 
11441       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11442       ts.tv_sec = wait.tv_sec;
11443       ts.tv_nsec = wait.tv_usec * 1000;
11444 
11445       ast_mutex_lock(&poll_lock);
11446       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11447       ast_mutex_unlock(&poll_lock);
11448 
11449       if (!poll_thread_run)
11450          break;
11451 
11452       poll_subscribed_mailboxes();
11453    }
11454 
11455    return NULL;
11456 }
11457 
11458 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11459 {
11460    ast_free(mwi_sub);
11461 }
11462 
11463 static int handle_unsubscribe(void *datap)
11464 {
11465    struct mwi_sub *mwi_sub;
11466    uint32_t *uniqueid = datap;
11467    
11468    AST_RWLIST_WRLOCK(&mwi_subs);
11469    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11470       if (mwi_sub->uniqueid == *uniqueid) {
11471          AST_LIST_REMOVE_CURRENT(entry);
11472          break;
11473       }
11474    }
11475    AST_RWLIST_TRAVERSE_SAFE_END
11476    AST_RWLIST_UNLOCK(&mwi_subs);
11477 
11478    if (mwi_sub)
11479       mwi_sub_destroy(mwi_sub);
11480 
11481    ast_free(uniqueid);  
11482    return 0;
11483 }
11484 
11485 static int handle_subscribe(void *datap)
11486 {
11487    unsigned int len;
11488    struct mwi_sub *mwi_sub;
11489    struct mwi_sub_task *p = datap;
11490 
11491    len = sizeof(*mwi_sub);
11492    if (!ast_strlen_zero(p->mailbox))
11493       len += strlen(p->mailbox);
11494 
11495    if (!ast_strlen_zero(p->context))
11496       len += strlen(p->context) + 1; /* Allow for seperator */
11497 
11498    if (!(mwi_sub = ast_calloc(1, len)))
11499       return -1;
11500 
11501    mwi_sub->uniqueid = p->uniqueid;
11502    if (!ast_strlen_zero(p->mailbox))
11503       strcpy(mwi_sub->mailbox, p->mailbox);
11504 
11505    if (!ast_strlen_zero(p->context)) {
11506       strcat(mwi_sub->mailbox, "@");
11507       strcat(mwi_sub->mailbox, p->context);
11508    }
11509 
11510    AST_RWLIST_WRLOCK(&mwi_subs);
11511    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11512    AST_RWLIST_UNLOCK(&mwi_subs);
11513    ast_free((void *) p->mailbox);
11514    ast_free((void *) p->context);
11515    ast_free(p);
11516    poll_subscribed_mailbox(mwi_sub);
11517    return 0;
11518 }
11519 
11520 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11521 {
11522    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11523 
11524    if (!uniqueid) {
11525       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11526       return;
11527    }
11528 
11529    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11530       ast_free(uniqueid);
11531       return;
11532    }
11533 
11534    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11535       ast_free(uniqueid);
11536       return;
11537    }
11538 
11539    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11540    *uniqueid = u;
11541    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11542       ast_free(uniqueid);
11543    }
11544 }
11545 
11546 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11547 {
11548    struct mwi_sub_task *mwist;
11549    
11550    if (ast_event_get_type(event) != AST_EVENT_SUB)
11551       return;
11552 
11553    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11554       return;
11555 
11556    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11557       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11558       return;
11559    }
11560    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11561    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11562    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11563    
11564    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11565       ast_free(mwist);
11566    }
11567 }
11568 
11569 static void start_poll_thread(void)
11570 {
11571    int errcode;
11572    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11573       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11574       AST_EVENT_IE_END);
11575 
11576    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11577       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11578       AST_EVENT_IE_END);
11579 
11580    if (mwi_sub_sub)
11581       ast_event_report_subs(mwi_sub_sub);
11582 
11583    poll_thread_run = 1;
11584 
11585    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11586       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11587    }
11588 }
11589 
11590 static void stop_poll_thread(void)
11591 {
11592    poll_thread_run = 0;
11593 
11594    if (mwi_sub_sub) {
11595       ast_event_unsubscribe(mwi_sub_sub);
11596       mwi_sub_sub = NULL;
11597    }
11598 
11599    if (mwi_unsub_sub) {
11600       ast_event_unsubscribe(mwi_unsub_sub);
11601       mwi_unsub_sub = NULL;
11602    }
11603 
11604    ast_mutex_lock(&poll_lock);
11605    ast_cond_signal(&poll_cond);
11606    ast_mutex_unlock(&poll_lock);
11607 
11608    pthread_join(poll_thread, NULL);
11609 
11610    poll_thread = AST_PTHREADT_NULL;
11611 }
11612 
11613 /*! \brief Manager list voicemail users command */
11614 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11615 {
11616    struct ast_vm_user *vmu = NULL;
11617    const char *id = astman_get_header(m, "ActionID");
11618    char actionid[128] = "";
11619 
11620    if (!ast_strlen_zero(id))
11621       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11622 
11623    AST_LIST_LOCK(&users);
11624 
11625    if (AST_LIST_EMPTY(&users)) {
11626       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11627       AST_LIST_UNLOCK(&users);
11628       return RESULT_SUCCESS;
11629    }
11630    
11631    astman_send_ack(s, m, "Voicemail user list will follow");
11632    
11633    AST_LIST_TRAVERSE(&users, vmu, list) {
11634       char dirname[256];
11635 
11636 #ifdef IMAP_STORAGE
11637       int new, old;
11638       inboxcount(vmu->mailbox, &new, &old);
11639 #endif
11640       
11641       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11642       astman_append(s,
11643          "%s"
11644          "Event: VoicemailUserEntry\r\n"
11645          "VMContext: %s\r\n"
11646          "VoiceMailbox: %s\r\n"
11647          "Fullname: %s\r\n"
11648          "Email: %s\r\n"
11649          "Pager: %s\r\n"
11650          "ServerEmail: %s\r\n"
11651          "MailCommand: %s\r\n"
11652          "Language: %s\r\n"
11653          "TimeZone: %s\r\n"
11654          "Callback: %s\r\n"
11655          "Dialout: %s\r\n"
11656          "UniqueID: %s\r\n"
11657          "ExitContext: %s\r\n"
11658          "SayDurationMinimum: %d\r\n"
11659          "SayEnvelope: %s\r\n"
11660          "SayCID: %s\r\n"
11661          "AttachMessage: %s\r\n"
11662          "AttachmentFormat: %s\r\n"
11663          "DeleteMessage: %s\r\n"
11664          "VolumeGain: %.2f\r\n"
11665          "CanReview: %s\r\n"
11666          "CallOperator: %s\r\n"
11667          "MaxMessageCount: %d\r\n"
11668          "MaxMessageLength: %d\r\n"
11669          "NewMessageCount: %d\r\n"
11670 #ifdef IMAP_STORAGE
11671          "OldMessageCount: %d\r\n"
11672          "IMAPUser: %s\r\n"
11673 #endif
11674          "\r\n",
11675          actionid,
11676          vmu->context,
11677          vmu->mailbox,
11678          vmu->fullname,
11679          vmu->email,
11680          vmu->pager,
11681          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11682          mailcmd,
11683          vmu->language,
11684          vmu->zonetag,
11685          vmu->callback,
11686          vmu->dialout,
11687          vmu->uniqueid,
11688          vmu->exit,
11689          vmu->saydurationm,
11690          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11691          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11692          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11693          vmu->attachfmt,
11694          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11695          vmu->volgain,
11696          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11697          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11698          vmu->maxmsg,
11699          vmu->maxsecs,
11700 #ifdef IMAP_STORAGE
11701          new, old, vmu->imapuser
11702 #else
11703          count_messages(vmu, dirname)
11704 #endif
11705          );
11706    }     
11707    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11708 
11709    AST_LIST_UNLOCK(&users);
11710 
11711    return RESULT_SUCCESS;
11712 }
11713 
11714 /*! \brief Free the users structure. */
11715 static void free_vm_users(void) 
11716 {
11717    struct ast_vm_user *current;
11718    AST_LIST_LOCK(&users);
11719    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11720       ast_set_flag(current, VM_ALLOCED);
11721       free_user(current);
11722    }
11723    AST_LIST_UNLOCK(&users);
11724 }
11725 
11726 /*! \brief Free the zones structure. */
11727 static void free_vm_zones(void)
11728 {
11729    struct vm_zone *zcur;
11730    AST_LIST_LOCK(&zones);
11731    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11732       free_zone(zcur);
11733    AST_LIST_UNLOCK(&zones);
11734 }
11735 
11736 static const char *substitute_escapes(const char *value)
11737 {
11738    char *current;
11739 
11740    /* Add 16 for fudge factor */
11741    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11742 
11743    ast_str_reset(str);
11744    
11745    /* Substitute strings \r, \n, and \t into the appropriate characters */
11746    for (current = (char *) value; *current; current++) {
11747       if (*current == '\\') {
11748          current++;
11749          if (!*current) {
11750             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11751             break;
11752          }
11753          switch (*current) {
11754          case '\\':
11755             ast_str_append(&str, 0, "\\");
11756             break;
11757          case 'r':
11758             ast_str_append(&str, 0, "\r");
11759             break;
11760          case 'n':
11761 #ifdef IMAP_STORAGE
11762             if (!str->used || str->str[str->used - 1] != '\r') {
11763                ast_str_append(&str, 0, "\r");
11764             }
11765 #endif
11766             ast_str_append(&str, 0, "\n");
11767             break;
11768          case 't':
11769             ast_str_append(&str, 0, "\t");
11770             break;
11771          default:
11772             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11773             break;
11774          }
11775       } else {
11776          ast_str_append(&str, 0, "%c", *current);
11777       }
11778    }
11779 
11780    return ast_str_buffer(str);
11781 }
11782 
11783 static int load_config(int reload)
11784 {
11785    struct ast_config *cfg, *ucfg;
11786    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11787    int res;
11788 
11789    ast_unload_realtime("voicemail");
11790    ast_unload_realtime("voicemail_data");
11791 
11792    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11793       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11794          return 0;
11795       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11796          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11797          ucfg = NULL;
11798       }
11799       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11800       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11801          ast_config_destroy(ucfg);
11802          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11803          return 0;
11804       }
11805    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11806       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11807       return 0;
11808    } else {
11809       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11810       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11811          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11812          ucfg = NULL;
11813       }
11814    }
11815 
11816    res = actual_load_config(reload, cfg, ucfg);
11817 
11818    ast_config_destroy(cfg);
11819    ast_config_destroy(ucfg);
11820 
11821    return res;
11822 }
11823 
11824 #ifdef TEST_FRAMEWORK
11825 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11826 {
11827    ast_unload_realtime("voicemail");
11828    ast_unload_realtime("voicemail_data");
11829    return actual_load_config(reload, cfg, ucfg);
11830 }
11831 #endif
11832 
11833 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11834 {
11835    struct ast_vm_user *current;
11836    char *cat;
11837    struct ast_variable *var;
11838    const char *val;
11839    char *q, *stringp, *tmp;
11840    int x;
11841    unsigned int tmpadsi[4];
11842    char secretfn[PATH_MAX] = "";
11843 
11844 #ifdef IMAP_STORAGE
11845    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11846 #endif
11847    /* set audio control prompts */
11848    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11849    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11850    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11851    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11852    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11853 
11854    /* Free all the users structure */  
11855    free_vm_users();
11856 
11857    /* Free all the zones structure */
11858    free_vm_zones();
11859 
11860    AST_LIST_LOCK(&users);  
11861 
11862    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11863    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11864 
11865    if (cfg) {
11866       /* General settings */
11867 
11868       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11869          val = "default";
11870       ast_copy_string(userscontext, val, sizeof(userscontext));
11871       /* Attach voice message to mail message ? */
11872       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11873          val = "yes";
11874       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11875 
11876       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11877          val = "no";
11878       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11879 
11880       volgain = 0.0;
11881       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11882          sscanf(val, "%30lf", &volgain);
11883 
11884 #ifdef ODBC_STORAGE
11885       strcpy(odbc_database, "asterisk");
11886       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11887          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11888       }
11889       strcpy(odbc_table, "voicemessages");
11890       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11891          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11892       }
11893 #endif      
11894       /* Mail command */
11895       strcpy(mailcmd, SENDMAIL);
11896       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11897          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11898 
11899       maxsilence = 0;
11900       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11901          maxsilence = atoi(val);
11902          if (maxsilence > 0)
11903             maxsilence *= 1000;
11904       }
11905       
11906       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11907          maxmsg = MAXMSG;
11908       } else {
11909          maxmsg = atoi(val);
11910          if (maxmsg < 0) {
11911             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11912             maxmsg = MAXMSG;
11913          } else if (maxmsg > MAXMSGLIMIT) {
11914             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11915             maxmsg = MAXMSGLIMIT;
11916          }
11917       }
11918 
11919       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11920          maxdeletedmsg = 0;
11921       } else {
11922          if (sscanf(val, "%30d", &x) == 1)
11923             maxdeletedmsg = x;
11924          else if (ast_true(val))
11925             maxdeletedmsg = MAXMSG;
11926          else
11927             maxdeletedmsg = 0;
11928 
11929          if (maxdeletedmsg < 0) {
11930             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11931             maxdeletedmsg = MAXMSG;
11932          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11933             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11934             maxdeletedmsg = MAXMSGLIMIT;
11935          }
11936       }
11937 
11938       /* Load date format config for voicemail mail */
11939       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11940          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11941       }
11942 
11943       /* Load date format config for voicemail pager mail */
11944       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11945          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11946       }
11947 
11948       /* External password changing command */
11949       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11950          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11951          pwdchange = PWDCHANGE_EXTERNAL;
11952       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11953          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11954          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11955       }
11956  
11957       /* External password validation command */
11958       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11959          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11960          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11961       }
11962 
11963 #ifdef IMAP_STORAGE
11964       /* IMAP server address */
11965       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11966          ast_copy_string(imapserver, val, sizeof(imapserver));
11967       } else {
11968          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11969       }
11970       /* IMAP server port */
11971       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11972          ast_copy_string(imapport, val, sizeof(imapport));
11973       } else {
11974          ast_copy_string(imapport, "143", sizeof(imapport));
11975       }
11976       /* IMAP server flags */
11977       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11978          ast_copy_string(imapflags, val, sizeof(imapflags));
11979       }
11980       /* IMAP server master username */
11981       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11982          ast_copy_string(authuser, val, sizeof(authuser));
11983       }
11984       /* IMAP server master password */
11985       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11986          ast_copy_string(authpassword, val, sizeof(authpassword));
11987       }
11988       /* Expunge on exit */
11989       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11990          if (ast_false(val))
11991             expungeonhangup = 0;
11992          else
11993             expungeonhangup = 1;
11994       } else {
11995          expungeonhangup = 1;
11996       }
11997       /* IMAP voicemail folder */
11998       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11999          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12000       } else {
12001          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12002       }
12003       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12004          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12005       }
12006       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12007          imapgreetings = ast_true(val);
12008       } else {
12009          imapgreetings = 0;
12010       }
12011       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12012          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12013       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12014          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12015          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12016       } else {
12017          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12018       }
12019 
12020       /* There is some very unorthodox casting done here. This is due
12021        * to the way c-client handles the argument passed in. It expects a 
12022        * void pointer and casts the pointer directly to a long without
12023        * first dereferencing it. */
12024       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12025          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12026       } else {
12027          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12028       }
12029 
12030       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12031          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12032       } else {
12033          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12034       }
12035 
12036       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12037          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12038       } else {
12039          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12040       }
12041 
12042       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12043          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12044       } else {
12045          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12046       }
12047 
12048       /* Increment configuration version */
12049       imapversion++;
12050 #endif
12051       /* External voicemail notify application */
12052       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12053          ast_copy_string(externnotify, val, sizeof(externnotify));
12054          ast_debug(1, "found externnotify: %s\n", externnotify);
12055       } else {
12056          externnotify[0] = '\0';
12057       }
12058 
12059       /* SMDI voicemail notification */
12060       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12061          ast_debug(1, "Enabled SMDI voicemail notification\n");
12062          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12063             smdi_iface = ast_smdi_interface_find(val);
12064          } else {
12065             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12066             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12067          }
12068          if (!smdi_iface) {
12069             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12070          } 
12071       }
12072 
12073       /* Silence treshold */
12074       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12075       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12076          silencethreshold = atoi(val);
12077       
12078       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12079          val = ASTERISK_USERNAME;
12080       ast_copy_string(serveremail, val, sizeof(serveremail));
12081       
12082       vmmaxsecs = 0;
12083       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12084          if (sscanf(val, "%30d", &x) == 1) {
12085             vmmaxsecs = x;
12086          } else {
12087             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12088          }
12089       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12090          static int maxmessage_deprecate = 0;
12091          if (maxmessage_deprecate == 0) {
12092             maxmessage_deprecate = 1;
12093             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12094          }
12095          if (sscanf(val, "%30d", &x) == 1) {
12096             vmmaxsecs = x;
12097          } else {
12098             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12099          }
12100       }
12101 
12102       vmminsecs = 0;
12103       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12104          if (sscanf(val, "%30d", &x) == 1) {
12105             vmminsecs = x;
12106             if (maxsilence / 1000 >= vmminsecs) {
12107                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12108             }
12109          } else {
12110             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12111          }
12112       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12113          static int maxmessage_deprecate = 0;
12114          if (maxmessage_deprecate == 0) {
12115             maxmessage_deprecate = 1;
12116             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12117          }
12118          if (sscanf(val, "%30d", &x) == 1) {
12119             vmminsecs = x;
12120             if (maxsilence / 1000 >= vmminsecs) {
12121                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12122             }
12123          } else {
12124             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12125          }
12126       }
12127 
12128       val = ast_variable_retrieve(cfg, "general", "format");
12129       if (!val) {
12130          val = "wav";   
12131       } else {
12132          tmp = ast_strdupa(val);
12133          val = ast_format_str_reduce(tmp);
12134          if (!val) {
12135             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12136             val = "wav";
12137          }
12138       }
12139       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12140 
12141       skipms = 3000;
12142       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12143          if (sscanf(val, "%30d", &x) == 1) {
12144             maxgreet = x;
12145          } else {
12146             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12147          }
12148       }
12149 
12150       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12151          if (sscanf(val, "%30d", &x) == 1) {
12152             skipms = x;
12153          } else {
12154             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12155          }
12156       }
12157 
12158       maxlogins = 3;
12159       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12160          if (sscanf(val, "%30d", &x) == 1) {
12161             maxlogins = x;
12162          } else {
12163             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12164          }
12165       }
12166 
12167       minpassword = MINPASSWORD;
12168       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12169          if (sscanf(val, "%30d", &x) == 1) {
12170             minpassword = x;
12171          } else {
12172             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12173          }
12174       }
12175 
12176       /* Force new user to record name ? */
12177       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12178          val = "no";
12179       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12180 
12181       /* Force new user to record greetings ? */
12182       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12183          val = "no";
12184       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12185 
12186       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12187          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12188          stringp = ast_strdupa(val);
12189          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12190             if (!ast_strlen_zero(stringp)) {
12191                q = strsep(&stringp, ",");
12192                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12193                   q++;
12194                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12195                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12196             } else {
12197                cidinternalcontexts[x][0] = '\0';
12198             }
12199          }
12200       }
12201       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12202          ast_debug(1, "VM Review Option disabled globally\n");
12203          val = "no";
12204       }
12205       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12206 
12207       /* Temporary greeting reminder */
12208       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12209          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12210          val = "no";
12211       } else {
12212          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12213       }
12214       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12215       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12216          ast_debug(1, "VM next message wrap disabled globally\n");
12217          val = "no";
12218       }
12219       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12220 
12221       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12222          ast_debug(1, "VM Operator break disabled globally\n");
12223          val = "no";
12224       }
12225       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12226 
12227       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12228          ast_debug(1, "VM CID Info before msg disabled globally\n");
12229          val = "no";
12230       } 
12231       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12232 
12233       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12234          ast_debug(1, "Send Voicemail msg disabled globally\n");
12235          val = "no";
12236       }
12237       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12238    
12239       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12240          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12241          val = "yes";
12242       }
12243       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12244 
12245       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12246          ast_debug(1, "Move Heard enabled globally\n");
12247          val = "yes";
12248       }
12249       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12250 
12251       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12252          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12253          val = "no";
12254       }
12255       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12256 
12257       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12258          ast_debug(1, "Duration info before msg enabled globally\n");
12259          val = "yes";
12260       }
12261       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12262 
12263       saydurationminfo = 2;
12264       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12265          if (sscanf(val, "%30d", &x) == 1) {
12266             saydurationminfo = x;
12267          } else {
12268             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12269          }
12270       }
12271 
12272       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12273          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12274          val = "no";
12275       }
12276       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12277 
12278       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12279          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12280          ast_debug(1, "found dialout context: %s\n", dialcontext);
12281       } else {
12282          dialcontext[0] = '\0';  
12283       }
12284       
12285       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12286          ast_copy_string(callcontext, val, sizeof(callcontext));
12287          ast_debug(1, "found callback context: %s\n", callcontext);
12288       } else {
12289          callcontext[0] = '\0';
12290       }
12291 
12292       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12293          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12294          ast_debug(1, "found operator context: %s\n", exitcontext);
12295       } else {
12296          exitcontext[0] = '\0';
12297       }
12298       
12299       /* load password sounds configuration */
12300       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12301          ast_copy_string(vm_password, val, sizeof(vm_password));
12302       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12303          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12304       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12305          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12306       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12307          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12308       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12309          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12310       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12311          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12312       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12313          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12314       }
12315       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12316          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12317       }
12318       /* load configurable audio prompts */
12319       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12320          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12321       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12322          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12323       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12324          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12325       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12326          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12327       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12328          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12329 
12330       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12331          val = "no";
12332       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12333 
12334       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12335          val = "voicemail.conf";
12336       }
12337       if (!(strcmp(val, "spooldir"))) {
12338          passwordlocation = OPT_PWLOC_SPOOLDIR;
12339       } else {
12340          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12341       }
12342 
12343       poll_freq = DEFAULT_POLL_FREQ;
12344       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12345          if (sscanf(val, "%30u", &poll_freq) != 1) {
12346             poll_freq = DEFAULT_POLL_FREQ;
12347             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12348          }
12349       }
12350 
12351       poll_mailboxes = 0;
12352       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12353          poll_mailboxes = ast_true(val);
12354 
12355       memset(fromstring, 0, sizeof(fromstring));
12356       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12357       strcpy(charset, "ISO-8859-1");
12358       if (emailbody) {
12359          ast_free(emailbody);
12360          emailbody = NULL;
12361       }
12362       if (emailsubject) {
12363          ast_free(emailsubject);
12364          emailsubject = NULL;
12365       }
12366       if (pagerbody) {
12367          ast_free(pagerbody);
12368          pagerbody = NULL;
12369       }
12370       if (pagersubject) {
12371          ast_free(pagersubject);
12372          pagersubject = NULL;
12373       }
12374       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12375          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12376       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12377          ast_copy_string(fromstring, val, sizeof(fromstring));
12378       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12379          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12380       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12381          ast_copy_string(charset, val, sizeof(charset));
12382       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12383          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12384          for (x = 0; x < 4; x++) {
12385             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12386          }
12387       }
12388       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12389          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12390          for (x = 0; x < 4; x++) {
12391             memcpy(&adsisec[x], &tmpadsi[x], 1);
12392          }
12393       }
12394       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12395          if (atoi(val)) {
12396             adsiver = atoi(val);
12397          }
12398       }
12399       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12400          ast_copy_string(zonetag, val, sizeof(zonetag));
12401       }
12402       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12403          ast_copy_string(locale, val, sizeof(locale));
12404       }
12405       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12406          emailsubject = ast_strdup(substitute_escapes(val));
12407       }
12408       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12409          emailbody = ast_strdup(substitute_escapes(val));
12410       }
12411       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12412          pagersubject = ast_strdup(substitute_escapes(val));
12413       }
12414       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12415          pagerbody = ast_strdup(substitute_escapes(val));
12416       }
12417 
12418       /* load mailboxes from users.conf */
12419       if (ucfg) { 
12420          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12421             if (!strcasecmp(cat, "general")) {
12422                continue;
12423             }
12424             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12425                continue;
12426             if ((current = find_or_create(userscontext, cat))) {
12427                populate_defaults(current);
12428                apply_options_full(current, ast_variable_browse(ucfg, cat));
12429                ast_copy_string(current->context, userscontext, sizeof(current->context));
12430                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12431                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12432                }
12433 
12434                switch (current->passwordlocation) {
12435                case OPT_PWLOC_SPOOLDIR:
12436                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12437                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12438                }
12439             }
12440          }
12441       }
12442 
12443       /* load mailboxes from voicemail.conf */
12444       cat = ast_category_browse(cfg, NULL);
12445       while (cat) {
12446          if (strcasecmp(cat, "general")) {
12447             var = ast_variable_browse(cfg, cat);
12448             if (strcasecmp(cat, "zonemessages")) {
12449                /* Process mailboxes in this context */
12450                while (var) {
12451                   append_mailbox(cat, var->name, var->value);
12452                   var = var->next;
12453                }
12454             } else {
12455                /* Timezones in this context */
12456                while (var) {
12457                   struct vm_zone *z;
12458                   if ((z = ast_malloc(sizeof(*z)))) {
12459                      char *msg_format, *tzone;
12460                      msg_format = ast_strdupa(var->value);
12461                      tzone = strsep(&msg_format, "|,");
12462                      if (msg_format) {
12463                         ast_copy_string(z->name, var->name, sizeof(z->name));
12464                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12465                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12466                         AST_LIST_LOCK(&zones);
12467                         AST_LIST_INSERT_HEAD(&zones, z, list);
12468                         AST_LIST_UNLOCK(&zones);
12469                      } else {
12470                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12471                         ast_free(z);
12472                      }
12473                   } else {
12474                      AST_LIST_UNLOCK(&users);
12475                      return -1;
12476                   }
12477                   var = var->next;
12478                }
12479             }
12480          }
12481          cat = ast_category_browse(cfg, cat);
12482       }
12483 
12484       AST_LIST_UNLOCK(&users);
12485 
12486       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12487          start_poll_thread();
12488       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12489          stop_poll_thread();;
12490 
12491       return 0;
12492    } else {
12493       AST_LIST_UNLOCK(&users);
12494       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12495       return 0;
12496    }
12497 }
12498 
12499 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12500 {
12501    int res = -1;
12502    char dir[PATH_MAX];
12503    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12504    ast_debug(2, "About to try retrieving name file %s\n", dir);
12505    RETRIEVE(dir, -1, mailbox, context);
12506    if (ast_fileexists(dir, NULL, NULL)) {
12507       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12508    }
12509    DISPOSE(dir, -1);
12510    return res;
12511 }
12512 
12513 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12514    struct ast_config *pwconf;
12515    struct ast_flags config_flags = { 0 };
12516 
12517    pwconf = ast_config_load(secretfn, config_flags);
12518    if (valid_config(pwconf)) {
12519       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12520       if (val) {
12521          ast_copy_string(password, val, passwordlen);
12522          ast_config_destroy(pwconf);
12523          return;
12524       }
12525       ast_config_destroy(pwconf);
12526    }
12527    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12528 }
12529 
12530 static int write_password_to_file(const char *secretfn, const char *password) {
12531    struct ast_config *conf;
12532    struct ast_category *cat;
12533    struct ast_variable *var;
12534    int res = -1;
12535 
12536    if (!(conf = ast_config_new())) {
12537       ast_log(LOG_ERROR, "Error creating new config structure\n");
12538       return res;
12539    }
12540    if (!(cat = ast_category_new("general", "", 1))) {
12541       ast_log(LOG_ERROR, "Error creating new category structure\n");
12542       ast_config_destroy(conf);
12543       return res;
12544    }
12545    if (!(var = ast_variable_new("password", password, ""))) {
12546       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12547       ast_config_destroy(conf);
12548       ast_category_destroy(cat);
12549       return res;
12550    }
12551    ast_category_append(conf, cat);
12552    ast_variable_append(cat, var);
12553    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12554       res = 0;
12555    } else {
12556       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12557    }
12558 
12559    ast_config_destroy(conf);
12560    return res;
12561 }
12562 
12563 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12564 {
12565    char *context;
12566    char *args_copy;
12567    int res;
12568 
12569    if (ast_strlen_zero(data)) {
12570       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12571       return -1;
12572    }
12573 
12574    args_copy = ast_strdupa(data);
12575    if ((context = strchr(args_copy, '@'))) {
12576       *context++ = '\0';
12577    } else {
12578       context = "default";
12579    }
12580 
12581    if ((res = sayname(chan, args_copy, context) < 0)) {
12582       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12583       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12584       if (!res) {
12585          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12586       }
12587    }
12588 
12589    return res;
12590 }
12591 
12592 #ifdef TEST_FRAMEWORK
12593 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12594 {
12595    return 0;
12596 }
12597 
12598 static struct ast_frame *fake_read(struct ast_channel *ast)
12599 {
12600    return &ast_null_frame;
12601 }
12602 
12603 AST_TEST_DEFINE(test_voicemail_vmsayname)
12604 {
12605    char dir[PATH_MAX];
12606    char dir2[PATH_MAX];
12607    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12608    static const char TEST_EXTENSION[] = "1234";
12609 
12610    struct ast_channel *test_channel1 = NULL;
12611    int res = -1;
12612 
12613    static const struct ast_channel_tech fake_tech = {
12614       .write = fake_write,
12615       .read = fake_read,
12616    };
12617 
12618    switch (cmd) {
12619    case TEST_INIT:
12620       info->name = "vmsayname_exec";
12621       info->category = "/apps/app_voicemail/";
12622       info->summary = "Vmsayname unit test";
12623       info->description =
12624          "This tests passing various parameters to vmsayname";
12625       return AST_TEST_NOT_RUN;
12626    case TEST_EXECUTE:
12627       break;
12628    }
12629 
12630    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12631         NULL, NULL, 0, 0, "TestChannel1"))) {
12632       goto exit_vmsayname_test;
12633    }
12634 
12635    /* normally this is done in the channel driver */
12636    test_channel1->nativeformats = AST_FORMAT_GSM;
12637    test_channel1->writeformat = AST_FORMAT_GSM;
12638    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12639    test_channel1->readformat = AST_FORMAT_GSM;
12640    test_channel1->rawreadformat = AST_FORMAT_GSM;
12641    test_channel1->tech = &fake_tech;
12642 
12643    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12644    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12645    if (!(res = vmsayname_exec(test_channel1, dir))) {
12646       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12647       if (ast_fileexists(dir, NULL, NULL)) {
12648          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12649          res = -1;
12650          goto exit_vmsayname_test;
12651       } else {
12652          /* no greeting already exists as expected, let's create one to fully test sayname */
12653          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12654             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12655             goto exit_vmsayname_test;
12656          }
12657          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12658          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12659          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12660          if ((res = symlink(dir, dir2))) {
12661             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12662             goto exit_vmsayname_test;
12663          }
12664          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12665          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12666          res = vmsayname_exec(test_channel1, dir);
12667 
12668          /* TODO: there may be a better way to do this */
12669          unlink(dir2);
12670          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12671          rmdir(dir2);
12672          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12673          rmdir(dir2);
12674       }
12675    }
12676 
12677 exit_vmsayname_test:
12678 
12679    if (test_channel1) {
12680       ast_hangup(test_channel1);
12681    }
12682 
12683    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12684 }
12685 
12686 AST_TEST_DEFINE(test_voicemail_msgcount)
12687 {
12688    int i, j, res = AST_TEST_PASS, syserr;
12689    struct ast_vm_user *vmu;
12690    struct ast_vm_user svm;
12691    struct vm_state vms;
12692 #ifdef IMAP_STORAGE
12693    struct ast_channel *chan = NULL;
12694 #endif
12695    struct {
12696       char dir[256];
12697       char file[256];
12698       char txtfile[256];
12699    } tmp[3];
12700    char syscmd[256];
12701    const char origweasels[] = "tt-weasels";
12702    const char testcontext[] = "test";
12703    const char testmailbox[] = "00000000";
12704    const char testspec[] = "00000000@test";
12705    FILE *txt;
12706    int new, old, urgent;
12707    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12708    const int folder2mbox[3] = { 1, 11, 0 };
12709    const int expected_results[3][12] = {
12710       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12711       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12712       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12713       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12714    };
12715 
12716    switch (cmd) {
12717    case TEST_INIT:
12718       info->name = "test_voicemail_msgcount";
12719       info->category = "/apps/app_voicemail/";
12720       info->summary = "Test Voicemail status checks";
12721       info->description =
12722          "Verify that message counts are correct when retrieved through the public API";
12723       return AST_TEST_NOT_RUN;
12724    case TEST_EXECUTE:
12725       break;
12726    }
12727 
12728    /* Make sure the original path was completely empty */
12729    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12730    if ((syserr = ast_safe_system(syscmd))) {
12731       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12732          syserr > 0 ? strerror(syserr) : "unable to fork()");
12733       return AST_TEST_FAIL;
12734    }
12735 
12736 #ifdef IMAP_STORAGE
12737    if (!(chan = ast_dummy_channel_alloc())) {
12738       ast_test_status_update(test, "Unable to create dummy channel\n");
12739       return AST_TEST_FAIL;
12740    }
12741 #endif
12742 
12743    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12744       !(vmu = find_or_create(testcontext, testmailbox))) {
12745       ast_test_status_update(test, "Cannot create vmu structure\n");
12746       ast_unreplace_sigchld();
12747 #ifdef IMAP_STORAGE
12748       chan = ast_channel_unref(chan);
12749 #endif
12750       return AST_TEST_FAIL;
12751    }
12752 
12753    populate_defaults(vmu);
12754    memset(&vms, 0, sizeof(vms));
12755 
12756    /* Create temporary voicemail */
12757    for (i = 0; i < 3; i++) {
12758       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12759       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12760       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12761 
12762       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12763          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12764             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12765          if ((syserr = ast_safe_system(syscmd))) {
12766             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12767                syserr > 0 ? strerror(syserr) : "unable to fork()");
12768             ast_unreplace_sigchld();
12769 #ifdef IMAP_STORAGE
12770             chan = ast_channel_unref(chan);
12771 #endif
12772             return AST_TEST_FAIL;
12773          }
12774       }
12775 
12776       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12777          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12778          fclose(txt);
12779       } else {
12780          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12781          res = AST_TEST_FAIL;
12782          break;
12783       }
12784       open_mailbox(&vms, vmu, folder2mbox[i]);
12785       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12786 
12787       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12788       for (j = 0; j < 3; j++) {
12789          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12790          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12791             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12792                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12793             res = AST_TEST_FAIL;
12794          }
12795       }
12796 
12797       new = old = urgent = 0;
12798       if (ast_app_inboxcount(testspec, &new, &old)) {
12799          ast_test_status_update(test, "inboxcount returned failure\n");
12800          res = AST_TEST_FAIL;
12801       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12802          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12803             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12804          res = AST_TEST_FAIL;
12805       }
12806 
12807       new = old = urgent = 0;
12808       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12809          ast_test_status_update(test, "inboxcount2 returned failure\n");
12810          res = AST_TEST_FAIL;
12811       } else if (old != expected_results[i][6 + 0] ||
12812             urgent != expected_results[i][6 + 1] ||
12813                new != expected_results[i][6 + 2]    ) {
12814          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12815             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12816          res = AST_TEST_FAIL;
12817       }
12818 
12819       new = old = urgent = 0;
12820       for (j = 0; j < 3; j++) {
12821          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12822             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12823                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12824             res = AST_TEST_FAIL;
12825          }
12826       }
12827    }
12828 
12829    for (i = 0; i < 3; i++) {
12830       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12831        * server, in which case, the rm below will not affect the
12832        * voicemails. */
12833       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12834       DISPOSE(tmp[i].dir, 0);
12835    }
12836 
12837    if (vms.deleted) {
12838       ast_free(vms.deleted);
12839    }
12840    if (vms.heard) {
12841       ast_free(vms.heard);
12842    }
12843 
12844 #ifdef IMAP_STORAGE
12845    chan = ast_channel_unref(chan);
12846 #endif
12847 
12848    /* And remove test directory */
12849    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12850    if ((syserr = ast_safe_system(syscmd))) {
12851       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12852          syserr > 0 ? strerror(syserr) : "unable to fork()");
12853    }
12854 
12855    return res;
12856 }
12857 
12858 AST_TEST_DEFINE(test_voicemail_notify_endl)
12859 {
12860    int res = AST_TEST_PASS;
12861    char testcontext[] = "test";
12862    char testmailbox[] = "00000000";
12863    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12864    char attach[256], attach2[256];
12865    char buf[256] = ""; /* No line should actually be longer than 80 */
12866    struct ast_channel *chan = NULL;
12867    struct ast_vm_user *vmu, vmus = {
12868       .flags = 0,
12869    };
12870    FILE *file;
12871    struct {
12872       char *name;
12873       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12874       void *location;
12875       union {
12876          int intval;
12877          char *strval;
12878       } u;
12879    } test_items[] = {
12880       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12881       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12882       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12883       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12884       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12885       { "attach2", STRPTR, attach2, .u.strval = "" },
12886       { "attach", STRPTR, attach, .u.strval = "" },
12887    };
12888    int which;
12889 
12890    switch (cmd) {
12891    case TEST_INIT:
12892       info->name = "test_voicemail_notify_endl";
12893       info->category = "/apps/app_voicemail/";
12894       info->summary = "Test Voicemail notification end-of-line";
12895       info->description =
12896          "Verify that notification emails use a consistent end-of-line character";
12897       return AST_TEST_NOT_RUN;
12898    case TEST_EXECUTE:
12899       break;
12900    }
12901 
12902    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12903    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12904 
12905    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12906       !(vmu = find_or_create(testcontext, testmailbox))) {
12907       ast_test_status_update(test, "Cannot create vmu structure\n");
12908       return AST_TEST_NOT_RUN;
12909    }
12910 
12911    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12912       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12913       return AST_TEST_NOT_RUN;
12914    }
12915 
12916    populate_defaults(vmu);
12917    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12918 #ifdef IMAP_STORAGE
12919    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12920 #endif
12921 
12922    file = tmpfile();
12923    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12924       /* Kill previous test, if any */
12925       rewind(file);
12926       if (ftruncate(fileno(file), 0)) {
12927          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12928          res = AST_TEST_FAIL;
12929          break;
12930       }
12931 
12932       /* Make each change, in order, to the test mailbox */
12933       if (test_items[which].type == INT) {
12934          *((int *) test_items[which].location) = test_items[which].u.intval;
12935       } else if (test_items[which].type == FLAGVAL) {
12936          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12937             ast_clear_flag(vmu, test_items[which].u.intval);
12938          } else {
12939             ast_set_flag(vmu, test_items[which].u.intval);
12940          }
12941       } else if (test_items[which].type == STATIC) {
12942          strcpy(test_items[which].location, test_items[which].u.strval);
12943       } else if (test_items[which].type == STRPTR) {
12944          test_items[which].location = test_items[which].u.strval;
12945       }
12946 
12947       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12948       rewind(file);
12949       while (fgets(buf, sizeof(buf), file)) {
12950          if (
12951 #ifdef IMAP_STORAGE
12952          buf[strlen(buf) - 2] != '\r'
12953 #else
12954          buf[strlen(buf) - 2] == '\r'
12955 #endif
12956          || buf[strlen(buf) - 1] != '\n') {
12957             res = AST_TEST_FAIL;
12958          }
12959       }
12960    }
12961    fclose(file);
12962    return res;
12963 }
12964 
12965 AST_TEST_DEFINE(test_voicemail_load_config)
12966 {
12967    int res = AST_TEST_PASS;
12968    struct ast_vm_user *vmu;
12969    struct ast_config *cfg;
12970    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12971    int fd;
12972    FILE *file;
12973    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12974 
12975    switch (cmd) {
12976    case TEST_INIT:
12977       info->name = "test_voicemail_load_config";
12978       info->category = "/apps/app_voicemail/";
12979       info->summary = "Test loading Voicemail config";
12980       info->description =
12981          "Verify that configuration is loaded consistently. "
12982          "This is to test regressions of ASTERISK-18838 where it was noticed that "
12983          "some options were loaded after the mailboxes were instantiated, causing "
12984          "those options not to be set correctly.";
12985       return AST_TEST_NOT_RUN;
12986    case TEST_EXECUTE:
12987       break;
12988    }
12989 
12990    /* build a config file by hand... */
12991    if ((fd = mkstemp(config_filename)) < 0) {
12992       return AST_TEST_FAIL;
12993    }
12994    if (!(file = fdopen(fd, "w"))) {
12995       close(fd);
12996       unlink(config_filename);
12997       return AST_TEST_FAIL;
12998    }
12999    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13000    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13001    fputs("00000002 => 9999,Mrs. Test\n", file);
13002    fclose(file);
13003 
13004    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13005       res = AST_TEST_FAIL;
13006       goto cleanup;
13007    }
13008 
13009    load_config_from_memory(1, cfg, NULL);
13010    ast_config_destroy(cfg);
13011 
13012 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13013    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13014    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13015 
13016    AST_LIST_LOCK(&users);
13017    AST_LIST_TRAVERSE(&users, vmu, list) {
13018       if (!strcmp(vmu->mailbox, "00000001")) {
13019          if (0); /* trick to get CHECK to work */
13020          CHECK(vmu, callback, "othercontext")
13021          CHECK(vmu, locale, "nl_NL.UTF-8")
13022          CHECK(vmu, zonetag, "central")
13023       } else if (!strcmp(vmu->mailbox, "00000002")) {
13024          if (0); /* trick to get CHECK to work */
13025          CHECK(vmu, callback, "somecontext")
13026          CHECK(vmu, locale, "de_DE.UTF-8")
13027          CHECK(vmu, zonetag, "european")
13028       }
13029    }
13030    AST_LIST_UNLOCK(&users);
13031 
13032 #undef CHECK
13033 
13034    /* restore config */
13035    load_config(1); /* this might say "Failed to load configuration file." */
13036 
13037 cleanup:
13038    unlink(config_filename);
13039    return res;
13040 }
13041 
13042 #endif /* defined(TEST_FRAMEWORK) */
13043 
13044 static int reload(void)
13045 {
13046    return load_config(1);
13047 }
13048 
13049 static int unload_module(void)
13050 {
13051    int res;
13052 
13053    res = ast_unregister_application(app);
13054    res |= ast_unregister_application(app2);
13055    res |= ast_unregister_application(app3);
13056    res |= ast_unregister_application(app4);
13057    res |= ast_unregister_application(sayname_app);
13058    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13059    res |= ast_manager_unregister("VoicemailUsersList");
13060    res |= ast_data_unregister(NULL);
13061 #ifdef TEST_FRAMEWORK
13062    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13063    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13064    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13065    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13066    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13067 #endif
13068    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13069    ast_uninstall_vm_functions();
13070    ao2_ref(inprocess_container, -1);
13071 
13072    if (poll_thread != AST_PTHREADT_NULL)
13073       stop_poll_thread();
13074 
13075    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13076    ast_unload_realtime("voicemail");
13077    ast_unload_realtime("voicemail_data");
13078 
13079    free_vm_users();
13080    free_vm_zones();
13081    return res;
13082 }
13083 
13084 static int load_module(void)
13085 {
13086    int res;
13087    my_umask = umask(0);
13088    umask(my_umask);
13089 
13090    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13091       return AST_MODULE_LOAD_DECLINE;
13092    }
13093 
13094    /* compute the location of the voicemail spool directory */
13095    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13096    
13097    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13098       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13099    }
13100 
13101    if ((res = load_config(0)))
13102       return res;
13103 
13104    res = ast_register_application_xml(app, vm_exec);
13105    res |= ast_register_application_xml(app2, vm_execmain);
13106    res |= ast_register_application_xml(app3, vm_box_exists);
13107    res |= ast_register_application_xml(app4, vmauthenticate);
13108    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13109    res |= ast_custom_function_register(&mailbox_exists_acf);
13110    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13111 #ifdef TEST_FRAMEWORK
13112    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13113    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13114    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13115    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13116    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13117 #endif
13118 
13119    if (res)
13120       return res;
13121 
13122    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13123    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13124 
13125    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13126    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13127    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13128 
13129    return res;
13130 }
13131 
13132 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13133 {
13134    int cmd = 0;
13135    char destination[80] = "";
13136    int retries = 0;
13137 
13138    if (!num) {
13139       ast_verb(3, "Destination number will be entered manually\n");
13140       while (retries < 3 && cmd != 't') {
13141          destination[1] = '\0';
13142          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13143          if (!cmd)
13144             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13145          if (!cmd)
13146             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13147          if (!cmd) {
13148             cmd = ast_waitfordigit(chan, 6000);
13149             if (cmd)
13150                destination[0] = cmd;
13151          }
13152          if (!cmd) {
13153             retries++;
13154          } else {
13155 
13156             if (cmd < 0)
13157                return 0;
13158             if (cmd == '*') {
13159                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13160                return 0;
13161             }
13162             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13163                retries++;
13164             else
13165                cmd = 't';
13166          }
13167          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13168       }
13169       if (retries >= 3) {
13170          return 0;
13171       }
13172       
13173    } else {
13174       if (option_verbose > 2)
13175          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13176       ast_copy_string(destination, num, sizeof(destination));
13177    }
13178 
13179    if (!ast_strlen_zero(destination)) {
13180       if (destination[strlen(destination) -1 ] == '*')
13181          return 0; 
13182       if (option_verbose > 2)
13183          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13184       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13185       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13186       chan->priority = 0;
13187       return 9;
13188    }
13189    return 0;
13190 }
13191 
13192 /*!
13193  * \brief The advanced options within a message.
13194  * \param chan
13195  * \param vmu 
13196  * \param vms
13197  * \param msg
13198  * \param option
13199  * \param record_gain
13200  *
13201  * Provides handling for the play message envelope, call the person back, or reply to message. 
13202  *
13203  * \return zero on success, -1 on error.
13204  */
13205 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)
13206 {
13207    int res = 0;
13208    char filename[PATH_MAX];
13209    struct ast_config *msg_cfg = NULL;
13210    const char *origtime, *context;
13211    char *name, *num;
13212    int retries = 0;
13213    char *cid;
13214    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13215 
13216    vms->starting = 0; 
13217 
13218    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13219 
13220    /* Retrieve info from VM attribute file */
13221    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13222    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13223    msg_cfg = ast_config_load(filename, config_flags);
13224    DISPOSE(vms->curdir, vms->curmsg);
13225    if (!valid_config(msg_cfg)) {
13226       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13227       return 0;
13228    }
13229 
13230    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13231       ast_config_destroy(msg_cfg);
13232       return 0;
13233    }
13234 
13235    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13236 
13237    context = ast_variable_retrieve(msg_cfg, "message", "context");
13238    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13239       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13240    switch (option) {
13241    case 3: /* Play message envelope */
13242       if (!res)
13243          res = play_message_datetime(chan, vmu, origtime, filename);
13244       if (!res)
13245          res = play_message_callerid(chan, vms, cid, context, 0);
13246 
13247       res = 't';
13248       break;
13249 
13250    case 2:  /* Call back */
13251 
13252       if (ast_strlen_zero(cid))
13253          break;
13254 
13255       ast_callerid_parse(cid, &name, &num);
13256       while ((res > -1) && (res != 't')) {
13257          switch (res) {
13258          case '1':
13259             if (num) {
13260                /* Dial the CID number */
13261                res = dialout(chan, vmu, num, vmu->callback);
13262                if (res) {
13263                   ast_config_destroy(msg_cfg);
13264                   return 9;
13265                }
13266             } else {
13267                res = '2';
13268             }
13269             break;
13270 
13271          case '2':
13272             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13273             if (!ast_strlen_zero(vmu->dialout)) {
13274                res = dialout(chan, vmu, NULL, vmu->dialout);
13275                if (res) {
13276                   ast_config_destroy(msg_cfg);
13277                   return 9;
13278                }
13279             } else {
13280                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13281                res = ast_play_and_wait(chan, "vm-sorry");
13282             }
13283             ast_config_destroy(msg_cfg);
13284             return res;
13285          case '*':
13286             res = 't';
13287             break;
13288          case '3':
13289          case '4':
13290          case '5':
13291          case '6':
13292          case '7':
13293          case '8':
13294          case '9':
13295          case '0':
13296 
13297             res = ast_play_and_wait(chan, "vm-sorry");
13298             retries++;
13299             break;
13300          default:
13301             if (num) {
13302                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13303                res = ast_play_and_wait(chan, "vm-num-i-have");
13304                if (!res)
13305                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13306                if (!res)
13307                   res = ast_play_and_wait(chan, "vm-tocallnum");
13308                /* Only prompt for a caller-specified number if there is a dialout context specified */
13309                if (!ast_strlen_zero(vmu->dialout)) {
13310                   if (!res)
13311                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13312                }
13313             } else {
13314                res = ast_play_and_wait(chan, "vm-nonumber");
13315                if (!ast_strlen_zero(vmu->dialout)) {
13316                   if (!res)
13317                      res = ast_play_and_wait(chan, "vm-toenternumber");
13318                }
13319             }
13320             if (!res) {
13321                res = ast_play_and_wait(chan, "vm-star-cancel");
13322             }
13323             if (!res) {
13324                res = ast_waitfordigit(chan, 6000);
13325             }
13326             if (!res) {
13327                retries++;
13328                if (retries > 3) {
13329                   res = 't';
13330                }
13331             }
13332             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13333             break; 
13334             
13335          }
13336          if (res == 't')
13337             res = 0;
13338          else if (res == '*')
13339             res = -1;
13340       }
13341       break;
13342       
13343    case 1:  /* Reply */
13344       /* Send reply directly to sender */
13345       if (ast_strlen_zero(cid))
13346          break;
13347 
13348       ast_callerid_parse(cid, &name, &num);
13349       if (!num) {
13350          ast_verb(3, "No CID number available, no reply sent\n");
13351          if (!res)
13352             res = ast_play_and_wait(chan, "vm-nonumber");
13353          ast_config_destroy(msg_cfg);
13354          return res;
13355       } else {
13356          struct ast_vm_user vmu2;
13357          if (find_user(&vmu2, vmu->context, num)) {
13358             struct leave_vm_options leave_options;
13359             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13360             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13361 
13362             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13363             
13364             memset(&leave_options, 0, sizeof(leave_options));
13365             leave_options.record_gain = record_gain;
13366             res = leave_voicemail(chan, mailbox, &leave_options);
13367             if (!res)
13368                res = 't';
13369             ast_config_destroy(msg_cfg);
13370             return res;
13371          } else {
13372             /* Sender has no mailbox, can't reply */
13373             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13374             ast_play_and_wait(chan, "vm-nobox");
13375             res = 't';
13376             ast_config_destroy(msg_cfg);
13377             return res;
13378          }
13379       } 
13380       res = 0;
13381 
13382       break;
13383    }
13384 
13385    ast_config_destroy(msg_cfg);
13386 
13387 #ifndef IMAP_STORAGE
13388    if (!res) {
13389       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13390       vms->heard[msg] = 1;
13391       res = wait_file(chan, vms, vms->fn);
13392    }
13393 #endif
13394    return res;
13395 }
13396 
13397 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13398          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13399          signed char record_gain, struct vm_state *vms, char *flag)
13400 {
13401    /* Record message & let caller review or re-record it, or set options if applicable */
13402    int res = 0;
13403    int cmd = 0;
13404    int max_attempts = 3;
13405    int attempts = 0;
13406    int recorded = 0;
13407    int msg_exists = 0;
13408    signed char zero_gain = 0;
13409    char tempfile[PATH_MAX];
13410    char *acceptdtmf = "#";
13411    char *canceldtmf = "";
13412    int canceleddtmf = 0;
13413 
13414    /* Note that urgent and private are for flagging messages as such in the future */
13415 
13416    /* barf if no pointer passed to store duration in */
13417    if (duration == NULL) {
13418       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13419       return -1;
13420    }
13421 
13422    if (!outsidecaller)
13423       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13424    else
13425       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13426 
13427    cmd = '3';  /* Want to start by recording */
13428 
13429    while ((cmd >= 0) && (cmd != 't')) {
13430       switch (cmd) {
13431       case '1':
13432          if (!msg_exists) {
13433             /* In this case, 1 is to record a message */
13434             cmd = '3';
13435             break;
13436          } else {
13437             /* Otherwise 1 is to save the existing message */
13438             ast_verb(3, "Saving message as is\n");
13439             if (!outsidecaller) 
13440                ast_filerename(tempfile, recordfile, NULL);
13441             ast_stream_and_wait(chan, "vm-msgsaved", "");
13442             if (!outsidecaller) {
13443                /* Saves to IMAP server only if imapgreeting=yes */
13444                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13445                DISPOSE(recordfile, -1);
13446             }
13447             cmd = 't';
13448             return res;
13449          }
13450       case '2':
13451          /* Review */
13452          ast_verb(3, "Reviewing the message\n");
13453          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13454          break;
13455       case '3':
13456          msg_exists = 0;
13457          /* Record */
13458          if (recorded == 1) 
13459             ast_verb(3, "Re-recording the message\n");
13460          else  
13461             ast_verb(3, "Recording the message\n");
13462          
13463          if (recorded && outsidecaller) {
13464             cmd = ast_play_and_wait(chan, INTRO);
13465             cmd = ast_play_and_wait(chan, "beep");
13466          }
13467          recorded = 1;
13468          /* 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 */
13469          if (record_gain)
13470             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13471          if (ast_test_flag(vmu, VM_OPERATOR))
13472             canceldtmf = "0";
13473          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13474          if (strchr(canceldtmf, cmd)) {
13475          /* need this flag here to distinguish between pressing '0' during message recording or after */
13476             canceleddtmf = 1;
13477          }
13478          if (record_gain)
13479             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13480          if (cmd == -1) {
13481             /* User has hung up, no options to give */
13482             if (!outsidecaller) {
13483                /* user was recording a greeting and they hung up, so let's delete the recording. */
13484                ast_filedelete(tempfile, NULL);
13485             }     
13486             return cmd;
13487          }
13488          if (cmd == '0') {
13489             break;
13490          } else if (cmd == '*') {
13491             break;
13492 #if 0
13493          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13494             /* Message is too short */
13495             ast_verb(3, "Message too short\n");
13496             cmd = ast_play_and_wait(chan, "vm-tooshort");
13497             cmd = ast_filedelete(tempfile, NULL);
13498             break;
13499          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13500             /* Message is all silence */
13501             ast_verb(3, "Nothing recorded\n");
13502             cmd = ast_filedelete(tempfile, NULL);
13503             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13504             if (!cmd)
13505                cmd = ast_play_and_wait(chan, "vm-speakup");
13506             break;
13507 #endif
13508          } else {
13509             /* If all is well, a message exists */
13510             msg_exists = 1;
13511             cmd = 0;
13512          }
13513          break;
13514       case '4':
13515          if (outsidecaller) {  /* only mark vm messages */
13516             /* Mark Urgent */
13517             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13518                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13519                res = ast_play_and_wait(chan, "vm-marked-urgent");
13520                strcpy(flag, "Urgent");
13521             } else if (flag) {
13522                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13523                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13524                strcpy(flag, "");
13525             } else {
13526                ast_play_and_wait(chan, "vm-sorry");
13527             }
13528             cmd = 0;
13529          } else {
13530             cmd = ast_play_and_wait(chan, "vm-sorry");
13531          }
13532          break;
13533       case '5':
13534       case '6':
13535       case '7':
13536       case '8':
13537       case '9':
13538       case '*':
13539       case '#':
13540          cmd = ast_play_and_wait(chan, "vm-sorry");
13541          break;
13542 #if 0 
13543 /*  XXX Commented out for the moment because of the dangers of deleting
13544     a message while recording (can put the message numbers out of sync) */
13545       case '*':
13546          /* Cancel recording, delete message, offer to take another message*/
13547          cmd = ast_play_and_wait(chan, "vm-deleted");
13548          cmd = ast_filedelete(tempfile, NULL);
13549          if (outsidecaller) {
13550             res = vm_exec(chan, NULL);
13551             return res;
13552          }
13553          else
13554             return 1;
13555 #endif
13556       case '0':
13557          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13558             cmd = ast_play_and_wait(chan, "vm-sorry");
13559             break;
13560          }
13561          if (msg_exists || recorded) {
13562             cmd = ast_play_and_wait(chan, "vm-saveoper");
13563             if (!cmd)
13564                cmd = ast_waitfordigit(chan, 3000);
13565             if (cmd == '1') {
13566                ast_filerename(tempfile, recordfile, NULL);
13567                ast_play_and_wait(chan, "vm-msgsaved");
13568                cmd = '0';
13569             } else if (cmd == '4') {
13570                if (flag) {
13571                   ast_play_and_wait(chan, "vm-marked-urgent");
13572                   strcpy(flag, "Urgent");
13573                }
13574                ast_play_and_wait(chan, "vm-msgsaved");
13575                cmd = '0';
13576             } else {
13577                ast_play_and_wait(chan, "vm-deleted");
13578                DELETE(tempfile, -1, tempfile, vmu);
13579                cmd = '0';
13580             }
13581          }
13582          return cmd;
13583       default:
13584          /* If the caller is an ouside caller, and the review option is enabled,
13585             allow them to review the message, but let the owner of the box review
13586             their OGM's */
13587          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13588             return cmd;
13589          if (msg_exists) {
13590             cmd = ast_play_and_wait(chan, "vm-review");
13591             if (!cmd && outsidecaller) {
13592                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13593                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13594                } else if (flag) {
13595                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13596                }
13597             }
13598          } else {
13599             cmd = ast_play_and_wait(chan, "vm-torerecord");
13600             if (!cmd)
13601                cmd = ast_waitfordigit(chan, 600);
13602          }
13603          
13604          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13605             cmd = ast_play_and_wait(chan, "vm-reachoper");
13606             if (!cmd)
13607                cmd = ast_waitfordigit(chan, 600);
13608          }
13609 #if 0
13610          if (!cmd)
13611             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13612 #endif
13613          if (!cmd)
13614             cmd = ast_waitfordigit(chan, 6000);
13615          if (!cmd) {
13616             attempts++;
13617          }
13618          if (attempts > max_attempts) {
13619             cmd = 't';
13620          }
13621       }
13622    }
13623    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13624       /* Hang up or timeout, so delete the recording. */
13625       ast_filedelete(tempfile, NULL);
13626    }
13627 
13628    if (cmd != 't' && outsidecaller)
13629       ast_play_and_wait(chan, "vm-goodbye");
13630 
13631    return cmd;
13632 }
13633 
13634 /* This is a workaround so that menuselect displays a proper description
13635  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13636  */
13637 
13638 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13639       .load = load_module,
13640       .unload = unload_module,
13641       .reload = reload,
13642       .nonoptreq = "res_adsi,res_smdi",
13643       );

Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1