Wed Apr 6 11:29:39 2011

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, read doc/imapstorage.txt
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  ***/
00044 
00045 /*** MAKEOPTS
00046 <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">
00047    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00048       <depend>generic_odbc</depend>
00049       <depend>ltdl</depend>
00050       <defaultenabled>yes</defaultenabled>
00051    </member>
00052 </category>
00053 <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">
00054    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00055       <depend>imap_tk</depend>
00056       <use>openssl</use>
00057       <defaultenabled>yes</defaultenabled>
00058    </member>
00059 </category>
00060 ***/
00061 
00062 #include "asterisk.h"
00063 
00064 #ifdef IMAP_STORAGE
00065 #include <ctype.h>
00066 #include <signal.h>
00067 #include <pwd.h>
00068 #ifdef USE_SYSTEM_IMAP
00069 #include <imap/c-client.h>
00070 #include <imap/imap4r1.h>
00071 #include <imap/linkage.h>
00072 #elif defined (USE_SYSTEM_CCLIENT)
00073 #include <c-client/c-client.h>
00074 #include <c-client/imap4r1.h>
00075 #include <c-client/linkage.h>
00076 #else
00077 #include "c-client.h"
00078 #include "imap4r1.h"
00079 #include "linkage.h"
00080 #endif
00081 #endif
00082 
00083 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 301047 $")
00084 
00085 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00086 #include <sys/time.h>
00087 #include <sys/stat.h>
00088 #include <sys/mman.h>
00089 #include <time.h>
00090 #include <dirent.h>
00091 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00092 #include <sys/wait.h>
00093 #endif
00094 
00095 #include "asterisk/logger.h"
00096 #include "asterisk/lock.h"
00097 #include "asterisk/file.h"
00098 #include "asterisk/channel.h"
00099 #include "asterisk/pbx.h"
00100 #include "asterisk/config.h"
00101 #include "asterisk/say.h"
00102 #include "asterisk/module.h"
00103 #include "asterisk/adsi.h"
00104 #include "asterisk/app.h"
00105 #include "asterisk/manager.h"
00106 #include "asterisk/dsp.h"
00107 #include "asterisk/localtime.h"
00108 #include "asterisk/cli.h"
00109 #include "asterisk/utils.h"
00110 #include "asterisk/stringfields.h"
00111 #include "asterisk/smdi.h"
00112 #include "asterisk/astobj2.h"
00113 #include "asterisk/event.h"
00114 #include "asterisk/taskprocessor.h"
00115 #include "asterisk/test.h"
00116 
00117 #ifdef ODBC_STORAGE
00118 #include "asterisk/res_odbc.h"
00119 #endif
00120 
00121 #ifdef IMAP_STORAGE
00122 #include "asterisk/threadstorage.h"
00123 #endif
00124 
00125 /*** DOCUMENTATION
00126    <application name="VoiceMail" language="en_US">
00127       <synopsis>
00128          Leave a Voicemail message.
00129       </synopsis>
00130       <syntax>
00131          <parameter name="mailboxs" argsep="&amp;" required="true">
00132             <argument name="mailbox1" argsep="@" required="true">
00133                <argument name="mailbox" required="true" />
00134                <argument name="context" />
00135             </argument>
00136             <argument name="mailbox2" argsep="@" multiple="true">
00137                <argument name="mailbox" required="true" />
00138                <argument name="context" />
00139             </argument>
00140          </parameter>
00141          <parameter name="options">
00142             <optionlist>
00143                <option name="b">
00144                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00145                </option>
00146                <option name="d">
00147                   <argument name="c" />
00148                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00149                   if played during the greeting. Context defaults to the current context.</para>
00150                </option>
00151                <option name="g">
00152                   <argument name="#" required="true" />
00153                   <para>Use the specified amount of gain when recording the voicemail
00154                   message. The units are whole-number decibels (dB). Only works on supported
00155                   technologies, which is DAHDI only.</para>
00156                </option>
00157                <option name="s">
00158                   <para>Skip the playback of instructions for leaving a message to the
00159                   calling party.</para>
00160                </option>
00161                <option name="u">
00162                   <para>Play the <literal>unavailable</literal> greeting.</para>
00163                </option>
00164                <option name="U">
00165                   <para>Mark message as <literal>URGENT</literal>.</para>
00166                </option>
00167                <option name="P">
00168                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00169                </option>
00170             </optionlist>
00171          </parameter>
00172       </syntax>
00173       <description>
00174          <para>This application allows the calling party to leave a message for the specified
00175          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00176          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00177          exist.</para>
00178          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00179          <enumlist>
00180             <enum name="0">
00181                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00182             </enum>
00183             <enum name="*">
00184                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00185             </enum>
00186          </enumlist>
00187          <para>This application will set the following channel variable upon completion:</para>
00188          <variablelist>
00189             <variable name="VMSTATUS">
00190                <para>This indicates the status of the execution of the VoiceMail application.</para>
00191                <value name="SUCCESS" />
00192                <value name="USEREXIT" />
00193                <value name="FAILED" />
00194             </variable>
00195          </variablelist>
00196       </description>
00197    </application>
00198    <application name="VoiceMailMain" language="en_US">
00199       <synopsis>
00200          Check Voicemail messages.
00201       </synopsis>
00202       <syntax>
00203          <parameter name="mailbox" required="true" argsep="@">
00204             <argument name="mailbox" />
00205             <argument name="context" />
00206          </parameter>
00207          <parameter name="options">
00208             <optionlist>
00209                <option name="p">
00210                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00211                   the mailbox that is entered by the caller.</para>
00212                </option>
00213                <option name="g">
00214                   <argument name="#" required="true" />
00215                   <para>Use the specified amount of gain when recording a voicemail message.
00216                   The units are whole-number decibels (dB).</para>
00217                </option>
00218                <option name="s">
00219                   <para>Skip checking the passcode for the mailbox.</para>
00220                </option>
00221                <option name="a">
00222                   <argument name="folder" required="true" />
00223                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00224                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00225                   <enumlist>
00226                      <enum name="0"><para>INBOX</para></enum>
00227                      <enum name="1"><para>Old</para></enum>
00228                      <enum name="2"><para>Work</para></enum>
00229                      <enum name="3"><para>Family</para></enum>
00230                      <enum name="4"><para>Friends</para></enum>
00231                      <enum name="5"><para>Cust1</para></enum>
00232                      <enum name="6"><para>Cust2</para></enum>
00233                      <enum name="7"><para>Cust3</para></enum>
00234                      <enum name="8"><para>Cust4</para></enum>
00235                      <enum name="9"><para>Cust5</para></enum>
00236                   </enumlist>
00237                </option>
00238             </optionlist>
00239          </parameter>
00240       </syntax>
00241       <description>
00242          <para>This application allows the calling party to check voicemail messages. A specific
00243          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00244          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00245          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00246          <literal>default</literal> context will be used.</para>
00247          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00248          or Password, and the extension exists:</para>
00249          <enumlist>
00250             <enum name="*">
00251                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00252             </enum>
00253          </enumlist>
00254       </description>
00255    </application>
00256    <application name="MailboxExists" language="en_US">
00257       <synopsis>
00258          Check to see if Voicemail mailbox exists.
00259       </synopsis>
00260       <syntax>
00261          <parameter name="mailbox" required="true" argsep="@">
00262             <argument name="mailbox" required="true" />
00263             <argument name="context" />
00264          </parameter>
00265          <parameter name="options">
00266             <para>None options.</para>
00267          </parameter>
00268       </syntax>
00269       <description>
00270          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00271          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00272          will be used.</para>
00273          <para>This application will set the following channel variable upon completion:</para>
00274          <variablelist>
00275             <variable name="VMBOXEXISTSSTATUS">
00276                <para>This will contain the status of the execution of the MailboxExists application.
00277                Possible values include:</para>
00278                <value name="SUCCESS" />
00279                <value name="FAILED" />
00280             </variable>
00281          </variablelist>
00282       </description>
00283    </application>
00284    <application name="VMAuthenticate" language="en_US">
00285       <synopsis>
00286          Authenticate with Voicemail passwords.
00287       </synopsis>
00288       <syntax>
00289          <parameter name="mailbox" required="true" argsep="@">
00290             <argument name="mailbox" />
00291             <argument name="context" />
00292          </parameter>
00293          <parameter name="options">
00294             <optionlist>
00295                <option name="s">
00296                   <para>Skip playing the initial prompts.</para>
00297                </option>
00298             </optionlist>
00299          </parameter>
00300       </syntax>
00301       <description>
00302          <para>This application behaves the same way as the Authenticate application, but the passwords
00303          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00304          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00305          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00306          mailbox.</para>
00307          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00308          or Password, and the extension exists:</para>
00309          <enumlist>
00310             <enum name="*">
00311                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00312             </enum>
00313          </enumlist>
00314       </description>
00315    </application>
00316    <application name="VMSayName" language="en_US">
00317       <synopsis>
00318          Play the name of a voicemail user
00319       </synopsis>
00320       <syntax>
00321          <parameter name="mailbox" required="true" argsep="@">
00322             <argument name="mailbox" />
00323             <argument name="context" />
00324          </parameter>
00325       </syntax>
00326       <description>
00327          <para>This application will say the recorded name of the voicemail user specified as the
00328          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00329       </description>
00330    </application>
00331    <function name="MAILBOX_EXISTS" language="en_US">
00332       <synopsis>
00333          Tell if a mailbox is configured.
00334       </synopsis>
00335       <syntax argsep="@">
00336          <parameter name="mailbox" required="true" />
00337          <parameter name="context" />
00338       </syntax>
00339       <description>
00340          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00341          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00342          context.</para>
00343       </description>
00344    </function>
00345    <manager name="VoicemailUsersList" language="en_US">
00346       <synopsis>
00347          List All Voicemail User Information.
00348       </synopsis>
00349       <syntax>
00350          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00351       </syntax>
00352       <description>
00353       </description>
00354    </manager>
00355  ***/
00356 
00357 #ifdef IMAP_STORAGE
00358 static char imapserver[48];
00359 static char imapport[8];
00360 static char imapflags[128];
00361 static char imapfolder[64];
00362 static char imapparentfolder[64] = "\0";
00363 static char greetingfolder[64];
00364 static char authuser[32];
00365 static char authpassword[42];
00366 static int imapversion = 1;
00367 
00368 static int expungeonhangup = 1;
00369 static int imapgreetings = 0;
00370 static char delimiter = '\0';
00371 
00372 struct vm_state;
00373 struct ast_vm_user;
00374 
00375 AST_THREADSTORAGE(ts_vmstate);
00376 
00377 /* Forward declarations for IMAP */
00378 static int init_mailstream(struct vm_state *vms, int box);
00379 static void write_file(char *filename, char *buffer, unsigned long len);
00380 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00381 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00382 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00383 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00384 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00385 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00386 static void vmstate_insert(struct vm_state *vms);
00387 static void vmstate_delete(struct vm_state *vms);
00388 static void set_update(MAILSTREAM * stream);
00389 static void init_vm_state(struct vm_state *vms);
00390 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00391 static void get_mailbox_delimiter(MAILSTREAM *stream);
00392 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00393 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00394 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);
00395 static void update_messages_by_imapuser(const char *user, unsigned long number);
00396 static int vm_delete(char *file);
00397 
00398 static int imap_remove_file (char *dir, int msgnum);
00399 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00400 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00401 static void check_quota(struct vm_state *vms, char *mailbox);
00402 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00403 struct vmstate {
00404    struct vm_state *vms;
00405    AST_LIST_ENTRY(vmstate) list;
00406 };
00407 
00408 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00409 
00410 #endif
00411 
00412 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00413 
00414 #define COMMAND_TIMEOUT 5000
00415 /* Don't modify these here; set your umask at runtime instead */
00416 #define  VOICEMAIL_DIR_MODE   0777
00417 #define  VOICEMAIL_FILE_MODE  0666
00418 #define  CHUNKSIZE   65536
00419 
00420 #define VOICEMAIL_CONFIG "voicemail.conf"
00421 #define ASTERISK_USERNAME "asterisk"
00422 
00423 /* Define fast-forward, pause, restart, and reverse keys
00424    while listening to a voicemail message - these are
00425    strings, not characters */
00426 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00427 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00428 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00429 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00430 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00431 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00432 
00433 /* Default mail command to mail voicemail. Change it with the
00434     mailcmd= command in voicemail.conf */
00435 #define SENDMAIL "/usr/sbin/sendmail -t"
00436 
00437 #define INTRO "vm-intro"
00438 
00439 #define MAXMSG 100
00440 #define MAXMSGLIMIT 9999
00441 
00442 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00443 
00444 #define BASELINELEN 72
00445 #define BASEMAXINLINE 256
00446 #ifdef IMAP_STORAGE
00447 #define ENDL "\r\n"
00448 #else
00449 #define ENDL "\n"
00450 #endif
00451 
00452 #define MAX_DATETIME_FORMAT   512
00453 #define MAX_NUM_CID_CONTEXTS 10
00454 
00455 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00456 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00457 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00458 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00459 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00460 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00461 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00462 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00463 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00464 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00465 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00466 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00467 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00468 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00469 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00470 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00471 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00472 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00473 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00474 #define ERROR_LOCK_PATH  -100
00475 #define OPERATOR_EXIT     300
00476 
00477 
00478 enum vm_box {
00479    NEW_FOLDER,
00480    OLD_FOLDER,
00481    WORK_FOLDER,
00482    FAMILY_FOLDER,
00483    FRIENDS_FOLDER,
00484    GREETINGS_FOLDER
00485 };
00486 
00487 enum vm_option_flags {
00488    OPT_SILENT =           (1 << 0),
00489    OPT_BUSY_GREETING =    (1 << 1),
00490    OPT_UNAVAIL_GREETING = (1 << 2),
00491    OPT_RECORDGAIN =       (1 << 3),
00492    OPT_PREPEND_MAILBOX =  (1 << 4),
00493    OPT_AUTOPLAY =         (1 << 6),
00494    OPT_DTMFEXIT =         (1 << 7),
00495    OPT_MESSAGE_Urgent =   (1 << 8),
00496    OPT_MESSAGE_PRIORITY = (1 << 9)
00497 };
00498 
00499 enum vm_option_args {
00500    OPT_ARG_RECORDGAIN = 0,
00501    OPT_ARG_PLAYFOLDER = 1,
00502    OPT_ARG_DTMFEXIT   = 2,
00503    /* This *must* be the last value in this enum! */
00504    OPT_ARG_ARRAY_SIZE = 3,
00505 };
00506 
00507 enum vm_passwordlocation {
00508    OPT_PWLOC_VOICEMAILCONF = 0,
00509    OPT_PWLOC_SPOOLDIR      = 1,
00510    OPT_PWLOC_USERSCONF     = 2,
00511 };
00512 
00513 AST_APP_OPTIONS(vm_app_options, {
00514    AST_APP_OPTION('s', OPT_SILENT),
00515    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00516    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00517    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00518    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00519    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00520    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00521    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00522    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00523 });
00524 
00525 static int load_config(int reload);
00526 
00527 /*! \page vmlang Voicemail Language Syntaxes Supported
00528 
00529    \par Syntaxes supported, not really language codes.
00530    \arg \b en    - English
00531    \arg \b de    - German
00532    \arg \b es    - Spanish
00533    \arg \b fr    - French
00534    \arg \b it    - Italian
00535    \arg \b nl    - Dutch
00536    \arg \b pt    - Portuguese
00537    \arg \b pt_BR - Portuguese (Brazil)
00538    \arg \b gr    - Greek
00539    \arg \b no    - Norwegian
00540    \arg \b se    - Swedish
00541    \arg \b tw    - Chinese (Taiwan)
00542    \arg \b ua - Ukrainian
00543 
00544 German requires the following additional soundfile:
00545 \arg \b 1F  einE (feminine)
00546 
00547 Spanish requires the following additional soundfile:
00548 \arg \b 1M      un (masculine)
00549 
00550 Dutch, Portuguese & Spanish require the following additional soundfiles:
00551 \arg \b vm-INBOXs singular of 'new'
00552 \arg \b vm-Olds      singular of 'old/heard/read'
00553 
00554 NB these are plural:
00555 \arg \b vm-INBOX  nieuwe (nl)
00556 \arg \b vm-Old    oude (nl)
00557 
00558 Polish uses:
00559 \arg \b vm-new-a  'new', feminine singular accusative
00560 \arg \b vm-new-e  'new', feminine plural accusative
00561 \arg \b vm-new-ych   'new', feminine plural genitive
00562 \arg \b vm-old-a  'old', feminine singular accusative
00563 \arg \b vm-old-e  'old', feminine plural accusative
00564 \arg \b vm-old-ych   'old', feminine plural genitive
00565 \arg \b digits/1-a   'one', not always same as 'digits/1'
00566 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00567 
00568 Swedish uses:
00569 \arg \b vm-nytt      singular of 'new'
00570 \arg \b vm-nya    plural of 'new'
00571 \arg \b vm-gammalt   singular of 'old'
00572 \arg \b vm-gamla  plural of 'old'
00573 \arg \b digits/ett   'one', not always same as 'digits/1'
00574 
00575 Norwegian uses:
00576 \arg \b vm-ny     singular of 'new'
00577 \arg \b vm-nye    plural of 'new'
00578 \arg \b vm-gammel singular of 'old'
00579 \arg \b vm-gamle  plural of 'old'
00580 
00581 Dutch also uses:
00582 \arg \b nl-om     'at'?
00583 
00584 Spanish also uses:
00585 \arg \b vm-youhaveno
00586 
00587 Italian requires the following additional soundfile:
00588 
00589 For vm_intro_it:
00590 \arg \b vm-nuovo  new
00591 \arg \b vm-nuovi  new plural
00592 \arg \b vm-vecchio   old
00593 \arg \b vm-vecchi old plural
00594 
00595 Chinese (Taiwan) requires the following additional soundfile:
00596 \arg \b vm-tong      A class-word for call (tong1)
00597 \arg \b vm-ri     A class-word for day (ri4)
00598 \arg \b vm-you    You (ni3)
00599 \arg \b vm-haveno   Have no (mei2 you3)
00600 \arg \b vm-have     Have (you3)
00601 \arg \b vm-listen   To listen (yao4 ting1)
00602 
00603 
00604 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00605 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00606 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00607 
00608 */
00609 
00610 struct baseio {
00611    int iocp;
00612    int iolen;
00613    int linelength;
00614    int ateof;
00615    unsigned char iobuf[BASEMAXINLINE];
00616 };
00617 
00618 /*! Structure for linked list of users 
00619  * Use ast_vm_user_destroy() to free one of these structures. */
00620 struct ast_vm_user {
00621    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00622    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00623    char password[80];               /*!< Secret pin code, numbers only */
00624    char fullname[80];               /*!< Full name, for directory app */
00625    char email[80];                  /*!< E-mail address */
00626    char *emailsubject;              /*!< E-mail subject */
00627    char *emailbody;                 /*!< E-mail body */
00628    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00629    char serveremail[80];            /*!< From: Mail address */
00630    char mailcmd[160];               /*!< Configurable mail command */
00631    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00632    char zonetag[80];                /*!< Time zone */
00633    char locale[20];                 /*!< The locale (for presentation of date/time) */
00634    char callback[80];
00635    char dialout[80];
00636    char uniqueid[80];               /*!< Unique integer identifier */
00637    char exit[80];
00638    char attachfmt[20];              /*!< Attachment format */
00639    unsigned int flags;              /*!< VM_ flags */ 
00640    int saydurationm;
00641    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00642    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00643    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00644    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00645    int passwordlocation;            /*!< Storage location of the password */
00646 #ifdef IMAP_STORAGE
00647    char imapuser[80];               /*!< IMAP server login */
00648    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00649    char imapfolder[64];             /*!< IMAP voicemail folder */
00650    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00651    int imapversion;                 /*!< If configuration changes, use the new values */
00652 #endif
00653    double volgain;                  /*!< Volume gain for voicemails sent via email */
00654    AST_LIST_ENTRY(ast_vm_user) list;
00655 };
00656 
00657 /*! Voicemail time zones */
00658 struct vm_zone {
00659    AST_LIST_ENTRY(vm_zone) list;
00660    char name[80];
00661    char timezone[80];
00662    char msg_format[512];
00663 };
00664 
00665 #define VMSTATE_MAX_MSG_ARRAY 256
00666 
00667 /*! Voicemail mailbox state */
00668 struct vm_state {
00669    char curbox[80];
00670    char username[80];
00671    char context[80];
00672    char curdir[PATH_MAX];
00673    char vmbox[PATH_MAX];
00674    char fn[PATH_MAX];
00675    char intro[PATH_MAX];
00676    int *deleted;
00677    int *heard;
00678    int dh_arraysize; /* used for deleted / heard allocation */
00679    int curmsg;
00680    int lastmsg;
00681    int newmessages;
00682    int oldmessages;
00683    int urgentmessages;
00684    int starting;
00685    int repeats;
00686 #ifdef IMAP_STORAGE
00687    ast_mutex_t lock;
00688    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00689    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00690    MAILSTREAM *mailstream;
00691    int vmArrayIndex;
00692    char imapuser[80];                   /*!< IMAP server login */
00693    char imapfolder[64];                 /*!< IMAP voicemail folder */
00694    int imapversion;
00695    int interactive;
00696    char introfn[PATH_MAX];              /*!< Name of prepended file */
00697    unsigned int quota_limit;
00698    unsigned int quota_usage;
00699    struct vm_state *persist_vms;
00700 #endif
00701 };
00702 
00703 #ifdef ODBC_STORAGE
00704 static char odbc_database[80];
00705 static char odbc_table[80];
00706 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00707 #define DISPOSE(a,b) remove_file(a,b)
00708 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00709 #define EXISTS(a,b,c,d) (message_exists(a,b))
00710 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00711 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00712 #define DELETE(a,b,c,d) (delete_file(a,b))
00713 #else
00714 #ifdef IMAP_STORAGE
00715 #define DISPOSE(a,b) (imap_remove_file(a,b))
00716 #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))
00717 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00718 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00719 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00720 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00721 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00722 #else
00723 #define RETRIEVE(a,b,c,d)
00724 #define DISPOSE(a,b)
00725 #define STORE(a,b,c,d,e,f,g,h,i,j)
00726 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00727 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00728 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00729 #define DELETE(a,b,c,d) (vm_delete(c))
00730 #endif
00731 #endif
00732 
00733 static char VM_SPOOL_DIR[PATH_MAX];
00734 
00735 static char ext_pass_cmd[128];
00736 static char ext_pass_check_cmd[128];
00737 
00738 static int my_umask;
00739 
00740 #define PWDCHANGE_INTERNAL (1 << 1)
00741 #define PWDCHANGE_EXTERNAL (1 << 2)
00742 static int pwdchange = PWDCHANGE_INTERNAL;
00743 
00744 #ifdef ODBC_STORAGE
00745 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00746 #else
00747 # ifdef IMAP_STORAGE
00748 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00749 # else
00750 # define tdesc "Comedian Mail (Voicemail System)"
00751 # endif
00752 #endif
00753 
00754 static char userscontext[AST_MAX_EXTENSION] = "default";
00755 
00756 static char *addesc = "Comedian Mail";
00757 
00758 /* Leave a message */
00759 static char *app = "VoiceMail";
00760 
00761 /* Check mail, control, etc */
00762 static char *app2 = "VoiceMailMain";
00763 
00764 static char *app3 = "MailboxExists";
00765 static char *app4 = "VMAuthenticate";
00766 
00767 static char *sayname_app = "VMSayName";
00768 
00769 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00770 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00771 static char zonetag[80];
00772 static char locale[20];
00773 static int maxsilence;
00774 static int maxmsg;
00775 static int maxdeletedmsg;
00776 static int silencethreshold = 128;
00777 static char serveremail[80];
00778 static char mailcmd[160];  /* Configurable mail cmd */
00779 static char externnotify[160]; 
00780 static struct ast_smdi_interface *smdi_iface = NULL;
00781 static char vmfmts[80];
00782 static double volgain;
00783 static int vmminsecs;
00784 static int vmmaxsecs;
00785 static int maxgreet;
00786 static int skipms;
00787 static int maxlogins;
00788 static int minpassword;
00789 static int passwordlocation;
00790 
00791 /*! Poll mailboxes for changes since there is something external to
00792  *  app_voicemail that may change them. */
00793 static unsigned int poll_mailboxes;
00794 
00795 /*! Polling frequency */
00796 static unsigned int poll_freq;
00797 /*! By default, poll every 30 seconds */
00798 #define DEFAULT_POLL_FREQ 30
00799 
00800 AST_MUTEX_DEFINE_STATIC(poll_lock);
00801 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00802 static pthread_t poll_thread = AST_PTHREADT_NULL;
00803 static unsigned char poll_thread_run;
00804 
00805 /*! Subscription to ... MWI event subscriptions */
00806 static struct ast_event_sub *mwi_sub_sub;
00807 /*! Subscription to ... MWI event un-subscriptions */
00808 static struct ast_event_sub *mwi_unsub_sub;
00809 
00810 /*!
00811  * \brief An MWI subscription
00812  *
00813  * This is so we can keep track of which mailboxes are subscribed to.
00814  * This way, we know which mailboxes to poll when the pollmailboxes
00815  * option is being used.
00816  */
00817 struct mwi_sub {
00818    AST_RWLIST_ENTRY(mwi_sub) entry;
00819    int old_urgent;
00820    int old_new;
00821    int old_old;
00822    uint32_t uniqueid;
00823    char mailbox[1];
00824 };
00825 
00826 struct mwi_sub_task {
00827    const char *mailbox;
00828    const char *context;
00829    uint32_t uniqueid;
00830 };
00831 
00832 static struct ast_taskprocessor *mwi_subscription_tps;
00833 
00834 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00835 
00836 /* custom audio control prompts for voicemail playback */
00837 static char listen_control_forward_key[12];
00838 static char listen_control_reverse_key[12];
00839 static char listen_control_pause_key[12];
00840 static char listen_control_restart_key[12];
00841 static char listen_control_stop_key[12];
00842 
00843 /* custom password sounds */
00844 static char vm_password[80] = "vm-password";
00845 static char vm_newpassword[80] = "vm-newpassword";
00846 static char vm_passchanged[80] = "vm-passchanged";
00847 static char vm_reenterpassword[80] = "vm-reenterpassword";
00848 static char vm_mismatch[80] = "vm-mismatch";
00849 static char vm_invalid_password[80] = "vm-invalid-password";
00850 static char vm_pls_try_again[80] = "vm-pls-try-again";
00851 
00852 static struct ast_flags globalflags = {0};
00853 
00854 static int saydurationminfo;
00855 
00856 static char dialcontext[AST_MAX_CONTEXT] = "";
00857 static char callcontext[AST_MAX_CONTEXT] = "";
00858 static char exitcontext[AST_MAX_CONTEXT] = "";
00859 
00860 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00861 
00862 
00863 static char *emailbody = NULL;
00864 static char *emailsubject = NULL;
00865 static char *pagerbody = NULL;
00866 static char *pagersubject = NULL;
00867 static char fromstring[100];
00868 static char pagerfromstring[100];
00869 static char charset[32] = "ISO-8859-1";
00870 
00871 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00872 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00873 static int adsiver = 1;
00874 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00875 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00876 
00877 /* Forward declarations - generic */
00878 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00879 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);
00880 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00881 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00882          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00883          signed char record_gain, struct vm_state *vms, char *flag);
00884 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00885 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00886 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);
00887 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);
00888 static void apply_options(struct ast_vm_user *vmu, const char *options);
00889 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);
00890 static int is_valid_dtmf(const char *key);
00891 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00892 static int write_password_to_file(const char *secretfn, const char *password);
00893 
00894 struct ao2_container *inprocess_container;
00895 
00896 struct inprocess {
00897    int count;
00898    char *context;
00899    char mailbox[0];
00900 };
00901 
00902 static int inprocess_hash_fn(const void *obj, const int flags)
00903 {
00904    const struct inprocess *i = obj;
00905    return atoi(i->mailbox);
00906 }
00907 
00908 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00909 {
00910    struct inprocess *i = obj, *j = arg;
00911    if (strcmp(i->mailbox, j->mailbox)) {
00912       return 0;
00913    }
00914    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00915 }
00916 
00917 static int inprocess_count(const char *context, const char *mailbox, int delta)
00918 {
00919    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00920    arg->context = arg->mailbox + strlen(mailbox) + 1;
00921    strcpy(arg->mailbox, mailbox); /* SAFE */
00922    strcpy(arg->context, context); /* SAFE */
00923    ao2_lock(inprocess_container);
00924    if ((i = ao2_find(inprocess_container, arg, 0))) {
00925       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00926       ao2_unlock(inprocess_container);
00927       ao2_ref(i, -1);
00928       return ret;
00929    }
00930    if (delta < 0) {
00931       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00932    }
00933    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00934       ao2_unlock(inprocess_container);
00935       return 0;
00936    }
00937    i->context = i->mailbox + strlen(mailbox) + 1;
00938    strcpy(i->mailbox, mailbox); /* SAFE */
00939    strcpy(i->context, context); /* SAFE */
00940    i->count = delta;
00941    ao2_link(inprocess_container, i);
00942    ao2_unlock(inprocess_container);
00943    ao2_ref(i, -1);
00944    return 0;
00945 }
00946 
00947 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00948 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00949 #endif
00950 
00951 /*!
00952  * \brief Strips control and non 7-bit clean characters from input string.
00953  *
00954  * \note To map control and none 7-bit characters to a 7-bit clean characters
00955  *  please use ast_str_encode_mine().
00956  */
00957 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00958 {
00959    char *bufptr = buf;
00960    for (; *input; input++) {
00961       if (*input < 32) {
00962          continue;
00963       }
00964       *bufptr++ = *input;
00965       if (bufptr == buf + buflen - 1) {
00966          break;
00967       }
00968    }
00969    *bufptr = '\0';
00970    return buf;
00971 }
00972 
00973 
00974 /*!
00975  * \brief Sets default voicemail system options to a voicemail user.
00976  *
00977  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00978  * - all the globalflags
00979  * - the saydurationminfo
00980  * - the callcontext
00981  * - the dialcontext
00982  * - the exitcontext
00983  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00984  * - volume gain.
00985  */
00986 static void populate_defaults(struct ast_vm_user *vmu)
00987 {
00988    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00989    vmu->passwordlocation = passwordlocation;
00990    if (saydurationminfo) {
00991       vmu->saydurationm = saydurationminfo;
00992    }
00993    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00994    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00995    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00996    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00997    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
00998    if (vmminsecs) {
00999       vmu->minsecs = vmminsecs;
01000    }
01001    if (vmmaxsecs) {
01002       vmu->maxsecs = vmmaxsecs;
01003    }
01004    if (maxmsg) {
01005       vmu->maxmsg = maxmsg;
01006    }
01007    if (maxdeletedmsg) {
01008       vmu->maxdeletedmsg = maxdeletedmsg;
01009    }
01010    vmu->volgain = volgain;
01011    vmu->emailsubject = NULL;
01012    vmu->emailbody = NULL;
01013 #ifdef IMAP_STORAGE
01014    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01015 #endif
01016 }
01017 
01018 /*!
01019  * \brief Sets a a specific property value.
01020  * \param vmu The voicemail user object to work with.
01021  * \param var The name of the property to be set.
01022  * \param value The value to be set to the property.
01023  * 
01024  * The property name must be one of the understood properties. See the source for details.
01025  */
01026 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01027 {
01028    int x;
01029    if (!strcasecmp(var, "attach")) {
01030       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01031    } else if (!strcasecmp(var, "attachfmt")) {
01032       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01033    } else if (!strcasecmp(var, "serveremail")) {
01034       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01035    } else if (!strcasecmp(var, "language")) {
01036       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01037    } else if (!strcasecmp(var, "tz")) {
01038       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01039    } else if (!strcasecmp(var, "locale")) {
01040       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01041 #ifdef IMAP_STORAGE
01042    } else if (!strcasecmp(var, "imapuser")) {
01043       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01044       vmu->imapversion = imapversion;
01045    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01046       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01047       vmu->imapversion = imapversion;
01048    } else if (!strcasecmp(var, "imapfolder")) {
01049       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01050    } else if (!strcasecmp(var, "imapvmshareid")) {
01051       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01052       vmu->imapversion = imapversion;
01053 #endif
01054    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01055       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01056    } else if (!strcasecmp(var, "saycid")){
01057       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01058    } else if (!strcasecmp(var, "sendvoicemail")){
01059       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01060    } else if (!strcasecmp(var, "review")){
01061       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01062    } else if (!strcasecmp(var, "tempgreetwarn")){
01063       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01064    } else if (!strcasecmp(var, "messagewrap")){
01065       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01066    } else if (!strcasecmp(var, "operator")) {
01067       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01068    } else if (!strcasecmp(var, "envelope")){
01069       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01070    } else if (!strcasecmp(var, "moveheard")){
01071       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01072    } else if (!strcasecmp(var, "sayduration")){
01073       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01074    } else if (!strcasecmp(var, "saydurationm")){
01075       if (sscanf(value, "%30d", &x) == 1) {
01076          vmu->saydurationm = x;
01077       } else {
01078          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01079       }
01080    } else if (!strcasecmp(var, "forcename")){
01081       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01082    } else if (!strcasecmp(var, "forcegreetings")){
01083       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01084    } else if (!strcasecmp(var, "callback")) {
01085       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01086    } else if (!strcasecmp(var, "dialout")) {
01087       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01088    } else if (!strcasecmp(var, "exitcontext")) {
01089       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01090    } else if (!strcasecmp(var, "minsecs")) {
01091       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01092          vmu->minsecs = x;
01093       } else {
01094          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01095          vmu->minsecs = vmminsecs;
01096       }
01097    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01098       vmu->maxsecs = atoi(value);
01099       if (vmu->maxsecs <= 0) {
01100          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01101          vmu->maxsecs = vmmaxsecs;
01102       } else {
01103          vmu->maxsecs = atoi(value);
01104       }
01105       if (!strcasecmp(var, "maxmessage"))
01106          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01107    } else if (!strcasecmp(var, "maxmsg")) {
01108       vmu->maxmsg = atoi(value);
01109       /* Accept maxmsg=0 (Greetings only voicemail) */
01110       if (vmu->maxmsg < 0) {
01111          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01112          vmu->maxmsg = MAXMSG;
01113       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01114          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01115          vmu->maxmsg = MAXMSGLIMIT;
01116       }
01117    } else if (!strcasecmp(var, "nextaftercmd")) {
01118       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01119    } else if (!strcasecmp(var, "backupdeleted")) {
01120       if (sscanf(value, "%30d", &x) == 1)
01121          vmu->maxdeletedmsg = x;
01122       else if (ast_true(value))
01123          vmu->maxdeletedmsg = MAXMSG;
01124       else
01125          vmu->maxdeletedmsg = 0;
01126 
01127       if (vmu->maxdeletedmsg < 0) {
01128          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01129          vmu->maxdeletedmsg = MAXMSG;
01130       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01131          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01132          vmu->maxdeletedmsg = MAXMSGLIMIT;
01133       }
01134    } else if (!strcasecmp(var, "volgain")) {
01135       sscanf(value, "%30lf", &vmu->volgain);
01136    } else if (!strcasecmp(var, "passwordlocation")) {
01137       if (!strcasecmp(value, "spooldir")) {
01138          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01139       } else {
01140          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01141       }
01142    } else if (!strcasecmp(var, "options")) {
01143       apply_options(vmu, value);
01144    }
01145 }
01146 
01147 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01148 {
01149    int fds[2], pid = 0;
01150 
01151    memset(buf, 0, len);
01152 
01153    if (pipe(fds)) {
01154       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01155    } else {
01156       /* good to go*/
01157       pid = ast_safe_fork(0);
01158 
01159       if (pid < 0) {
01160          /* ok maybe not */
01161          close(fds[0]);
01162          close(fds[1]);
01163          snprintf(buf, len, "FAILURE: Fork failed");
01164       } else if (pid) {
01165          /* parent */
01166          close(fds[1]);
01167          if (read(fds[0], buf, len) < 0) {
01168             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01169          }
01170          close(fds[0]);
01171       } else {
01172          /*  child */
01173          AST_DECLARE_APP_ARGS(arg,
01174             AST_APP_ARG(v)[20];
01175          );
01176          char *mycmd = ast_strdupa(command);
01177 
01178          close(fds[0]);
01179          dup2(fds[1], STDOUT_FILENO);
01180          close(fds[1]);
01181          ast_close_fds_above_n(STDOUT_FILENO);
01182 
01183          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01184 
01185          execv(arg.v[0], arg.v); 
01186          printf("FAILURE: %s", strerror(errno));
01187          _exit(0);
01188       }
01189    }
01190    return buf;
01191 }
01192 
01193 /*!
01194  * \brief Check that password meets minimum required length
01195  * \param vmu The voicemail user to change the password for.
01196  * \param password The password string to check
01197  *
01198  * \return zero on ok, 1 on not ok.
01199  */
01200 static int check_password(struct ast_vm_user *vmu, char *password)
01201 {
01202    /* check minimum length */
01203    if (strlen(password) < minpassword)
01204       return 1;
01205    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01206       char cmd[255], buf[255];
01207 
01208       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01209 
01210       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01211       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01212          ast_debug(5, "Result: %s\n", buf);
01213          if (!strncasecmp(buf, "VALID", 5)) {
01214             ast_debug(3, "Passed password check: '%s'\n", buf);
01215             return 0;
01216          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01217             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01218             return 0;
01219          } else {
01220             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01221             return 1;
01222          }
01223       }
01224    }
01225    return 0;
01226 }
01227 
01228 /*! 
01229  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01230  * \param vmu The voicemail user to change the password for.
01231  * \param password The new value to be set to the password for this user.
01232  * 
01233  * This only works if there is a realtime engine configured.
01234  * This is called from the (top level) vm_change_password.
01235  *
01236  * \return zero on success, -1 on error.
01237  */
01238 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01239 {
01240    int res = -1;
01241    if (!strcmp(vmu->password, password)) {
01242       /* No change (but an update would return 0 rows updated, so we opt out here) */
01243       return 0;
01244    }
01245 
01246    if (strlen(password) > 10) {
01247       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01248    }
01249    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01250       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01251       res = 0;
01252    }
01253    return res;
01254 }
01255 
01256 /*!
01257  * \brief Destructively Parse options and apply.
01258  */
01259 static void apply_options(struct ast_vm_user *vmu, const char *options)
01260 {  
01261    char *stringp;
01262    char *s;
01263    char *var, *value;
01264    stringp = ast_strdupa(options);
01265    while ((s = strsep(&stringp, "|"))) {
01266       value = s;
01267       if ((var = strsep(&value, "=")) && value) {
01268          apply_option(vmu, var, value);
01269       }
01270    }  
01271 }
01272 
01273 /*!
01274  * \brief Loads the options specific to a voicemail user.
01275  * 
01276  * This is called when a vm_user structure is being set up, such as from load_options.
01277  */
01278 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01279 {
01280    for (; var; var = var->next) {
01281       if (!strcasecmp(var->name, "vmsecret")) {
01282          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01283       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01284          if (ast_strlen_zero(retval->password))
01285             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01286       } else if (!strcasecmp(var->name, "uniqueid")) {
01287          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01288       } else if (!strcasecmp(var->name, "pager")) {
01289          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01290       } else if (!strcasecmp(var->name, "email")) {
01291          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01292       } else if (!strcasecmp(var->name, "fullname")) {
01293          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01294       } else if (!strcasecmp(var->name, "context")) {
01295          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01296       } else if (!strcasecmp(var->name, "emailsubject")) {
01297          retval->emailsubject = ast_strdup(var->value);
01298       } else if (!strcasecmp(var->name, "emailbody")) {
01299          retval->emailbody = ast_strdup(var->value);
01300 #ifdef IMAP_STORAGE
01301       } else if (!strcasecmp(var->name, "imapuser")) {
01302          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01303          retval->imapversion = imapversion;
01304       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01305          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01306          retval->imapversion = imapversion;
01307       } else if (!strcasecmp(var->name, "imapfolder")) {
01308          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01309       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01310          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01311          retval->imapversion = imapversion;
01312 #endif
01313       } else
01314          apply_option(retval, var->name, var->value);
01315    }
01316 }
01317 
01318 /*!
01319  * \brief Determines if a DTMF key entered is valid.
01320  * \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.
01321  *
01322  * Tests the character entered against the set of valid DTMF characters. 
01323  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01324  */
01325 static int is_valid_dtmf(const char *key)
01326 {
01327    int i;
01328    char *local_key = ast_strdupa(key);
01329 
01330    for (i = 0; i < strlen(key); ++i) {
01331       if (!strchr(VALID_DTMF, *local_key)) {
01332          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01333          return 0;
01334       }
01335       local_key++;
01336    }
01337    return 1;
01338 }
01339 
01340 /*!
01341  * \brief Finds a voicemail user from the realtime engine.
01342  * \param ivm
01343  * \param context
01344  * \param mailbox
01345  *
01346  * 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.
01347  *
01348  * \return The ast_vm_user structure for the user that was found.
01349  */
01350 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01351 {
01352    struct ast_variable *var;
01353    struct ast_vm_user *retval;
01354 
01355    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01356       if (!ivm)
01357          ast_set_flag(retval, VM_ALLOCED);   
01358       else
01359          memset(retval, 0, sizeof(*retval));
01360       if (mailbox) 
01361          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01362       populate_defaults(retval);
01363       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01364          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01365       else
01366          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01367       if (var) {
01368          apply_options_full(retval, var);
01369          ast_variables_destroy(var);
01370       } else { 
01371          if (!ivm) 
01372             ast_free(retval);
01373          retval = NULL;
01374       }  
01375    } 
01376    return retval;
01377 }
01378 
01379 /*!
01380  * \brief Finds a voicemail user from the users file or the realtime engine.
01381  * \param ivm
01382  * \param context
01383  * \param mailbox
01384  * 
01385  * \return The ast_vm_user structure for the user that was found.
01386  */
01387 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01388 {
01389    /* This function could be made to generate one from a database, too */
01390    struct ast_vm_user *vmu = NULL, *cur;
01391    AST_LIST_LOCK(&users);
01392 
01393    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01394       context = "default";
01395 
01396    AST_LIST_TRAVERSE(&users, cur, list) {
01397 #ifdef IMAP_STORAGE
01398       if (cur->imapversion != imapversion) {
01399          continue;
01400       }
01401 #endif
01402       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01403          break;
01404       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01405          break;
01406    }
01407    if (cur) {
01408       /* Make a copy, so that on a reload, we have no race */
01409       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01410          memcpy(vmu, cur, sizeof(*vmu));
01411          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01412          AST_LIST_NEXT(vmu, list) = NULL;
01413       }
01414    } else
01415       vmu = find_user_realtime(ivm, context, mailbox);
01416    AST_LIST_UNLOCK(&users);
01417    return vmu;
01418 }
01419 
01420 /*!
01421  * \brief Resets a user password to a specified password.
01422  * \param context
01423  * \param mailbox
01424  * \param newpass
01425  *
01426  * This does the actual change password work, called by the vm_change_password() function.
01427  *
01428  * \return zero on success, -1 on error.
01429  */
01430 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01431 {
01432    /* This function could be made to generate one from a database, too */
01433    struct ast_vm_user *cur;
01434    int res = -1;
01435    AST_LIST_LOCK(&users);
01436    AST_LIST_TRAVERSE(&users, cur, list) {
01437       if ((!context || !strcasecmp(context, cur->context)) &&
01438          (!strcasecmp(mailbox, cur->mailbox)))
01439             break;
01440    }
01441    if (cur) {
01442       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01443       res = 0;
01444    }
01445    AST_LIST_UNLOCK(&users);
01446    return res;
01447 }
01448 
01449 /*! 
01450  * \brief The handler for the change password option.
01451  * \param vmu The voicemail user to work with.
01452  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01453  * 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.
01454  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01455  */
01456 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01457 {
01458    struct ast_config   *cfg = NULL;
01459    struct ast_variable *var = NULL;
01460    struct ast_category *cat = NULL;
01461    char *category = NULL, *value = NULL, *new = NULL;
01462    const char *tmp = NULL;
01463    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01464    char secretfn[PATH_MAX] = "";
01465    int found = 0;
01466 
01467    if (!change_password_realtime(vmu, newpassword))
01468       return;
01469 
01470    /* check if we should store the secret in the spool directory next to the messages */
01471    switch (vmu->passwordlocation) {
01472    case OPT_PWLOC_SPOOLDIR:
01473       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01474       if (write_password_to_file(secretfn, newpassword) == 0) {
01475          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01476          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01477          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01478          break;
01479       } else {
01480          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01481       }
01482       /* Fall-through */
01483    case OPT_PWLOC_VOICEMAILCONF:
01484       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01485          while ((category = ast_category_browse(cfg, category))) {
01486             if (!strcasecmp(category, vmu->context)) {
01487                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01488                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01489                   break;
01490                }
01491                value = strstr(tmp, ",");
01492                if (!value) {
01493                   new = alloca(strlen(newpassword)+1);
01494                   sprintf(new, "%s", newpassword);
01495                } else {
01496                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01497                   sprintf(new, "%s%s", newpassword, value);
01498                }
01499                if (!(cat = ast_category_get(cfg, category))) {
01500                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01501                   break;
01502                }
01503                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01504                found = 1;
01505             }
01506          }
01507          /* save the results */
01508          if (found) {
01509             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01510             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01511             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01512             break;
01513          }
01514       }
01515       /* Fall-through */
01516    case OPT_PWLOC_USERSCONF:
01517       /* check users.conf and update the password stored for the mailbox */
01518       /* if no vmsecret entry exists create one. */
01519       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01520          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01521          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01522             ast_debug(4, "users.conf: %s\n", category);
01523             if (!strcasecmp(category, vmu->mailbox)) {
01524                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01525                   ast_debug(3, "looks like we need to make vmsecret!\n");
01526                   var = ast_variable_new("vmsecret", newpassword, "");
01527                } else {
01528                   var = NULL;
01529                }
01530                new = alloca(strlen(newpassword) + 1);
01531                sprintf(new, "%s", newpassword);
01532                if (!(cat = ast_category_get(cfg, category))) {
01533                   ast_debug(4, "failed to get category!\n");
01534                   ast_free(var);
01535                   break;
01536                }
01537                if (!var) {
01538                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01539                } else {
01540                   ast_variable_append(cat, var);
01541                }
01542                found = 1;
01543                break;
01544             }
01545          }
01546          /* save the results and clean things up */
01547          if (found) {
01548             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01549             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01550             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01551          }
01552       }
01553    }
01554 }
01555 
01556 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01557 {
01558    char buf[255];
01559    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01560    if (!ast_safe_system(buf)) {
01561       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01562       /* Reset the password in memory, too */
01563       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01564    }
01565 }
01566 
01567 /*! 
01568  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01569  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01570  * \param len The length of the path string that was written out.
01571  * \param context
01572  * \param ext 
01573  * \param folder 
01574  * 
01575  * The path is constructed as 
01576  *    VM_SPOOL_DIRcontext/ext/folder
01577  *
01578  * \return zero on success, -1 on error.
01579  */
01580 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01581 {
01582    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01583 }
01584 
01585 /*! 
01586  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01587  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01588  * \param len The length of the path string that was written out.
01589  * \param dir 
01590  * \param num 
01591  * 
01592  * The path is constructed as 
01593  *    VM_SPOOL_DIRcontext/ext/folder
01594  *
01595  * \return zero on success, -1 on error.
01596  */
01597 static int make_file(char *dest, const int len, const char *dir, const int num)
01598 {
01599    return snprintf(dest, len, "%s/msg%04d", dir, num);
01600 }
01601 
01602 /* same as mkstemp, but return a FILE * */
01603 static FILE *vm_mkftemp(char *template)
01604 {
01605    FILE *p = NULL;
01606    int pfd = mkstemp(template);
01607    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01608    if (pfd > -1) {
01609       p = fdopen(pfd, "w+");
01610       if (!p) {
01611          close(pfd);
01612          pfd = -1;
01613       }
01614    }
01615    return p;
01616 }
01617 
01618 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01619  * \param dest    String. base directory.
01620  * \param len     Length of dest.
01621  * \param context String. Ignored if is null or empty string.
01622  * \param ext     String. Ignored if is null or empty string.
01623  * \param folder  String. Ignored if is null or empty string. 
01624  * \return -1 on failure, 0 on success.
01625  */
01626 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01627 {
01628    mode_t   mode = VOICEMAIL_DIR_MODE;
01629    int res;
01630 
01631    make_dir(dest, len, context, ext, folder);
01632    if ((res = ast_mkdir(dest, mode))) {
01633       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01634       return -1;
01635    }
01636    return 0;
01637 }
01638 
01639 static const char * const mailbox_folders[] = {
01640 #ifdef IMAP_STORAGE
01641    imapfolder,
01642 #else
01643    "INBOX",
01644 #endif
01645    "Old",
01646    "Work",
01647    "Family",
01648    "Friends",
01649    "Cust1",
01650    "Cust2",
01651    "Cust3",
01652    "Cust4",
01653    "Cust5",
01654    "Deleted",
01655    "Urgent",
01656 };
01657 
01658 static const char *mbox(struct ast_vm_user *vmu, int id)
01659 {
01660 #ifdef IMAP_STORAGE
01661    if (vmu && id == 0) {
01662       return vmu->imapfolder;
01663    }
01664 #endif
01665    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01666 }
01667 
01668 static int get_folder_by_name(const char *name)
01669 {
01670    size_t i;
01671 
01672    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01673       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01674          return i;
01675       }
01676    }
01677 
01678    return -1;
01679 }
01680 
01681 static void free_user(struct ast_vm_user *vmu)
01682 {
01683    if (ast_test_flag(vmu, VM_ALLOCED)) {
01684       if (vmu->emailbody != NULL) {
01685          ast_free(vmu->emailbody);
01686          vmu->emailbody = NULL;
01687       }
01688       if (vmu->emailsubject != NULL) {
01689          ast_free(vmu->emailsubject);
01690          vmu->emailsubject = NULL;
01691       }
01692       ast_free(vmu);
01693    }
01694 }
01695 
01696 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01697 
01698    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01699    if (!vms->dh_arraysize) {
01700       /* initial allocation */
01701       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01702          return -1;
01703       }
01704       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01705          return -1;
01706       }
01707       vms->dh_arraysize = arraysize;
01708    } else if (vms->dh_arraysize < arraysize) {
01709       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01710          return -1;
01711       }
01712       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01713          return -1;
01714       }
01715       memset(vms->deleted, 0, arraysize * sizeof(int));
01716       memset(vms->heard, 0, arraysize * sizeof(int));
01717       vms->dh_arraysize = arraysize;
01718    }
01719 
01720    return 0;
01721 }
01722 
01723 /* All IMAP-specific functions should go in this block. This
01724  * keeps them from being spread out all over the code */
01725 #ifdef IMAP_STORAGE
01726 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01727 {
01728    char arg[10];
01729    struct vm_state *vms;
01730    unsigned long messageNum;
01731 
01732    /* If greetings aren't stored in IMAP, just delete the file */
01733    if (msgnum < 0 && !imapgreetings) {
01734       ast_filedelete(file, NULL);
01735       return;
01736    }
01737 
01738    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01739       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);
01740       return;
01741    }
01742 
01743    /* find real message number based on msgnum */
01744    /* this may be an index into vms->msgArray based on the msgnum. */
01745    messageNum = vms->msgArray[msgnum];
01746    if (messageNum == 0) {
01747       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01748       return;
01749    }
01750    if (option_debug > 2)
01751       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01752    /* delete message */
01753    snprintf (arg, sizeof(arg), "%lu", messageNum);
01754    ast_mutex_lock(&vms->lock);
01755    mail_setflag (vms->mailstream, arg, "\\DELETED");
01756    mail_expunge(vms->mailstream);
01757    ast_mutex_unlock(&vms->lock);
01758 }
01759 
01760 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01761 {
01762    struct vm_state *vms_p;
01763    char *file, *filename;
01764    char *attachment;
01765    int ret = 0, i;
01766    BODY *body;
01767 
01768    /* This function is only used for retrieval of IMAP greetings
01769     * regular messages are not retrieved this way, nor are greetings
01770     * if they are stored locally*/
01771    if (msgnum > -1 || !imapgreetings) {
01772       return 0;
01773    } else {
01774       file = strrchr(ast_strdupa(dir), '/');
01775       if (file)
01776          *file++ = '\0';
01777       else {
01778          ast_debug (1, "Failed to procure file name from directory passed.\n");
01779          return -1;
01780       }
01781    }
01782 
01783    /* check if someone is accessing this box right now... */
01784    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01785       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01786       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01787       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01788       * that's all we need to do.
01789       */
01790       if (!(vms_p = create_vm_state_from_user(vmu))) {
01791          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01792          return -1;
01793       }
01794    }
01795    
01796    /* Greetings will never have a prepended message */
01797    *vms_p->introfn = '\0';
01798 
01799    ast_mutex_lock(&vms_p->lock);
01800    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01801    if (!vms_p->mailstream) {
01802       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01803       ast_mutex_unlock(&vms_p->lock);
01804       return -1;
01805    }
01806 
01807    /*XXX Yuck, this could probably be done a lot better */
01808    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01809       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01810       /* We have the body, now we extract the file name of the first attachment. */
01811       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01812          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01813       } else {
01814          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01815          ast_mutex_unlock(&vms_p->lock);
01816          return -1;
01817       }
01818       filename = strsep(&attachment, ".");
01819       if (!strcmp(filename, file)) {
01820          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01821          vms_p->msgArray[vms_p->curmsg] = i + 1;
01822          save_body(body, vms_p, "2", attachment, 0);
01823          ast_mutex_unlock(&vms_p->lock);
01824          return 0;
01825       }
01826    }
01827    ast_mutex_unlock(&vms_p->lock);
01828 
01829    return -1;
01830 }
01831 
01832 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01833 {
01834    BODY *body;
01835    char *header_content;
01836    char *attachedfilefmt;
01837    char buf[80];
01838    struct vm_state *vms;
01839    char text_file[PATH_MAX];
01840    FILE *text_file_ptr;
01841    int res = 0;
01842    struct ast_vm_user *vmu;
01843 
01844    if (!(vmu = find_user(NULL, context, mailbox))) {
01845       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01846       return -1;
01847    }
01848    
01849    if (msgnum < 0) {
01850       if (imapgreetings) {
01851          res = imap_retrieve_greeting(dir, msgnum, vmu);
01852          goto exit;
01853       } else {
01854          res = 0;
01855          goto exit;
01856       }
01857    }
01858 
01859    /* Before anything can happen, we need a vm_state so that we can
01860     * actually access the imap server through the vms->mailstream
01861     */
01862    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01863       /* This should not happen. If it does, then I guess we'd
01864        * need to create the vm_state, extract which mailbox to
01865        * open, and then set up the msgArray so that the correct
01866        * IMAP message could be accessed. If I have seen correctly
01867        * though, the vms should be obtainable from the vmstates list
01868        * and should have its msgArray properly set up.
01869        */
01870       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01871       res = -1;
01872       goto exit;
01873    }
01874    
01875    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01876    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01877 
01878    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01879    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01880       res = 0;
01881       goto exit;
01882    }
01883 
01884    if (option_debug > 2)
01885       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01886    if (vms->msgArray[msgnum] == 0) {
01887       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01888       res = -1;
01889       goto exit;
01890    }
01891 
01892    /* This will only work for new messages... */
01893    ast_mutex_lock(&vms->lock);
01894    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01895    ast_mutex_unlock(&vms->lock);
01896    /* empty string means no valid header */
01897    if (ast_strlen_zero(header_content)) {
01898       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01899       res = -1;
01900       goto exit;
01901    }
01902 
01903    ast_mutex_lock(&vms->lock);
01904    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01905    ast_mutex_unlock(&vms->lock);
01906 
01907    /* We have the body, now we extract the file name of the first attachment. */
01908    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01909       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01910    } else {
01911       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01912       res = -1;
01913       goto exit;
01914    }
01915    
01916    /* Find the format of the attached file */
01917 
01918    strsep(&attachedfilefmt, ".");
01919    if (!attachedfilefmt) {
01920       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01921       res = -1;
01922       goto exit;
01923    }
01924    
01925    save_body(body, vms, "2", attachedfilefmt, 0);
01926    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01927       *vms->introfn = '\0';
01928    }
01929 
01930    /* Get info from headers!! */
01931    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01932 
01933    if (!(text_file_ptr = fopen(text_file, "w"))) {
01934       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01935    }
01936 
01937    fprintf(text_file_ptr, "%s\n", "[message]");
01938 
01939    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01940    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01941    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01942    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01943    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01944    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01945    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01946    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01947    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01948    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01949    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01950    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01951    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01952    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01953    fclose(text_file_ptr);
01954 
01955 exit:
01956    free_user(vmu);
01957    return res;
01958 }
01959 
01960 static int folder_int(const char *folder)
01961 {
01962    /*assume a NULL folder means INBOX*/
01963    if (!folder) {
01964       return 0;
01965    }
01966    if (!strcasecmp(folder, imapfolder)) {
01967       return 0;
01968    } else if (!strcasecmp(folder, "Old")) {
01969       return 1;
01970    } else if (!strcasecmp(folder, "Work")) {
01971       return 2;
01972    } else if (!strcasecmp(folder, "Family")) {
01973       return 3;
01974    } else if (!strcasecmp(folder, "Friends")) {
01975       return 4;
01976    } else if (!strcasecmp(folder, "Cust1")) {
01977       return 5;
01978    } else if (!strcasecmp(folder, "Cust2")) {
01979       return 6;
01980    } else if (!strcasecmp(folder, "Cust3")) {
01981       return 7;
01982    } else if (!strcasecmp(folder, "Cust4")) {
01983       return 8;
01984    } else if (!strcasecmp(folder, "Cust5")) {
01985       return 9;
01986    } else if (!strcasecmp(folder, "Urgent")) {
01987       return 11;
01988    } else { /*assume they meant INBOX if folder is not found otherwise*/
01989       return 0;
01990    }
01991 }
01992 
01993 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01994 {
01995    SEARCHPGM *pgm;
01996    SEARCHHEADER *hdr;
01997 
01998    struct ast_vm_user *vmu, vmus;
01999    struct vm_state *vms_p;
02000    int ret = 0;
02001    int fold = folder_int(folder);
02002    int urgent = 0;
02003    
02004    /* If URGENT, then look at INBOX */
02005    if (fold == 11) {
02006       fold = NEW_FOLDER;
02007       urgent = 1;
02008    }
02009 
02010    if (ast_strlen_zero(mailbox))
02011       return 0;
02012 
02013    /* We have to get the user before we can open the stream! */
02014    vmu = find_user(&vmus, context, mailbox);
02015    if (!vmu) {
02016       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02017       return -1;
02018    } else {
02019       /* No IMAP account available */
02020       if (vmu->imapuser[0] == '\0') {
02021          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02022          return -1;
02023       }
02024    }
02025    
02026    /* No IMAP account available */
02027    if (vmu->imapuser[0] == '\0') {
02028       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02029       free_user(vmu);
02030       return -1;
02031    }
02032 
02033    /* check if someone is accessing this box right now... */
02034    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02035    if (!vms_p) {
02036       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02037    }
02038    if (vms_p) {
02039       ast_debug(3, "Returning before search - user is logged in\n");
02040       if (fold == 0) { /* INBOX */
02041          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02042       }
02043       if (fold == 1) { /* Old messages */
02044          return vms_p->oldmessages;
02045       }
02046    }
02047 
02048    /* add one if not there... */
02049    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02050    if (!vms_p) {
02051       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02052    }
02053 
02054    if (!vms_p) {
02055       vms_p = create_vm_state_from_user(vmu);
02056    }
02057    ret = init_mailstream(vms_p, fold);
02058    if (!vms_p->mailstream) {
02059       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02060       return -1;
02061    }
02062    if (ret == 0) {
02063       ast_mutex_lock(&vms_p->lock);
02064       pgm = mail_newsearchpgm ();
02065       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02066       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02067       pgm->header = hdr;
02068       if (fold != OLD_FOLDER) {
02069          pgm->unseen = 1;
02070          pgm->seen = 0;
02071       }
02072       /* In the special case where fold is 1 (old messages) we have to do things a bit
02073        * differently. Old messages are stored in the INBOX but are marked as "seen"
02074        */
02075       else {
02076          pgm->unseen = 0;
02077          pgm->seen = 1;
02078       }
02079       /* look for urgent messages */
02080       if (fold == NEW_FOLDER) {
02081          if (urgent) {
02082             pgm->flagged = 1;
02083             pgm->unflagged = 0;
02084          } else {
02085             pgm->flagged = 0;
02086             pgm->unflagged = 1;
02087          }
02088       }
02089       pgm->undeleted = 1;
02090       pgm->deleted = 0;
02091 
02092       vms_p->vmArrayIndex = 0;
02093       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02094       if (fold == 0 && urgent == 0)
02095          vms_p->newmessages = vms_p->vmArrayIndex;
02096       if (fold == 1)
02097          vms_p->oldmessages = vms_p->vmArrayIndex;
02098       if (fold == 0 && urgent == 1)
02099          vms_p->urgentmessages = vms_p->vmArrayIndex;
02100       /*Freeing the searchpgm also frees the searchhdr*/
02101       mail_free_searchpgm(&pgm);
02102       ast_mutex_unlock(&vms_p->lock);
02103       vms_p->updated = 0;
02104       return vms_p->vmArrayIndex;
02105    } else {
02106       ast_mutex_lock(&vms_p->lock);
02107       mail_ping(vms_p->mailstream);
02108       ast_mutex_unlock(&vms_p->lock);
02109    }
02110    return 0;
02111 }
02112 
02113 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02114 {
02115    /* Check if mailbox is full */
02116    check_quota(vms, vmu->imapfolder);
02117    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02118       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02119       ast_play_and_wait(chan, "vm-mailboxfull");
02120       return -1;
02121    }
02122    
02123    /* Check if we have exceeded maxmsg */
02124    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));
02125    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02126       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02127       ast_play_and_wait(chan, "vm-mailboxfull");
02128       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02129       return -1;
02130    }
02131 
02132    return 0;
02133 }
02134 
02135 /*!
02136  * \brief Gets the number of messages that exist in a mailbox folder.
02137  * \param context
02138  * \param mailbox
02139  * \param folder
02140  * 
02141  * This method is used when IMAP backend is used.
02142  * \return The number of messages in this mailbox folder (zero or more).
02143  */
02144 static int messagecount(const char *context, const char *mailbox, const char *folder)
02145 {
02146    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02147       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02148    } else {
02149       return __messagecount(context, mailbox, folder);
02150    }
02151 }
02152 
02153 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)
02154 {
02155    char *myserveremail = serveremail;
02156    char fn[PATH_MAX];
02157    char introfn[PATH_MAX];
02158    char mailbox[256];
02159    char *stringp;
02160    FILE *p = NULL;
02161    char tmp[80] = "/tmp/astmail-XXXXXX";
02162    long len;
02163    void *buf;
02164    int tempcopy = 0;
02165    STRING str;
02166    int ret; /* for better error checking */
02167    char *imap_flags = NIL;
02168    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02169 
02170     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02171     if (msgnum < 0 && !imapgreetings) {
02172         return 0;
02173     }
02174    
02175    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02176       return -1;
02177    }
02178 
02179    /* Set urgent flag for IMAP message */
02180    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02181       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02182       imap_flags = "\\FLAGGED";
02183    }
02184    
02185    /* Attach only the first format */
02186    fmt = ast_strdupa(fmt);
02187    stringp = fmt;
02188    strsep(&stringp, "|");
02189 
02190    if (!ast_strlen_zero(vmu->serveremail))
02191       myserveremail = vmu->serveremail;
02192 
02193    if (msgnum > -1)
02194       make_file(fn, sizeof(fn), dir, msgnum);
02195    else
02196       ast_copy_string (fn, dir, sizeof(fn));
02197 
02198    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02199    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02200       *introfn = '\0';
02201    }
02202    
02203    if (ast_strlen_zero(vmu->email)) {
02204       /* We need the vmu->email to be set when we call make_email_file, but
02205        * if we keep it set, a duplicate e-mail will be created. So at the end
02206        * of this function, we will revert back to an empty string if tempcopy
02207        * is 1.
02208        */
02209       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02210       tempcopy = 1;
02211    }
02212 
02213    if (!strcmp(fmt, "wav49"))
02214       fmt = "WAV";
02215    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02216 
02217    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02218       command hangs. */
02219    if (!(p = vm_mkftemp(tmp))) {
02220       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02221       if (tempcopy)
02222          *(vmu->email) = '\0';
02223       return -1;
02224    }
02225 
02226    if (msgnum < 0 && imapgreetings) {
02227       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02228          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02229          return -1;
02230       }
02231       imap_delete_old_greeting(fn, vms);
02232    }
02233 
02234    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02235       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02236       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02237       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02238    /* read mail file to memory */
02239    len = ftell(p);
02240    rewind(p);
02241    if (!(buf = ast_malloc(len + 1))) {
02242       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02243       fclose(p);
02244       if (tempcopy)
02245          *(vmu->email) = '\0';
02246       return -1;
02247    }
02248    if (fread(buf, len, 1, p) < len) {
02249       if (ferror(p)) {
02250          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02251          return -1;
02252       }
02253    }
02254    ((char *) buf)[len] = '\0';
02255    INIT(&str, mail_string, buf, len);
02256    ret = init_mailstream(vms, NEW_FOLDER);
02257    if (ret == 0) {
02258       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02259       ast_mutex_lock(&vms->lock);
02260       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02261          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02262       ast_mutex_unlock(&vms->lock);
02263       fclose(p);
02264       unlink(tmp);
02265       ast_free(buf);
02266    } else {
02267       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02268       fclose(p);
02269       unlink(tmp);
02270       ast_free(buf);
02271       return -1;
02272    }
02273    ast_debug(3, "%s stored\n", fn);
02274    
02275    if (tempcopy)
02276       *(vmu->email) = '\0';
02277    inprocess_count(vmu->mailbox, vmu->context, -1);
02278    return 0;
02279 
02280 }
02281 
02282 /*!
02283  * \brief Gets the number of messages that exist in the inbox folder.
02284  * \param mailbox_context
02285  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02286  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02287  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02288  * 
02289  * This method is used when IMAP backend is used.
02290  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02291  *
02292  * \return zero on success, -1 on error.
02293  */
02294 
02295 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02296 {
02297    char tmp[PATH_MAX] = "";
02298    char *mailboxnc;
02299    char *context;
02300    char *mb;
02301    char *cur;
02302    if (newmsgs)
02303       *newmsgs = 0;
02304    if (oldmsgs)
02305       *oldmsgs = 0;
02306    if (urgentmsgs)
02307       *urgentmsgs = 0;
02308 
02309    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02310    /* If no mailbox, return immediately */
02311    if (ast_strlen_zero(mailbox_context))
02312       return 0;
02313    
02314    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02315    context = strchr(tmp, '@');
02316    if (strchr(mailbox_context, ',')) {
02317       int tmpnew, tmpold, tmpurgent;
02318       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02319       mb = tmp;
02320       while ((cur = strsep(&mb, ", "))) {
02321          if (!ast_strlen_zero(cur)) {
02322             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02323                return -1;
02324             else {
02325                if (newmsgs)
02326                   *newmsgs += tmpnew; 
02327                if (oldmsgs)
02328                   *oldmsgs += tmpold;
02329                if (urgentmsgs)
02330                   *urgentmsgs += tmpurgent;
02331             }
02332          }
02333       }
02334       return 0;
02335    }
02336    if (context) {
02337       *context = '\0';
02338       mailboxnc = tmp;
02339       context++;
02340    } else {
02341       context = "default";
02342       mailboxnc = (char *) mailbox_context;
02343    }
02344 
02345    if (newmsgs) {
02346       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02347       if (!vmu) {
02348          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02349          return -1;
02350       }
02351       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02352          return -1;
02353       }
02354    }
02355    if (oldmsgs) {
02356       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02357          return -1;
02358       }
02359    }
02360    if (urgentmsgs) {
02361       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02362          return -1;
02363       }
02364    }
02365    return 0;
02366 }
02367 
02368 /** 
02369  * \brief Determines if the given folder has messages.
02370  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02371  * \param folder the folder to look in
02372  *
02373  * This function is used when the mailbox is stored in an IMAP back end.
02374  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02375  * \return 1 if the folder has one or more messages. zero otherwise.
02376  */
02377 
02378 static int has_voicemail(const char *mailbox, const char *folder)
02379 {
02380    char tmp[256], *tmp2, *box, *context;
02381    ast_copy_string(tmp, mailbox, sizeof(tmp));
02382    tmp2 = tmp;
02383    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02384       while ((box = strsep(&tmp2, ",&"))) {
02385          if (!ast_strlen_zero(box)) {
02386             if (has_voicemail(box, folder)) {
02387                return 1;
02388             }
02389          }
02390       }
02391    }
02392    if ((context = strchr(tmp, '@'))) {
02393       *context++ = '\0';
02394    } else {
02395       context = "default";
02396    }
02397    return __messagecount(context, tmp, folder) ? 1 : 0;
02398 }
02399 
02400 /*!
02401  * \brief Copies a message from one mailbox to another.
02402  * \param chan
02403  * \param vmu
02404  * \param imbox
02405  * \param msgnum
02406  * \param duration
02407  * \param recip
02408  * \param fmt
02409  * \param dir
02410  *
02411  * This works with IMAP storage based mailboxes.
02412  *
02413  * \return zero on success, -1 on error.
02414  */
02415 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)
02416 {
02417    struct vm_state *sendvms = NULL, *destvms = NULL;
02418    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02419    if (msgnum >= recip->maxmsg) {
02420       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02421       return -1;
02422    }
02423    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02424       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02425       return -1;
02426    }
02427    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02428       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02429       return -1;
02430    }
02431    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02432    ast_mutex_lock(&sendvms->lock);
02433    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02434       ast_mutex_unlock(&sendvms->lock);
02435       return 0;
02436    }
02437    ast_mutex_unlock(&sendvms->lock);
02438    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02439    return -1;
02440 }
02441 
02442 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02443 {
02444    char tmp[256], *t = tmp;
02445    size_t left = sizeof(tmp);
02446    
02447    if (box == OLD_FOLDER) {
02448       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02449    } else {
02450       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02451    }
02452 
02453    if (box == NEW_FOLDER) {
02454       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02455    } else {
02456       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02457    }
02458 
02459    /* Build up server information */
02460    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02461 
02462    /* Add authentication user if present */
02463    if (!ast_strlen_zero(authuser))
02464       ast_build_string(&t, &left, "/authuser=%s", authuser);
02465 
02466    /* Add flags if present */
02467    if (!ast_strlen_zero(imapflags))
02468       ast_build_string(&t, &left, "/%s", imapflags);
02469 
02470    /* End with username */
02471 #if 1
02472    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02473 #else
02474    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02475 #endif
02476    if (box == NEW_FOLDER || box == OLD_FOLDER)
02477       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02478    else if (box == GREETINGS_FOLDER)
02479       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02480    else {   /* Other folders such as Friends, Family, etc... */
02481       if (!ast_strlen_zero(imapparentfolder)) {
02482          /* imapparentfolder would typically be set to INBOX */
02483          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02484       } else {
02485          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02486       }
02487    }
02488 }
02489 
02490 static int init_mailstream(struct vm_state *vms, int box)
02491 {
02492    MAILSTREAM *stream = NIL;
02493    long debug;
02494    char tmp[256];
02495    
02496    if (!vms) {
02497       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02498       return -1;
02499    }
02500    if (option_debug > 2)
02501       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02502    if (vms->mailstream == NIL || !vms->mailstream) {
02503       if (option_debug)
02504          ast_log(LOG_DEBUG, "mailstream not set.\n");
02505    } else {
02506       stream = vms->mailstream;
02507    }
02508    /* debug = T;  user wants protocol telemetry? */
02509    debug = NIL;  /* NO protocol telemetry? */
02510 
02511    if (delimiter == '\0') {      /* did not probe the server yet */
02512       char *cp;
02513 #ifdef USE_SYSTEM_IMAP
02514 #include <imap/linkage.c>
02515 #elif defined(USE_SYSTEM_CCLIENT)
02516 #include <c-client/linkage.c>
02517 #else
02518 #include "linkage.c"
02519 #endif
02520       /* Connect to INBOX first to get folders delimiter */
02521       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02522       ast_mutex_lock(&vms->lock);
02523       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02524       ast_mutex_unlock(&vms->lock);
02525       if (stream == NIL) {
02526          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02527          return -1;
02528       }
02529       get_mailbox_delimiter(stream);
02530       /* update delimiter in imapfolder */
02531       for (cp = vms->imapfolder; *cp; cp++)
02532          if (*cp == '/')
02533             *cp = delimiter;
02534    }
02535    /* Now connect to the target folder */
02536    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02537    if (option_debug > 2)
02538       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02539    ast_mutex_lock(&vms->lock);
02540    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02541    ast_mutex_unlock(&vms->lock);
02542    if (vms->mailstream == NIL) {
02543       return -1;
02544    } else {
02545       return 0;
02546    }
02547 }
02548 
02549 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02550 {
02551    SEARCHPGM *pgm;
02552    SEARCHHEADER *hdr;
02553    int ret, urgent = 0;
02554 
02555    /* If Urgent, then look at INBOX */
02556    if (box == 11) {
02557       box = NEW_FOLDER;
02558       urgent = 1;
02559    }
02560 
02561    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02562    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02563    vms->imapversion = vmu->imapversion;
02564    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02565 
02566    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02567       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02568       return -1;
02569    }
02570    
02571    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02572    
02573    /* Check Quota */
02574    if  (box == 0)  {
02575       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02576       check_quota(vms, (char *) mbox(vmu, box));
02577    }
02578 
02579    ast_mutex_lock(&vms->lock);
02580    pgm = mail_newsearchpgm();
02581 
02582    /* Check IMAP folder for Asterisk messages only... */
02583    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02584    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02585    pgm->header = hdr;
02586    pgm->deleted = 0;
02587    pgm->undeleted = 1;
02588 
02589    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02590    if (box == NEW_FOLDER && urgent == 1) {
02591       pgm->unseen = 1;
02592       pgm->seen = 0;
02593       pgm->flagged = 1;
02594       pgm->unflagged = 0;
02595    } else if (box == NEW_FOLDER && urgent == 0) {
02596       pgm->unseen = 1;
02597       pgm->seen = 0;
02598       pgm->flagged = 0;
02599       pgm->unflagged = 1;
02600    } else if (box == OLD_FOLDER) {
02601       pgm->seen = 1;
02602       pgm->unseen = 0;
02603    }
02604 
02605    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02606 
02607    vms->vmArrayIndex = 0;
02608    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02609    vms->lastmsg = vms->vmArrayIndex - 1;
02610    mail_free_searchpgm(&pgm);
02611    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02612     * ensure to allocate enough space to account for all of them. Warn if old messages
02613     * have not been checked first as that is required.
02614     */
02615    if (box == 0 && !vms->dh_arraysize) {
02616       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02617    }
02618    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02619       ast_mutex_unlock(&vms->lock);
02620       return -1;
02621    }
02622 
02623    ast_mutex_unlock(&vms->lock);
02624    return 0;
02625 }
02626 
02627 static void write_file(char *filename, char *buffer, unsigned long len)
02628 {
02629    FILE *output;
02630 
02631    output = fopen (filename, "w");
02632    if (fwrite(buffer, len, 1, output) != 1) {
02633       if (ferror(output)) {
02634          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02635       }
02636    }
02637    fclose (output);
02638 }
02639 
02640 static void update_messages_by_imapuser(const char *user, unsigned long number)
02641 {
02642    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02643 
02644    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02645       return;
02646    }
02647 
02648    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02649    vms->msgArray[vms->vmArrayIndex++] = number;
02650 }
02651 
02652 void mm_searched(MAILSTREAM *stream, unsigned long number)
02653 {
02654    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02655 
02656    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02657       return;
02658 
02659    update_messages_by_imapuser(user, number);
02660 }
02661 
02662 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02663 {
02664    struct ast_variable *var;
02665    struct ast_vm_user *vmu;
02666 
02667    vmu = ast_calloc(1, sizeof *vmu);
02668    if (!vmu)
02669       return NULL;
02670    ast_set_flag(vmu, VM_ALLOCED);
02671    populate_defaults(vmu);
02672 
02673    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02674    if (var) {
02675       apply_options_full(vmu, var);
02676       ast_variables_destroy(var);
02677       return vmu;
02678    } else {
02679       ast_free(vmu);
02680       return NULL;
02681    }
02682 }
02683 
02684 /* Interfaces to C-client */
02685 
02686 void mm_exists(MAILSTREAM * stream, unsigned long number)
02687 {
02688    /* mail_ping will callback here if new mail! */
02689    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02690    if (number == 0) return;
02691    set_update(stream);
02692 }
02693 
02694 
02695 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02696 {
02697    /* mail_ping will callback here if expunged mail! */
02698    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02699    if (number == 0) return;
02700    set_update(stream);
02701 }
02702 
02703 
02704 void mm_flags(MAILSTREAM * stream, unsigned long number)
02705 {
02706    /* mail_ping will callback here if read mail! */
02707    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02708    if (number == 0) return;
02709    set_update(stream);
02710 }
02711 
02712 
02713 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02714 {
02715    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02716    mm_log (string, errflg);
02717 }
02718 
02719 
02720 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02721 {
02722    if (delimiter == '\0') {
02723       delimiter = delim;
02724    }
02725 
02726    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02727    if (attributes & LATT_NOINFERIORS)
02728       ast_debug(5, "no inferiors\n");
02729    if (attributes & LATT_NOSELECT)
02730       ast_debug(5, "no select\n");
02731    if (attributes & LATT_MARKED)
02732       ast_debug(5, "marked\n");
02733    if (attributes & LATT_UNMARKED)
02734       ast_debug(5, "unmarked\n");
02735 }
02736 
02737 
02738 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02739 {
02740    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02741    if (attributes & LATT_NOINFERIORS)
02742       ast_debug(5, "no inferiors\n");
02743    if (attributes & LATT_NOSELECT)
02744       ast_debug(5, "no select\n");
02745    if (attributes & LATT_MARKED)
02746       ast_debug(5, "marked\n");
02747    if (attributes & LATT_UNMARKED)
02748       ast_debug(5, "unmarked\n");
02749 }
02750 
02751 
02752 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02753 {
02754    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02755    if (status->flags & SA_MESSAGES)
02756       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02757    if (status->flags & SA_RECENT)
02758       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02759    if (status->flags & SA_UNSEEN)
02760       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02761    if (status->flags & SA_UIDVALIDITY)
02762       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02763    if (status->flags & SA_UIDNEXT)
02764       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02765    ast_log(AST_LOG_NOTICE, "\n");
02766 }
02767 
02768 
02769 void mm_log(char *string, long errflg)
02770 {
02771    switch ((short) errflg) {
02772       case NIL:
02773          ast_debug(1, "IMAP Info: %s\n", string);
02774          break;
02775       case PARSE:
02776       case WARN:
02777          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02778          break;
02779       case ERROR:
02780          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02781          break;
02782    }
02783 }
02784 
02785 
02786 void mm_dlog(char *string)
02787 {
02788    ast_log(AST_LOG_NOTICE, "%s\n", string);
02789 }
02790 
02791 
02792 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02793 {
02794    struct ast_vm_user *vmu;
02795 
02796    ast_debug(4, "Entering callback mm_login\n");
02797 
02798    ast_copy_string(user, mb->user, MAILTMPLEN);
02799 
02800    /* We should only do this when necessary */
02801    if (!ast_strlen_zero(authpassword)) {
02802       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02803    } else {
02804       AST_LIST_TRAVERSE(&users, vmu, list) {
02805          if (!strcasecmp(mb->user, vmu->imapuser)) {
02806             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02807             break;
02808          }
02809       }
02810       if (!vmu) {
02811          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02812             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02813             free_user(vmu);
02814          }
02815       }
02816    }
02817 }
02818 
02819 
02820 void mm_critical(MAILSTREAM * stream)
02821 {
02822 }
02823 
02824 
02825 void mm_nocritical(MAILSTREAM * stream)
02826 {
02827 }
02828 
02829 
02830 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02831 {
02832    kill (getpid (), SIGSTOP);
02833    return NIL;
02834 }
02835 
02836 
02837 void mm_fatal(char *string)
02838 {
02839    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02840 }
02841 
02842 /* C-client callback to handle quota */
02843 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02844 {
02845    struct vm_state *vms;
02846    char *mailbox = stream->mailbox, *user;
02847    char buf[1024] = "";
02848    unsigned long usage = 0, limit = 0;
02849    
02850    while (pquota) {
02851       usage = pquota->usage;
02852       limit = pquota->limit;
02853       pquota = pquota->next;
02854    }
02855    
02856    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)))) {
02857       ast_log(AST_LOG_ERROR, "No state found.\n");
02858       return;
02859    }
02860 
02861    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02862 
02863    vms->quota_usage = usage;
02864    vms->quota_limit = limit;
02865 }
02866 
02867 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02868 {
02869    char *start, *eol_pnt;
02870    int taglen;
02871 
02872    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02873       return NULL;
02874 
02875    taglen = strlen(tag) + 1;
02876    if (taglen < 1)
02877       return NULL;
02878 
02879    if (!(start = strstr(header, tag)))
02880       return NULL;
02881 
02882    /* Since we can be called multiple times we should clear our buffer */
02883    memset(buf, 0, len);
02884 
02885    ast_copy_string(buf, start+taglen, len);
02886    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02887       *eol_pnt = '\0';
02888    return buf;
02889 }
02890 
02891 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02892 {
02893    char *start, *quote, *eol_pnt;
02894 
02895    if (ast_strlen_zero(mailbox))
02896       return NULL;
02897 
02898    if (!(start = strstr(mailbox, "/user=")))
02899       return NULL;
02900 
02901    ast_copy_string(buf, start+6, len);
02902 
02903    if (!(quote = strchr(buf, '\"'))) {
02904       if (!(eol_pnt = strchr(buf, '/')))
02905          eol_pnt = strchr(buf,'}');
02906       *eol_pnt = '\0';
02907       return buf;
02908    } else {
02909       eol_pnt = strchr(buf+1,'\"');
02910       *eol_pnt = '\0';
02911       return buf+1;
02912    }
02913 }
02914 
02915 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02916 {
02917    struct vm_state *vms_p;
02918 
02919    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02920    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02921       return vms_p;
02922    }
02923    if (option_debug > 4)
02924       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02925    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02926       return NULL;
02927    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02928    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02929    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02930    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02931    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02932    vms_p->imapversion = vmu->imapversion;
02933    if (option_debug > 4)
02934       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02935    vms_p->updated = 1;
02936    /* set mailbox to INBOX! */
02937    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02938    init_vm_state(vms_p);
02939    vmstate_insert(vms_p);
02940    return vms_p;
02941 }
02942 
02943 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02944 {
02945    struct vmstate *vlist = NULL;
02946 
02947    if (interactive) {
02948       struct vm_state *vms;
02949       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02950       vms = pthread_getspecific(ts_vmstate.key);
02951       return vms;
02952    }
02953 
02954    AST_LIST_LOCK(&vmstates);
02955    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02956       if (!vlist->vms) {
02957          ast_debug(3, "error: vms is NULL for %s\n", user);
02958          continue;
02959       }
02960       if (vlist->vms->imapversion != imapversion) {
02961          continue;
02962       }
02963       if (!vlist->vms->imapuser) {
02964          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02965          continue;
02966       }
02967 
02968       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02969          AST_LIST_UNLOCK(&vmstates);
02970          return vlist->vms;
02971       }
02972    }
02973    AST_LIST_UNLOCK(&vmstates);
02974 
02975    ast_debug(3, "%s not found in vmstates\n", user);
02976 
02977    return NULL;
02978 }
02979 
02980 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02981 {
02982 
02983    struct vmstate *vlist = NULL;
02984    const char *local_context = S_OR(context, "default");
02985 
02986    if (interactive) {
02987       struct vm_state *vms;
02988       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02989       vms = pthread_getspecific(ts_vmstate.key);
02990       return vms;
02991    }
02992 
02993    AST_LIST_LOCK(&vmstates);
02994    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02995       if (!vlist->vms) {
02996          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02997          continue;
02998       }
02999       if (vlist->vms->imapversion != imapversion) {
03000          continue;
03001       }
03002       if (!vlist->vms->username || !vlist->vms->context) {
03003          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03004          continue;
03005       }
03006 
03007       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);
03008       
03009       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03010          ast_debug(3, "Found it!\n");
03011          AST_LIST_UNLOCK(&vmstates);
03012          return vlist->vms;
03013       }
03014    }
03015    AST_LIST_UNLOCK(&vmstates);
03016 
03017    ast_debug(3, "%s not found in vmstates\n", mailbox);
03018 
03019    return NULL;
03020 }
03021 
03022 static void vmstate_insert(struct vm_state *vms) 
03023 {
03024    struct vmstate *v;
03025    struct vm_state *altvms;
03026 
03027    /* If interactive, it probably already exists, and we should
03028       use the one we already have since it is more up to date.
03029       We can compare the username to find the duplicate */
03030    if (vms->interactive == 1) {
03031       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03032       if (altvms) {  
03033          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03034          vms->newmessages = altvms->newmessages;
03035          vms->oldmessages = altvms->oldmessages;
03036          vms->vmArrayIndex = altvms->vmArrayIndex;
03037          vms->lastmsg = altvms->lastmsg;
03038          vms->curmsg = altvms->curmsg;
03039          /* get a pointer to the persistent store */
03040          vms->persist_vms = altvms;
03041          /* Reuse the mailstream? */
03042 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03043          vms->mailstream = altvms->mailstream;
03044 #else
03045          vms->mailstream = NIL;
03046 #endif
03047       }
03048       return;
03049    }
03050 
03051    if (!(v = ast_calloc(1, sizeof(*v))))
03052       return;
03053    
03054    v->vms = vms;
03055 
03056    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03057 
03058    AST_LIST_LOCK(&vmstates);
03059    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03060    AST_LIST_UNLOCK(&vmstates);
03061 }
03062 
03063 static void vmstate_delete(struct vm_state *vms) 
03064 {
03065    struct vmstate *vc = NULL;
03066    struct vm_state *altvms = NULL;
03067 
03068    /* If interactive, we should copy pertinent info
03069       back to the persistent state (to make update immediate) */
03070    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03071       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03072       altvms->newmessages = vms->newmessages;
03073       altvms->oldmessages = vms->oldmessages;
03074       altvms->updated = 1;
03075       vms->mailstream = mail_close(vms->mailstream);
03076 
03077       /* Interactive states are not stored within the persistent list */
03078       return;
03079    }
03080    
03081    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03082    
03083    AST_LIST_LOCK(&vmstates);
03084    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03085       if (vc->vms == vms) {
03086          AST_LIST_REMOVE_CURRENT(list);
03087          break;
03088       }
03089    }
03090    AST_LIST_TRAVERSE_SAFE_END
03091    AST_LIST_UNLOCK(&vmstates);
03092    
03093    if (vc) {
03094       ast_mutex_destroy(&vc->vms->lock);
03095       ast_free(vc);
03096    }
03097    else
03098       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03099 }
03100 
03101 static void set_update(MAILSTREAM * stream) 
03102 {
03103    struct vm_state *vms;
03104    char *mailbox = stream->mailbox, *user;
03105    char buf[1024] = "";
03106 
03107    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03108       if (user && option_debug > 2)
03109          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03110       return;
03111    }
03112 
03113    ast_debug(3, "User %s mailbox set for update.\n", user);
03114 
03115    vms->updated = 1; /* Set updated flag since mailbox changed */
03116 }
03117 
03118 static void init_vm_state(struct vm_state *vms) 
03119 {
03120    int x;
03121    vms->vmArrayIndex = 0;
03122    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03123       vms->msgArray[x] = 0;
03124    }
03125    ast_mutex_init(&vms->lock);
03126 }
03127 
03128 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03129 {
03130    char *body_content;
03131    char *body_decoded;
03132    char *fn = is_intro ? vms->introfn : vms->fn;
03133    unsigned long len;
03134    unsigned long newlen;
03135    char filename[256];
03136    
03137    if (!body || body == NIL)
03138       return -1;
03139 
03140    ast_mutex_lock(&vms->lock);
03141    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03142    ast_mutex_unlock(&vms->lock);
03143    if (body_content != NIL) {
03144       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03145       /* ast_debug(1,body_content); */
03146       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03147       /* If the body of the file is empty, return an error */
03148       if (!newlen) {
03149          return -1;
03150       }
03151       write_file(filename, (char *) body_decoded, newlen);
03152    } else {
03153       ast_debug(5, "Body of message is NULL.\n");
03154       return -1;
03155    }
03156    return 0;
03157 }
03158 
03159 /*! 
03160  * \brief Get delimiter via mm_list callback 
03161  * \param stream
03162  *
03163  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03164  */
03165 /* MUTEX should already be held */
03166 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03167    char tmp[50];
03168    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03169    mail_list(stream, tmp, "*");
03170 }
03171 
03172 /*! 
03173  * \brief Check Quota for user 
03174  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03175  * \param mailbox the mailbox to check the quota for.
03176  *
03177  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03178  */
03179 static void check_quota(struct vm_state *vms, char *mailbox) {
03180    ast_mutex_lock(&vms->lock);
03181    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03182    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03183    if (vms && vms->mailstream != NULL) {
03184       imap_getquotaroot(vms->mailstream, mailbox);
03185    } else {
03186       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03187    }
03188    ast_mutex_unlock(&vms->lock);
03189 }
03190 
03191 #endif /* IMAP_STORAGE */
03192 
03193 /*! \brief Lock file path
03194     only return failure if ast_lock_path returns 'timeout',
03195    not if the path does not exist or any other reason
03196 */
03197 static int vm_lock_path(const char *path)
03198 {
03199    switch (ast_lock_path(path)) {
03200    case AST_LOCK_TIMEOUT:
03201       return -1;
03202    default:
03203       return 0;
03204    }
03205 }
03206 
03207 
03208 #ifdef ODBC_STORAGE
03209 struct generic_prepare_struct {
03210    char *sql;
03211    int argc;
03212    char **argv;
03213 };
03214 
03215 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03216 {
03217    struct generic_prepare_struct *gps = data;
03218    int res, i;
03219    SQLHSTMT stmt;
03220 
03221    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03222    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03223       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03224       return NULL;
03225    }
03226    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03227    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03228       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03229       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03230       return NULL;
03231    }
03232    for (i = 0; i < gps->argc; i++)
03233       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03234 
03235    return stmt;
03236 }
03237 
03238 /*!
03239  * \brief Retrieves a file from an ODBC data store.
03240  * \param dir the path to the file to be retreived.
03241  * \param msgnum the message number, such as within a mailbox folder.
03242  * 
03243  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03244  * 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.
03245  *
03246  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03247  * The output is the message information file with the name msgnum and the extension .txt
03248  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03249  * 
03250  * \return 0 on success, -1 on error.
03251  */
03252 static int retrieve_file(char *dir, int msgnum)
03253 {
03254    int x = 0;
03255    int res;
03256    int fd = -1;
03257    size_t fdlen = 0;
03258    void *fdm = MAP_FAILED;
03259    SQLSMALLINT colcount = 0;
03260    SQLHSTMT stmt;
03261    char sql[PATH_MAX];
03262    char fmt[80]="";
03263    char *c;
03264    char coltitle[256];
03265    SQLSMALLINT collen;
03266    SQLSMALLINT datatype;
03267    SQLSMALLINT decimaldigits;
03268    SQLSMALLINT nullable;
03269    SQLULEN colsize;
03270    SQLLEN colsize2;
03271    FILE *f = NULL;
03272    char rowdata[80];
03273    char fn[PATH_MAX];
03274    char full_fn[PATH_MAX];
03275    char msgnums[80];
03276    char *argv[] = { dir, msgnums };
03277    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03278 
03279    struct odbc_obj *obj;
03280    obj = ast_odbc_request_obj(odbc_database, 0);
03281    if (obj) {
03282       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03283       c = strchr(fmt, '|');
03284       if (c)
03285          *c = '\0';
03286       if (!strcasecmp(fmt, "wav49"))
03287          strcpy(fmt, "WAV");
03288       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03289       if (msgnum > -1)
03290          make_file(fn, sizeof(fn), dir, msgnum);
03291       else
03292          ast_copy_string(fn, dir, sizeof(fn));
03293 
03294       /* Create the information file */
03295       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03296       
03297       if (!(f = fopen(full_fn, "w+"))) {
03298          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03299          goto yuck;
03300       }
03301       
03302       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03303       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03304       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03305       if (!stmt) {
03306          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03307          ast_odbc_release_obj(obj);
03308          goto yuck;
03309       }
03310       res = SQLFetch(stmt);
03311       if (res == SQL_NO_DATA) {
03312          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03313          ast_odbc_release_obj(obj);
03314          goto yuck;
03315       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03316          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03317          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03318          ast_odbc_release_obj(obj);
03319          goto yuck;
03320       }
03321       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03322       if (fd < 0) {
03323          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03324          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03325          ast_odbc_release_obj(obj);
03326          goto yuck;
03327       }
03328       res = SQLNumResultCols(stmt, &colcount);
03329       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03330          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03331          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03332          ast_odbc_release_obj(obj);
03333          goto yuck;
03334       }
03335       if (f) 
03336          fprintf(f, "[message]\n");
03337       for (x = 0; x < colcount; x++) {
03338          rowdata[0] = '\0';
03339          collen = sizeof(coltitle);
03340          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03341                   &datatype, &colsize, &decimaldigits, &nullable);
03342          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03343             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03344             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03345             ast_odbc_release_obj(obj);
03346             goto yuck;
03347          }
03348          if (!strcasecmp(coltitle, "recording")) {
03349             off_t offset;
03350             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03351             fdlen = colsize2;
03352             if (fd > -1) {
03353                char tmp[1]="";
03354                lseek(fd, fdlen - 1, SEEK_SET);
03355                if (write(fd, tmp, 1) != 1) {
03356                   close(fd);
03357                   fd = -1;
03358                   continue;
03359                }
03360                /* Read out in small chunks */
03361                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03362                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03363                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03364                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03365                      ast_odbc_release_obj(obj);
03366                      goto yuck;
03367                   } else {
03368                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03369                      munmap(fdm, CHUNKSIZE);
03370                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03371                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03372                         unlink(full_fn);
03373                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03374                         ast_odbc_release_obj(obj);
03375                         goto yuck;
03376                      }
03377                   }
03378                }
03379                if (truncate(full_fn, fdlen) < 0) {
03380                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03381                }
03382             }
03383          } else {
03384             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03385             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03386                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03387                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03388                ast_odbc_release_obj(obj);
03389                goto yuck;
03390             }
03391             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03392                fprintf(f, "%s=%s\n", coltitle, rowdata);
03393          }
03394       }
03395       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03396       ast_odbc_release_obj(obj);
03397    } else
03398       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03399 yuck: 
03400    if (f)
03401       fclose(f);
03402    if (fd > -1)
03403       close(fd);
03404    return x - 1;
03405 }
03406 
03407 /*!
03408  * \brief Determines the highest message number in use for a given user and mailbox folder.
03409  * \param vmu 
03410  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03411  *
03412  * This method is used when mailboxes are stored in an ODBC back end.
03413  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03414  *
03415  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03416  */
03417 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03418 {
03419    int x = 0;
03420    int res;
03421    SQLHSTMT stmt;
03422    char sql[PATH_MAX];
03423    char rowdata[20];
03424    char *argv[] = { dir };
03425    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03426 
03427    struct odbc_obj *obj;
03428    obj = ast_odbc_request_obj(odbc_database, 0);
03429    if (obj) {
03430       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03431       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03432       if (!stmt) {
03433          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03434          ast_odbc_release_obj(obj);
03435          goto yuck;
03436       }
03437       res = SQLFetch(stmt);
03438       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03439          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03440          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03441          ast_odbc_release_obj(obj);
03442          goto yuck;
03443       }
03444       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03445       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03446          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03447          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03448          ast_odbc_release_obj(obj);
03449          goto yuck;
03450       }
03451       if (sscanf(rowdata, "%30d", &x) != 1)
03452          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03453       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03454       ast_odbc_release_obj(obj);
03455    } else
03456       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03457 yuck: 
03458    return x - 1;
03459 }
03460 
03461 /*!
03462  * \brief Determines if the specified message exists.
03463  * \param dir the folder the mailbox folder to look for messages. 
03464  * \param msgnum the message index to query for.
03465  *
03466  * This method is used when mailboxes are stored in an ODBC back end.
03467  *
03468  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03469  */
03470 static int message_exists(char *dir, int msgnum)
03471 {
03472    int x = 0;
03473    int res;
03474    SQLHSTMT stmt;
03475    char sql[PATH_MAX];
03476    char rowdata[20];
03477    char msgnums[20];
03478    char *argv[] = { dir, msgnums };
03479    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03480 
03481    struct odbc_obj *obj;
03482    obj = ast_odbc_request_obj(odbc_database, 0);
03483    if (obj) {
03484       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03485       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03486       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03487       if (!stmt) {
03488          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03489          ast_odbc_release_obj(obj);
03490          goto yuck;
03491       }
03492       res = SQLFetch(stmt);
03493       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03494          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03495          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03496          ast_odbc_release_obj(obj);
03497          goto yuck;
03498       }
03499       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03500       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03501          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03502          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03503          ast_odbc_release_obj(obj);
03504          goto yuck;
03505       }
03506       if (sscanf(rowdata, "%30d", &x) != 1)
03507          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03508       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03509       ast_odbc_release_obj(obj);
03510    } else
03511       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03512 yuck: 
03513    return x;
03514 }
03515 
03516 /*!
03517  * \brief returns the one-based count for messages.
03518  * \param vmu
03519  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03520  *
03521  * This method is used when mailboxes are stored in an ODBC back end.
03522  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03523  * one-based messages.
03524  * This method just calls last_message_index and returns +1 of its value.
03525  *
03526  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03527  */
03528 static int count_messages(struct ast_vm_user *vmu, char *dir)
03529 {
03530    return last_message_index(vmu, dir) + 1;
03531 }
03532 
03533 /*!
03534  * \brief Deletes a message from the mailbox folder.
03535  * \param sdir The mailbox folder to work in.
03536  * \param smsg The message index to be deleted.
03537  *
03538  * This method is used when mailboxes are stored in an ODBC back end.
03539  * The specified message is directly deleted from the database 'voicemessages' table.
03540  * 
03541  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03542  */
03543 static void delete_file(const char *sdir, int smsg)
03544 {
03545    SQLHSTMT stmt;
03546    char sql[PATH_MAX];
03547    char msgnums[20];
03548    char *argv[] = { NULL, msgnums };
03549    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03550    struct odbc_obj *obj;
03551 
03552    argv[0] = ast_strdupa(sdir);
03553 
03554    obj = ast_odbc_request_obj(odbc_database, 0);
03555    if (obj) {
03556       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03557       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03558       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03559       if (!stmt)
03560          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03561       else
03562          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03563       ast_odbc_release_obj(obj);
03564    } else
03565       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03566    return;  
03567 }
03568 
03569 /*!
03570  * \brief Copies a voicemail from one mailbox to another.
03571  * \param sdir the folder for which to look for the message to be copied.
03572  * \param smsg the index of the message to be copied.
03573  * \param ddir the destination folder to copy the message into.
03574  * \param dmsg the index to be used for the copied message.
03575  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03576  * \param dmailboxcontext The context for the destination user.
03577  *
03578  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03579  */
03580 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03581 {
03582    SQLHSTMT stmt;
03583    char sql[512];
03584    char msgnums[20];
03585    char msgnumd[20];
03586    struct odbc_obj *obj;
03587    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03588    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03589 
03590    delete_file(ddir, dmsg);
03591    obj = ast_odbc_request_obj(odbc_database, 0);
03592    if (obj) {
03593       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03594       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03595       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);
03596       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03597       if (!stmt)
03598          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03599       else
03600          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03601       ast_odbc_release_obj(obj);
03602    } else
03603       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03604    return;  
03605 }
03606 
03607 struct insert_data {
03608    char *sql;
03609    const char *dir;
03610    const char *msgnums;
03611    void *data;
03612    SQLLEN datalen;
03613    SQLLEN indlen;
03614    const char *context;
03615    const char *macrocontext;
03616    const char *callerid;
03617    const char *origtime;
03618    const char *duration;
03619    const char *mailboxuser;
03620    const char *mailboxcontext;
03621    const char *category;
03622    const char *flag;
03623 };
03624 
03625 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03626 {
03627    struct insert_data *data = vdata;
03628    int res;
03629    SQLHSTMT stmt;
03630 
03631    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03632    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03633       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03634       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03635       return NULL;
03636    }
03637 
03638    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03639    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03640    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03641    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03642    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03643    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03644    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03645    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03646    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03647    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03648    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03649    if (!ast_strlen_zero(data->category)) {
03650       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03651    }
03652    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03653    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03654       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03655       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03656       return NULL;
03657    }
03658 
03659    return stmt;
03660 }
03661 
03662 /*!
03663  * \brief Stores a voicemail into the database.
03664  * \param dir the folder the mailbox folder to store the message.
03665  * \param mailboxuser the user owning the mailbox folder.
03666  * \param mailboxcontext
03667  * \param msgnum the message index for the message to be stored.
03668  *
03669  * This method is used when mailboxes are stored in an ODBC back end.
03670  * The message sound file and information file is looked up on the file system. 
03671  * A SQL query is invoked to store the message into the (MySQL) database.
03672  *
03673  * \return the zero on success -1 on error.
03674  */
03675 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03676 {
03677    int res = 0;
03678    int fd = -1;
03679    void *fdm = MAP_FAILED;
03680    size_t fdlen = -1;
03681    SQLHSTMT stmt;
03682    char sql[PATH_MAX];
03683    char msgnums[20];
03684    char fn[PATH_MAX];
03685    char full_fn[PATH_MAX];
03686    char fmt[80]="";
03687    char *c;
03688    struct ast_config *cfg = NULL;
03689    struct odbc_obj *obj;
03690    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03691       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03692    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03693 
03694    delete_file(dir, msgnum);
03695    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03696       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03697       return -1;
03698    }
03699 
03700    do {
03701       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03702       c = strchr(fmt, '|');
03703       if (c)
03704          *c = '\0';
03705       if (!strcasecmp(fmt, "wav49"))
03706          strcpy(fmt, "WAV");
03707       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03708       if (msgnum > -1)
03709          make_file(fn, sizeof(fn), dir, msgnum);
03710       else
03711          ast_copy_string(fn, dir, sizeof(fn));
03712       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03713       cfg = ast_config_load(full_fn, config_flags);
03714       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03715       fd = open(full_fn, O_RDWR);
03716       if (fd < 0) {
03717          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03718          res = -1;
03719          break;
03720       }
03721       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03722          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03723             idata.context = "";
03724          }
03725          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03726             idata.macrocontext = "";
03727          }
03728          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03729             idata.callerid = "";
03730          }
03731          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03732             idata.origtime = "";
03733          }
03734          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03735             idata.duration = "";
03736          }
03737          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03738             idata.category = "";
03739          }
03740          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03741             idata.flag = "";
03742          }
03743       }
03744       fdlen = lseek(fd, 0, SEEK_END);
03745       lseek(fd, 0, SEEK_SET);
03746       printf("Length is %zd\n", fdlen);
03747       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03748       if (fdm == MAP_FAILED) {
03749          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03750          res = -1;
03751          break;
03752       } 
03753       idata.data = fdm;
03754       idata.datalen = idata.indlen = fdlen;
03755 
03756       if (!ast_strlen_zero(idata.category)) 
03757          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03758       else
03759          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03760 
03761       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03762          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03763       } else {
03764          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03765          res = -1;
03766       }
03767    } while (0);
03768    if (obj) {
03769       ast_odbc_release_obj(obj);
03770    }
03771    if (cfg)
03772       ast_config_destroy(cfg);
03773    if (fdm != MAP_FAILED)
03774       munmap(fdm, fdlen);
03775    if (fd > -1)
03776       close(fd);
03777    return res;
03778 }
03779 
03780 /*!
03781  * \brief Renames a message in a mailbox folder.
03782  * \param sdir The folder of the message to be renamed.
03783  * \param smsg The index of the message to be renamed.
03784  * \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.
03785  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03786  * \param ddir The destination folder for the message to be renamed into
03787  * \param dmsg The destination message for the message to be renamed.
03788  *
03789  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03790  * 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.
03791  * 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.
03792  */
03793 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03794 {
03795    SQLHSTMT stmt;
03796    char sql[PATH_MAX];
03797    char msgnums[20];
03798    char msgnumd[20];
03799    struct odbc_obj *obj;
03800    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03801    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03802 
03803    delete_file(ddir, dmsg);
03804    obj = ast_odbc_request_obj(odbc_database, 0);
03805    if (obj) {
03806       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03807       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03808       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03809       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03810       if (!stmt)
03811          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03812       else
03813          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03814       ast_odbc_release_obj(obj);
03815    } else
03816       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03817    return;  
03818 }
03819 
03820 /*!
03821  * \brief Removes a voicemail message file.
03822  * \param dir the path to the message file.
03823  * \param msgnum the unique number for the message within the mailbox.
03824  *
03825  * Removes the message content file and the information file.
03826  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03827  * Typical use is to clean up after a RETRIEVE operation. 
03828  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03829  * \return zero on success, -1 on error.
03830  */
03831 static int remove_file(char *dir, int msgnum)
03832 {
03833    char fn[PATH_MAX];
03834    char full_fn[PATH_MAX];
03835    char msgnums[80];
03836    
03837    if (msgnum > -1) {
03838       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03839       make_file(fn, sizeof(fn), dir, msgnum);
03840    } else
03841       ast_copy_string(fn, dir, sizeof(fn));
03842    ast_filedelete(fn, NULL);  
03843    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03844    unlink(full_fn);
03845    return 0;
03846 }
03847 #else
03848 #ifndef IMAP_STORAGE
03849 /*!
03850  * \brief Find all .txt files - even if they are not in sequence from 0000.
03851  * \param vmu
03852  * \param dir
03853  *
03854  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03855  *
03856  * \return the count of messages, zero or more.
03857  */
03858 static int count_messages(struct ast_vm_user *vmu, char *dir)
03859 {
03860 
03861    int vmcount = 0;
03862    DIR *vmdir = NULL;
03863    struct dirent *vment = NULL;
03864 
03865    if (vm_lock_path(dir))
03866       return ERROR_LOCK_PATH;
03867 
03868    if ((vmdir = opendir(dir))) {
03869       while ((vment = readdir(vmdir))) {
03870          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03871             vmcount++;
03872          }
03873       }
03874       closedir(vmdir);
03875    }
03876    ast_unlock_path(dir);
03877    
03878    return vmcount;
03879 }
03880 
03881 /*!
03882  * \brief Renames a message in a mailbox folder.
03883  * \param sfn The path to the mailbox information and data file to be renamed.
03884  * \param dfn The path for where the message data and information files will be renamed to.
03885  *
03886  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03887  */
03888 static void rename_file(char *sfn, char *dfn)
03889 {
03890    char stxt[PATH_MAX];
03891    char dtxt[PATH_MAX];
03892    ast_filerename(sfn, dfn, NULL);
03893    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03894    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03895    if (ast_check_realtime("voicemail_data")) {
03896       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03897    }
03898    rename(stxt, dtxt);
03899 }
03900 
03901 /*! 
03902  * \brief Determines the highest message number in use for a given user and mailbox folder.
03903  * \param vmu 
03904  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03905  *
03906  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03907  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03908  *
03909  * \note Should always be called with a lock already set on dir.
03910  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03911  */
03912 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03913 {
03914    int x;
03915    unsigned char map[MAXMSGLIMIT] = "";
03916    DIR *msgdir;
03917    struct dirent *msgdirent;
03918    int msgdirint;
03919 
03920    /* Reading the entire directory into a file map scales better than
03921     * doing a stat repeatedly on a predicted sequence.  I suspect this
03922     * is partially due to stat(2) internally doing a readdir(2) itself to
03923     * find each file. */
03924    if (!(msgdir = opendir(dir))) {
03925       return -1;
03926    }
03927 
03928    while ((msgdirent = readdir(msgdir))) {
03929       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03930          map[msgdirint] = 1;
03931    }
03932    closedir(msgdir);
03933 
03934    for (x = 0; x < vmu->maxmsg; x++) {
03935       if (map[x] == 0)
03936          break;
03937    }
03938 
03939    return x - 1;
03940 }
03941 
03942 #endif /* #ifndef IMAP_STORAGE */
03943 #endif /* #else of #ifdef ODBC_STORAGE */
03944 #ifndef IMAP_STORAGE
03945 /*!
03946  * \brief Utility function to copy a file.
03947  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03948  * \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.
03949  *
03950  * 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.
03951  * The copy operation copies up to 4096 bytes at once.
03952  *
03953  * \return zero on success, -1 on error.
03954  */
03955 static int copy(char *infile, char *outfile)
03956 {
03957    int ifd;
03958    int ofd;
03959    int res;
03960    int len;
03961    char buf[4096];
03962 
03963 #ifdef HARDLINK_WHEN_POSSIBLE
03964    /* Hard link if possible; saves disk space & is faster */
03965    if (link(infile, outfile)) {
03966 #endif
03967       if ((ifd = open(infile, O_RDONLY)) < 0) {
03968          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03969          return -1;
03970       }
03971       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03972          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03973          close(ifd);
03974          return -1;
03975       }
03976       do {
03977          len = read(ifd, buf, sizeof(buf));
03978          if (len < 0) {
03979             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03980             close(ifd);
03981             close(ofd);
03982             unlink(outfile);
03983          }
03984          if (len) {
03985             res = write(ofd, buf, len);
03986             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03987                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03988                close(ifd);
03989                close(ofd);
03990                unlink(outfile);
03991             }
03992          }
03993       } while (len);
03994       close(ifd);
03995       close(ofd);
03996       return 0;
03997 #ifdef HARDLINK_WHEN_POSSIBLE
03998    } else {
03999       /* Hard link succeeded */
04000       return 0;
04001    }
04002 #endif
04003 }
04004 
04005 /*!
04006  * \brief Copies a voicemail information (envelope) file.
04007  * \param frompath
04008  * \param topath 
04009  *
04010  * Every voicemail has the data (.wav) file, and the information file.
04011  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04012  * This is used by the COPY macro when not using IMAP storage.
04013  */
04014 static void copy_plain_file(char *frompath, char *topath)
04015 {
04016    char frompath2[PATH_MAX], topath2[PATH_MAX];
04017    struct ast_variable *tmp,*var = NULL;
04018    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04019    ast_filecopy(frompath, topath, NULL);
04020    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04021    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04022    if (ast_check_realtime("voicemail_data")) {
04023       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04024       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04025       for (tmp = var; tmp; tmp = tmp->next) {
04026          if (!strcasecmp(tmp->name, "origmailbox")) {
04027             origmailbox = tmp->value;
04028          } else if (!strcasecmp(tmp->name, "context")) {
04029             context = tmp->value;
04030          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04031             macrocontext = tmp->value;
04032          } else if (!strcasecmp(tmp->name, "exten")) {
04033             exten = tmp->value;
04034          } else if (!strcasecmp(tmp->name, "priority")) {
04035             priority = tmp->value;
04036          } else if (!strcasecmp(tmp->name, "callerchan")) {
04037             callerchan = tmp->value;
04038          } else if (!strcasecmp(tmp->name, "callerid")) {
04039             callerid = tmp->value;
04040          } else if (!strcasecmp(tmp->name, "origdate")) {
04041             origdate = tmp->value;
04042          } else if (!strcasecmp(tmp->name, "origtime")) {
04043             origtime = tmp->value;
04044          } else if (!strcasecmp(tmp->name, "category")) {
04045             category = tmp->value;
04046          } else if (!strcasecmp(tmp->name, "duration")) {
04047             duration = tmp->value;
04048          }
04049       }
04050       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);
04051    }
04052    copy(frompath2, topath2);
04053    ast_variables_destroy(var);
04054 }
04055 #endif
04056 
04057 /*! 
04058  * \brief Removes the voicemail sound and information file.
04059  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04060  *
04061  * This is used by the DELETE macro when voicemails are stored on the file system.
04062  *
04063  * \return zero on success, -1 on error.
04064  */
04065 static int vm_delete(char *file)
04066 {
04067    char *txt;
04068    int txtsize = 0;
04069 
04070    txtsize = (strlen(file) + 5)*sizeof(char);
04071    txt = alloca(txtsize);
04072    /* Sprintf here would safe because we alloca'd exactly the right length,
04073     * but trying to eliminate all sprintf's anyhow
04074     */
04075    if (ast_check_realtime("voicemail_data")) {
04076       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04077    }
04078    snprintf(txt, txtsize, "%s.txt", file);
04079    unlink(txt);
04080    return ast_filedelete(file, NULL);
04081 }
04082 
04083 /*!
04084  * \brief utility used by inchar(), for base_encode()
04085  */
04086 static int inbuf(struct baseio *bio, FILE *fi)
04087 {
04088    int l;
04089 
04090    if (bio->ateof)
04091       return 0;
04092 
04093    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04094       if (ferror(fi))
04095          return -1;
04096 
04097       bio->ateof = 1;
04098       return 0;
04099    }
04100 
04101    bio->iolen = l;
04102    bio->iocp = 0;
04103 
04104    return 1;
04105 }
04106 
04107 /*!
04108  * \brief utility used by base_encode()
04109  */
04110 static int inchar(struct baseio *bio, FILE *fi)
04111 {
04112    if (bio->iocp>=bio->iolen) {
04113       if (!inbuf(bio, fi))
04114          return EOF;
04115    }
04116 
04117    return bio->iobuf[bio->iocp++];
04118 }
04119 
04120 /*!
04121  * \brief utility used by base_encode()
04122  */
04123 static int ochar(struct baseio *bio, int c, FILE *so)
04124 {
04125    if (bio->linelength >= BASELINELEN) {
04126       if (fputs(ENDL, so) == EOF) {
04127          return -1;
04128       }
04129 
04130       bio->linelength = 0;
04131    }
04132 
04133    if (putc(((unsigned char) c), so) == EOF) {
04134       return -1;
04135    }
04136 
04137    bio->linelength++;
04138 
04139    return 1;
04140 }
04141 
04142 /*!
04143  * \brief Performs a base 64 encode algorithm on the contents of a File
04144  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04145  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04146  *
04147  * 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 ?
04148  *
04149  * \return zero on success, -1 on error.
04150  */
04151 static int base_encode(char *filename, FILE *so)
04152 {
04153    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04154       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04155       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04156       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04157    int i, hiteof = 0;
04158    FILE *fi;
04159    struct baseio bio;
04160 
04161    memset(&bio, 0, sizeof(bio));
04162    bio.iocp = BASEMAXINLINE;
04163 
04164    if (!(fi = fopen(filename, "rb"))) {
04165       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04166       return -1;
04167    }
04168 
04169    while (!hiteof){
04170       unsigned char igroup[3], ogroup[4];
04171       int c, n;
04172 
04173       memset(igroup, 0, sizeof(igroup));
04174 
04175       for (n = 0; n < 3; n++) {
04176          if ((c = inchar(&bio, fi)) == EOF) {
04177             hiteof = 1;
04178             break;
04179          }
04180 
04181          igroup[n] = (unsigned char) c;
04182       }
04183 
04184       if (n > 0) {
04185          ogroup[0]= dtable[igroup[0] >> 2];
04186          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04187          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04188          ogroup[3]= dtable[igroup[2] & 0x3F];
04189 
04190          if (n < 3) {
04191             ogroup[3] = '=';
04192 
04193             if (n < 2)
04194                ogroup[2] = '=';
04195          }
04196 
04197          for (i = 0; i < 4; i++)
04198             ochar(&bio, ogroup[i], so);
04199       }
04200    }
04201 
04202    fclose(fi);
04203    
04204    if (fputs(ENDL, so) == EOF) {
04205       return 0;
04206    }
04207 
04208    return 1;
04209 }
04210 
04211 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)
04212 {
04213    char callerid[256];
04214    char num[12];
04215    char fromdir[256], fromfile[256];
04216    struct ast_config *msg_cfg;
04217    const char *origcallerid, *origtime;
04218    char origcidname[80], origcidnum[80], origdate[80];
04219    int inttime;
04220    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04221 
04222    /* Prepare variables for substitution in email body and subject */
04223    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04224    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04225    snprintf(num, sizeof(num), "%d", msgnum);
04226    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04227    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04228    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04229    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04230       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04231    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04232    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04233    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04234    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04235    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04236 
04237    /* Retrieve info from VM attribute file */
04238    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04239    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04240    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04241       strcat(fromfile, ".txt");
04242    }
04243    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04244       if (option_debug > 0) {
04245          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04246       }
04247       return;
04248    }
04249 
04250    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04251       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04252       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04253       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04254       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04255    }
04256 
04257    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04258       struct timeval tv = { inttime, };
04259       struct ast_tm tm;
04260       ast_localtime(&tv, &tm, NULL);
04261       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04262       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04263    }
04264    ast_config_destroy(msg_cfg);
04265 }
04266 
04267 /*!
04268  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04269  * \param from The string to work with.
04270  * \param buf The buffer into which to write the modified quoted string.
04271  * \param maxlen Always zero, but see \see ast_str
04272  * 
04273  * \return The destination string with quotes wrapped on it (the to field).
04274  */
04275 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04276 {
04277    const char *ptr;
04278 
04279    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04280    ast_str_set(buf, maxlen, "\"");
04281    for (ptr = from; *ptr; ptr++) {
04282       if (*ptr == '"' || *ptr == '\\') {
04283          ast_str_append(buf, maxlen, "\\%c", *ptr);
04284       } else {
04285          ast_str_append(buf, maxlen, "%c", *ptr);
04286       }
04287    }
04288    ast_str_append(buf, maxlen, "\"");
04289 
04290    return ast_str_buffer(*buf);
04291 }
04292 
04293 /*! \brief
04294  * fill in *tm for current time according to the proper timezone, if any.
04295  * \return tm so it can be used as a function argument.
04296  */
04297 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04298 {
04299    const struct vm_zone *z = NULL;
04300    struct timeval t = ast_tvnow();
04301 
04302    /* Does this user have a timezone specified? */
04303    if (!ast_strlen_zero(vmu->zonetag)) {
04304       /* Find the zone in the list */
04305       AST_LIST_LOCK(&zones);
04306       AST_LIST_TRAVERSE(&zones, z, list) {
04307          if (!strcmp(z->name, vmu->zonetag))
04308             break;
04309       }
04310       AST_LIST_UNLOCK(&zones);
04311    }
04312    ast_localtime(&t, tm, z ? z->timezone : NULL);
04313    return tm;
04314 }
04315 
04316 /*!\brief Check if the string would need encoding within the MIME standard, to
04317  * avoid confusing certain mail software that expects messages to be 7-bit
04318  * clean.
04319  */
04320 static int check_mime(const char *str)
04321 {
04322    for (; *str; str++) {
04323       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04324          return 1;
04325       }
04326    }
04327    return 0;
04328 }
04329 
04330 /*!\brief Encode a string according to the MIME rules for encoding strings
04331  * that are not 7-bit clean or contain control characters.
04332  *
04333  * Additionally, if the encoded string would exceed the MIME limit of 76
04334  * characters per line, then the encoding will be broken up into multiple
04335  * sections, separated by a space character, in order to facilitate
04336  * breaking up the associated header across multiple lines.
04337  *
04338  * \param end An expandable buffer for holding the result
04339  * \param maxlen Always zero, but see \see ast_str
04340  * \param start A string to be encoded
04341  * \param preamble The length of the first line already used for this string,
04342  * to ensure that each line maintains a maximum length of 76 chars.
04343  * \param postamble the length of any additional characters appended to the
04344  * line, used to ensure proper field wrapping.
04345  * \retval The encoded string.
04346  */
04347 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04348 {
04349    struct ast_str *tmp = ast_str_alloca(80);
04350    int first_section = 1;
04351 
04352    ast_str_reset(*end);
04353    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04354    for (; *start; start++) {
04355       int need_encoding = 0;
04356       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04357          need_encoding = 1;
04358       }
04359       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04360          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04361          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04362          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04363          /* Start new line */
04364          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04365          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04366          first_section = 0;
04367       }
04368       if (need_encoding && *start == ' ') {
04369          ast_str_append(&tmp, -1, "_");
04370       } else if (need_encoding) {
04371          ast_str_append(&tmp, -1, "=%hhX", *start);
04372       } else {
04373          ast_str_append(&tmp, -1, "%c", *start);
04374       }
04375    }
04376    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04377    return ast_str_buffer(*end);
04378 }
04379 
04380 /*!
04381  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04382  * \param p The output file to generate the email contents into.
04383  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04384  * \param vmu The voicemail user who is sending the voicemail.
04385  * \param msgnum The message index in the mailbox folder.
04386  * \param context 
04387  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04388  * \param fromfolder
04389  * \param cidnum The caller ID number.
04390  * \param cidname The caller ID name.
04391  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04392  * \param attach2 
04393  * \param format The message sound file format. i.e. .wav
04394  * \param duration The time of the message content, in seconds.
04395  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04396  * \param chan
04397  * \param category
04398  * \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.
04399  * \param flag
04400  *
04401  * 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.
04402  */
04403 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)
04404 {
04405    char date[256];
04406    char host[MAXHOSTNAMELEN] = "";
04407    char who[256];
04408    char bound[256];
04409    char dur[256];
04410    struct ast_tm tm;
04411    char enc_cidnum[256] = "", enc_cidname[256] = "";
04412    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04413    char *greeting_attachment; 
04414    char filename[256];
04415 
04416    if (!str1 || !str2) {
04417       ast_free(str1);
04418       ast_free(str2);
04419       return;
04420    }
04421 
04422    if (cidnum) {
04423       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04424    }
04425    if (cidname) {
04426       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04427    }
04428    gethostname(host, sizeof(host) - 1);
04429 
04430    if (strchr(srcemail, '@')) {
04431       ast_copy_string(who, srcemail, sizeof(who));
04432    } else {
04433       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04434    }
04435 
04436    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04437    if (greeting_attachment) {
04438       *greeting_attachment++ = '\0';
04439    }
04440 
04441    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04442    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04443    fprintf(p, "Date: %s" ENDL, date);
04444 
04445    /* Set date format for voicemail mail */
04446    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04447 
04448    if (!ast_strlen_zero(fromstring)) {
04449       struct ast_channel *ast;
04450       if ((ast = ast_dummy_channel_alloc())) {
04451          char *ptr;
04452          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04453          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04454 
04455          if (check_mime(ast_str_buffer(str1))) {
04456             int first_line = 1;
04457             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04458             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04459                *ptr = '\0';
04460                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04461                first_line = 0;
04462                /* Substring is smaller, so this will never grow */
04463                ast_str_set(&str2, 0, "%s", ptr + 1);
04464             }
04465             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04466          } else {
04467             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04468          }
04469          ast = ast_channel_release(ast);
04470       } else {
04471          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04472       }
04473    } else {
04474       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04475    }
04476 
04477    if (check_mime(vmu->fullname)) {
04478       int first_line = 1;
04479       char *ptr;
04480       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04481       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04482          *ptr = '\0';
04483          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04484          first_line = 0;
04485          /* Substring is smaller, so this will never grow */
04486          ast_str_set(&str2, 0, "%s", ptr + 1);
04487       }
04488       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04489    } else {
04490       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04491    }
04492 
04493    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04494       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04495       struct ast_channel *ast;
04496       if ((ast = ast_dummy_channel_alloc())) {
04497          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04498          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04499          if (check_mime(ast_str_buffer(str1))) {
04500             int first_line = 1;
04501             char *ptr;
04502             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04503             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04504                *ptr = '\0';
04505                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04506                first_line = 0;
04507                /* Substring is smaller, so this will never grow */
04508                ast_str_set(&str2, 0, "%s", ptr + 1);
04509             }
04510             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04511          } else {
04512             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04513          }
04514          ast = ast_channel_release(ast);
04515       } else {
04516          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04517       }
04518    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04519       if (ast_strlen_zero(flag)) {
04520          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04521       } else {
04522          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04523       }
04524    } else {
04525       if (ast_strlen_zero(flag)) {
04526          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04527       } else {
04528          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04529       }
04530    }
04531 
04532    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04533       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04534    if (imap) {
04535       /* additional information needed for IMAP searching */
04536       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04537       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04538       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04539       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04540 #ifdef IMAP_STORAGE
04541       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04542 #else
04543       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04544 #endif
04545       /* flag added for Urgent */
04546       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04547       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04548       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04549       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04550       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04551       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04552       if (!ast_strlen_zero(category)) {
04553          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04554       } else {
04555          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04556       }
04557       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04558       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04559       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04560    }
04561    if (!ast_strlen_zero(cidnum)) {
04562       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04563    }
04564    if (!ast_strlen_zero(cidname)) {
04565       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04566    }
04567    fprintf(p, "MIME-Version: 1.0" ENDL);
04568    if (attach_user_voicemail) {
04569       /* Something unique. */
04570       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04571          (int) getpid(), (unsigned int) ast_random());
04572 
04573       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04574       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04575       fprintf(p, "--%s" ENDL, bound);
04576    }
04577    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04578    if (emailbody || vmu->emailbody) {
04579       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04580       struct ast_channel *ast;
04581       if ((ast = ast_dummy_channel_alloc())) {
04582          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04583          ast_str_substitute_variables(&str1, 0, ast, e_body);
04584 #ifdef IMAP_STORAGE
04585             {
04586                /* Convert body to native line terminators for IMAP backend */
04587                char *line = ast_str_buffer(str1), *next;
04588                do {
04589                   /* Terminate line before outputting it to the file */
04590                   if ((next = strchr(line, '\n'))) {
04591                      *next++ = '\0';
04592                   }
04593                   fprintf(p, "%s" ENDL, line);
04594                   line = next;
04595                } while (!ast_strlen_zero(line));
04596             }
04597 #else
04598          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04599 #endif
04600          ast = ast_channel_release(ast);
04601       } else {
04602          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04603       }
04604    } else if (msgnum > -1) {
04605       if (strcmp(vmu->mailbox, mailbox)) {
04606          /* Forwarded type */
04607          struct ast_config *msg_cfg;
04608          const char *v;
04609          int inttime;
04610          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04611          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04612          /* Retrieve info from VM attribute file */
04613          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04614          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04615          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04616             strcat(fromfile, ".txt");
04617          }
04618          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04619             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04620                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04621             }
04622 
04623             /* You might be tempted to do origdate, except that a) it's in the wrong
04624              * format, and b) it's missing for IMAP recordings. */
04625             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04626                struct timeval tv = { inttime, };
04627                struct ast_tm tm;
04628                ast_localtime(&tv, &tm, NULL);
04629                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04630             }
04631             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04632                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04633                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04634                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04635                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04636                date, origcallerid, origdate);
04637             ast_config_destroy(msg_cfg);
04638          } else {
04639             goto plain_message;
04640          }
04641       } else {
04642 plain_message:
04643          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04644             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04645             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04646             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04647             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04648       }
04649    } else {
04650       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04651             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04652    }
04653 
04654    if (imap || attach_user_voicemail) {
04655       if (!ast_strlen_zero(attach2)) {
04656          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04657          ast_debug(5, "creating second attachment filename %s\n", filename);
04658          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04659          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04660          ast_debug(5, "creating attachment filename %s\n", filename);
04661          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04662       } else {
04663          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04664          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04665          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04666       }
04667    }
04668    ast_free(str1);
04669    ast_free(str2);
04670 }
04671 
04672 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)
04673 {
04674    char tmpdir[256], newtmp[256];
04675    char fname[256];
04676    char tmpcmd[256];
04677    int tmpfd = -1;
04678 
04679    /* Eww. We want formats to tell us their own MIME type */
04680    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04681 
04682    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04683       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04684       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04685       tmpfd = mkstemp(newtmp);
04686       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04687       ast_debug(3, "newtmp: %s\n", newtmp);
04688       if (tmpfd > -1) {
04689          int soxstatus;
04690          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04691          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04692             attach = newtmp;
04693             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04694          } else {
04695             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04696                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04697             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04698          }
04699       }
04700    }
04701    fprintf(p, "--%s" ENDL, bound);
04702    if (msgnum > -1)
04703       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04704    else
04705       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04706    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04707    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04708    if (msgnum > -1)
04709       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04710    else
04711       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04712    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04713    base_encode(fname, p);
04714    if (last)
04715       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04716    if (tmpfd > -1) {
04717       unlink(fname);
04718       close(tmpfd);
04719       unlink(newtmp);
04720    }
04721    return 0;
04722 }
04723 
04724 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)
04725 {
04726    FILE *p = NULL;
04727    char tmp[80] = "/tmp/astmail-XXXXXX";
04728    char tmp2[256];
04729    char *stringp;
04730 
04731    if (vmu && ast_strlen_zero(vmu->email)) {
04732       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04733       return(0);
04734    }
04735 
04736    /* Mail only the first format */
04737    format = ast_strdupa(format);
04738    stringp = format;
04739    strsep(&stringp, "|");
04740 
04741    if (!strcmp(format, "wav49"))
04742       format = "WAV";
04743    ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
04744    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04745       command hangs */
04746    if ((p = vm_mkftemp(tmp)) == NULL) {
04747       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04748       return -1;
04749    } else {
04750       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04751       fclose(p);
04752       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04753       ast_safe_system(tmp2);
04754       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04755    }
04756    return 0;
04757 }
04758 
04759 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)
04760 {
04761    char enc_cidnum[256], enc_cidname[256];
04762    char date[256];
04763    char host[MAXHOSTNAMELEN] = "";
04764    char who[256];
04765    char dur[PATH_MAX];
04766    char tmp[80] = "/tmp/astmail-XXXXXX";
04767    char tmp2[PATH_MAX];
04768    struct ast_tm tm;
04769    FILE *p;
04770    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04771 
04772    if (!str1 || !str2) {
04773       ast_free(str1);
04774       ast_free(str2);
04775       return -1;
04776    }
04777 
04778    if (cidnum) {
04779       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04780    }
04781    if (cidname) {
04782       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04783    }
04784 
04785    if ((p = vm_mkftemp(tmp)) == NULL) {
04786       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04787       ast_free(str1);
04788       ast_free(str2);
04789       return -1;
04790    }
04791    gethostname(host, sizeof(host)-1);
04792    if (strchr(srcemail, '@')) {
04793       ast_copy_string(who, srcemail, sizeof(who));
04794    } else {
04795       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04796    }
04797    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04798    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04799    fprintf(p, "Date: %s\n", date);
04800 
04801    /* Reformat for custom pager format */
04802    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04803 
04804    if (!ast_strlen_zero(pagerfromstring)) {
04805       struct ast_channel *ast;
04806       if ((ast = ast_dummy_channel_alloc())) {
04807          char *ptr;
04808          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04809          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04810 
04811          if (check_mime(ast_str_buffer(str1))) {
04812             int first_line = 1;
04813             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04814             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04815                *ptr = '\0';
04816                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04817                first_line = 0;
04818                /* Substring is smaller, so this will never grow */
04819                ast_str_set(&str2, 0, "%s", ptr + 1);
04820             }
04821             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04822          } else {
04823             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04824          }
04825          ast = ast_channel_release(ast);
04826       } else {
04827          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04828       }
04829    } else {
04830       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04831    }
04832 
04833    if (check_mime(vmu->fullname)) {
04834       int first_line = 1;
04835       char *ptr;
04836       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04837       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04838          *ptr = '\0';
04839          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04840          first_line = 0;
04841          /* Substring is smaller, so this will never grow */
04842          ast_str_set(&str2, 0, "%s", ptr + 1);
04843       }
04844       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04845    } else {
04846       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04847    }
04848 
04849    if (!ast_strlen_zero(pagersubject)) {
04850       struct ast_channel *ast;
04851       if ((ast = ast_dummy_channel_alloc())) {
04852          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04853          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04854          if (check_mime(ast_str_buffer(str1))) {
04855             int first_line = 1;
04856             char *ptr;
04857             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04858             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04859                *ptr = '\0';
04860                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04861                first_line = 0;
04862                /* Substring is smaller, so this will never grow */
04863                ast_str_set(&str2, 0, "%s", ptr + 1);
04864             }
04865             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04866          } else {
04867             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04868          }
04869          ast = ast_channel_release(ast);
04870       } else {
04871          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04872       }
04873    } else {
04874       if (ast_strlen_zero(flag)) {
04875          fprintf(p, "Subject: New VM\n\n");
04876       } else {
04877          fprintf(p, "Subject: New %s VM\n\n", flag);
04878       }
04879    }
04880 
04881    if (pagerbody) {
04882       struct ast_channel *ast;
04883       if ((ast = ast_dummy_channel_alloc())) {
04884          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04885          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04886          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04887          ast = ast_channel_release(ast);
04888       } else {
04889          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04890       }
04891    } else {
04892       fprintf(p, "New %s long %s msg in box %s\n"
04893             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04894    }
04895 
04896    fclose(p);
04897    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04898    ast_safe_system(tmp2);
04899    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04900    ast_free(str1);
04901    ast_free(str2);
04902    return 0;
04903 }
04904 
04905 /*!
04906  * \brief Gets the current date and time, as formatted string.
04907  * \param s The buffer to hold the output formatted date.
04908  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04909  * 
04910  * The date format string used is "%a %b %e %r UTC %Y".
04911  * 
04912  * \return zero on success, -1 on error.
04913  */
04914 static int get_date(char *s, int len)
04915 {
04916    struct ast_tm tm;
04917    struct timeval t = ast_tvnow();
04918    
04919    ast_localtime(&t, &tm, "UTC");
04920 
04921    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04922 }
04923 
04924 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04925 {
04926    int res;
04927    char fn[PATH_MAX];
04928    char dest[PATH_MAX];
04929 
04930    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04931 
04932    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04933       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04934       return -1;
04935    }
04936 
04937    RETRIEVE(fn, -1, ext, context);
04938    if (ast_fileexists(fn, NULL, NULL) > 0) {
04939       res = ast_stream_and_wait(chan, fn, ecodes);
04940       if (res) {
04941          DISPOSE(fn, -1);
04942          return res;
04943       }
04944    } else {
04945       /* Dispose just in case */
04946       DISPOSE(fn, -1);
04947       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04948       if (res)
04949          return res;
04950       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04951       if (res)
04952          return res;
04953    }
04954    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04955    return res;
04956 }
04957 
04958 static void free_zone(struct vm_zone *z)
04959 {
04960    ast_free(z);
04961 }
04962 
04963 #ifdef ODBC_STORAGE
04964 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04965 {
04966    int x = -1;
04967    int res;
04968    SQLHSTMT stmt = NULL;
04969    char sql[PATH_MAX];
04970    char rowdata[20];
04971    char tmp[PATH_MAX] = "";
04972    struct odbc_obj *obj = NULL;
04973    char *context;
04974    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04975 
04976    if (newmsgs)
04977       *newmsgs = 0;
04978    if (oldmsgs)
04979       *oldmsgs = 0;
04980    if (urgentmsgs)
04981       *urgentmsgs = 0;
04982 
04983    /* If no mailbox, return immediately */
04984    if (ast_strlen_zero(mailbox))
04985       return 0;
04986 
04987    ast_copy_string(tmp, mailbox, sizeof(tmp));
04988 
04989    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04990       int u, n, o;
04991       char *next, *remaining = tmp;
04992       while ((next = strsep(&remaining, " ,"))) {
04993          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04994             return -1;
04995          }
04996          if (urgentmsgs) {
04997             *urgentmsgs += u;
04998          }
04999          if (newmsgs) {
05000             *newmsgs += n;
05001          }
05002          if (oldmsgs) {
05003             *oldmsgs += o;
05004          }
05005       }
05006       return 0;
05007    }
05008 
05009    context = strchr(tmp, '@');
05010    if (context) {
05011       *context = '\0';
05012       context++;
05013    } else
05014       context = "default";
05015 
05016    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05017       do {
05018          if (newmsgs) {
05019             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05020             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05021                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05022                break;
05023             }
05024             res = SQLFetch(stmt);
05025             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05026                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05027                break;
05028             }
05029             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05030             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05031                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05032                break;
05033             }
05034             *newmsgs = atoi(rowdata);
05035             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05036          }
05037 
05038          if (oldmsgs) {
05039             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05040             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05041                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05042                break;
05043             }
05044             res = SQLFetch(stmt);
05045             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05046                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05047                break;
05048             }
05049             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05050             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05051                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05052                break;
05053             }
05054             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05055             *oldmsgs = atoi(rowdata);
05056          }
05057 
05058          if (urgentmsgs) {
05059             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05060             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05061                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05062                break;
05063             }
05064             res = SQLFetch(stmt);
05065             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05066                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05067                break;
05068             }
05069             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05070             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05071                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05072                break;
05073             }
05074             *urgentmsgs = atoi(rowdata);
05075          }
05076 
05077          x = 0;
05078       } while (0);
05079    } else {
05080       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05081    }
05082 
05083    if (stmt) {
05084       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05085    }
05086    if (obj) {
05087       ast_odbc_release_obj(obj);
05088    }
05089 
05090    return x;
05091 }
05092 
05093 /*!
05094  * \brief Gets the number of messages that exist in a mailbox folder.
05095  * \param context
05096  * \param mailbox
05097  * \param folder
05098  * 
05099  * This method is used when ODBC backend is used.
05100  * \return The number of messages in this mailbox folder (zero or more).
05101  */
05102 static int messagecount(const char *context, const char *mailbox, const char *folder)
05103 {
05104    struct odbc_obj *obj = NULL;
05105    int nummsgs = 0;
05106    int res;
05107    SQLHSTMT stmt = NULL;
05108    char sql[PATH_MAX];
05109    char rowdata[20];
05110    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05111    if (!folder)
05112       folder = "INBOX";
05113    /* If no mailbox, return immediately */
05114    if (ast_strlen_zero(mailbox))
05115       return 0;
05116 
05117    obj = ast_odbc_request_obj(odbc_database, 0);
05118    if (obj) {
05119       if (!strcmp(folder, "INBOX")) {
05120          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);
05121       } else {
05122          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05123       }
05124       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05125       if (!stmt) {
05126          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05127          goto yuck;
05128       }
05129       res = SQLFetch(stmt);
05130       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05131          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05132          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05133          goto yuck;
05134       }
05135       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05136       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05137          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05138          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05139          goto yuck;
05140       }
05141       nummsgs = atoi(rowdata);
05142       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05143    } else
05144       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05145 
05146 yuck:
05147    if (obj)
05148       ast_odbc_release_obj(obj);
05149    return nummsgs;
05150 }
05151 
05152 /** 
05153  * \brief Determines if the given folder has messages.
05154  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05155  * 
05156  * This function is used when the mailbox is stored in an ODBC back end.
05157  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05158  * \return 1 if the folder has one or more messages. zero otherwise.
05159  */
05160 static int has_voicemail(const char *mailbox, const char *folder)
05161 {
05162    char tmp[256], *tmp2 = tmp, *box, *context;
05163    ast_copy_string(tmp, mailbox, sizeof(tmp));
05164    while ((context = box = strsep(&tmp2, ",&"))) {
05165       strsep(&context, "@");
05166       if (ast_strlen_zero(context))
05167          context = "default";
05168       if (messagecount(context, box, folder))
05169          return 1;
05170    }
05171    return 0;
05172 }
05173 #endif
05174 #ifndef IMAP_STORAGE
05175 /*! 
05176  * \brief Copies a message from one mailbox to another.
05177  * \param chan
05178  * \param vmu
05179  * \param imbox
05180  * \param msgnum
05181  * \param duration
05182  * \param recip
05183  * \param fmt
05184  * \param dir
05185  * \param flag
05186  *
05187  * This is only used by file storage based mailboxes.
05188  *
05189  * \return zero on success, -1 on error.
05190  */
05191 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)
05192 {
05193    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05194    const char *frombox = mbox(vmu, imbox);
05195    int recipmsgnum;
05196    int res = 0;
05197 
05198    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05199 
05200    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05201       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05202    } else {
05203       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05204    }
05205    
05206    if (!dir)
05207       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05208    else
05209       ast_copy_string(fromdir, dir, sizeof(fromdir));
05210 
05211    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05212    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05213 
05214    if (vm_lock_path(todir))
05215       return ERROR_LOCK_PATH;
05216 
05217    recipmsgnum = last_message_index(recip, todir) + 1;
05218    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05219       make_file(topath, sizeof(topath), todir, recipmsgnum);
05220 #ifndef ODBC_STORAGE
05221       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05222          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05223       } else {
05224 #endif
05225          /* If we are prepending a message for ODBC, then the message already
05226           * exists in the database, but we want to force copying from the
05227           * filesystem (since only the FS contains the prepend). */
05228          copy_plain_file(frompath, topath);
05229          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05230          vm_delete(topath);
05231 #ifndef ODBC_STORAGE
05232       }
05233 #endif
05234    } else {
05235       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05236       res = -1;
05237    }
05238    ast_unlock_path(todir);
05239    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05240       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05241       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05242       flag);
05243    
05244    return res;
05245 }
05246 #endif
05247 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05248 
05249 static int messagecount(const char *context, const char *mailbox, const char *folder)
05250 {
05251    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05252 }
05253 
05254 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05255 {
05256    DIR *dir;
05257    struct dirent *de;
05258    char fn[256];
05259    int ret = 0;
05260 
05261    /* If no mailbox, return immediately */
05262    if (ast_strlen_zero(mailbox))
05263       return 0;
05264 
05265    if (ast_strlen_zero(folder))
05266       folder = "INBOX";
05267    if (ast_strlen_zero(context))
05268       context = "default";
05269 
05270    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05271 
05272    if (!(dir = opendir(fn)))
05273       return 0;
05274 
05275    while ((de = readdir(dir))) {
05276       if (!strncasecmp(de->d_name, "msg", 3)) {
05277          if (shortcircuit) {
05278             ret = 1;
05279             break;
05280          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05281             ret++;
05282          }
05283       }
05284    }
05285 
05286    closedir(dir);
05287 
05288    return ret;
05289 }
05290 
05291 /** 
05292  * \brief Determines if the given folder has messages.
05293  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05294  * \param folder the folder to look in
05295  *
05296  * This function is used when the mailbox is stored in a filesystem back end.
05297  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05298  * \return 1 if the folder has one or more messages. zero otherwise.
05299  */
05300 static int has_voicemail(const char *mailbox, const char *folder)
05301 {
05302    char tmp[256], *tmp2 = tmp, *box, *context;
05303    ast_copy_string(tmp, mailbox, sizeof(tmp));
05304    if (ast_strlen_zero(folder)) {
05305       folder = "INBOX";
05306    }
05307    while ((box = strsep(&tmp2, ",&"))) {
05308       if ((context = strchr(box, '@')))
05309          *context++ = '\0';
05310       else
05311          context = "default";
05312       if (__has_voicemail(context, box, folder, 1))
05313          return 1;
05314       /* If we are checking INBOX, we should check Urgent as well */
05315       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05316          return 1;
05317       }
05318    }
05319    return 0;
05320 }
05321 
05322 
05323 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05324 {
05325    char tmp[256];
05326    char *context;
05327 
05328    /* If no mailbox, return immediately */
05329    if (ast_strlen_zero(mailbox))
05330       return 0;
05331 
05332    if (newmsgs)
05333       *newmsgs = 0;
05334    if (oldmsgs)
05335       *oldmsgs = 0;
05336    if (urgentmsgs)
05337       *urgentmsgs = 0;
05338 
05339    if (strchr(mailbox, ',')) {
05340       int tmpnew, tmpold, tmpurgent;
05341       char *mb, *cur;
05342 
05343       ast_copy_string(tmp, mailbox, sizeof(tmp));
05344       mb = tmp;
05345       while ((cur = strsep(&mb, ", "))) {
05346          if (!ast_strlen_zero(cur)) {
05347             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05348                return -1;
05349             else {
05350                if (newmsgs)
05351                   *newmsgs += tmpnew; 
05352                if (oldmsgs)
05353                   *oldmsgs += tmpold;
05354                if (urgentmsgs)
05355                   *urgentmsgs += tmpurgent;
05356             }
05357          }
05358       }
05359       return 0;
05360    }
05361 
05362    ast_copy_string(tmp, mailbox, sizeof(tmp));
05363    
05364    if ((context = strchr(tmp, '@')))
05365       *context++ = '\0';
05366    else
05367       context = "default";
05368 
05369    if (newmsgs)
05370       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05371    if (oldmsgs)
05372       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05373    if (urgentmsgs)
05374       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05375 
05376    return 0;
05377 }
05378 
05379 #endif
05380 
05381 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05382 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05383 {
05384    int urgentmsgs = 0;
05385    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05386    if (newmsgs) {
05387       *newmsgs += urgentmsgs;
05388    }
05389    return res;
05390 }
05391 
05392 static void run_externnotify(char *context, char *extension, const char *flag)
05393 {
05394    char arguments[255];
05395    char ext_context[256] = "";
05396    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05397    struct ast_smdi_mwi_message *mwi_msg;
05398 
05399    if (!ast_strlen_zero(context))
05400       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05401    else
05402       ast_copy_string(ext_context, extension, sizeof(ext_context));
05403 
05404    if (smdi_iface) {
05405       if (ast_app_has_voicemail(ext_context, NULL)) 
05406          ast_smdi_mwi_set(smdi_iface, extension);
05407       else
05408          ast_smdi_mwi_unset(smdi_iface, extension);
05409 
05410       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05411          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05412          if (!strncmp(mwi_msg->cause, "INV", 3))
05413             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05414          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05415             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05416          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05417          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05418       } else {
05419          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05420       }
05421    }
05422 
05423    if (!ast_strlen_zero(externnotify)) {
05424       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05425          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05426       } else {
05427          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05428          ast_debug(1, "Executing %s\n", arguments);
05429          ast_safe_system(arguments);
05430       }
05431    }
05432 }
05433 
05434 /*!
05435  * \brief Variables used for saving a voicemail.
05436  *
05437  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05438  */
05439 struct leave_vm_options {
05440    unsigned int flags;
05441    signed char record_gain;
05442    char *exitcontext;
05443 };
05444 
05445 /*!
05446  * \brief Prompts the user and records a voicemail to a mailbox.
05447  * \param chan
05448  * \param ext
05449  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05450  * 
05451  * 
05452  * 
05453  * \return zero on success, -1 on error.
05454  */
05455 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05456 {
05457 #ifdef IMAP_STORAGE
05458    int newmsgs, oldmsgs;
05459 #else
05460    char urgdir[PATH_MAX];
05461 #endif
05462    char txtfile[PATH_MAX];
05463    char tmptxtfile[PATH_MAX];
05464    struct vm_state *vms = NULL;
05465    char callerid[256];
05466    FILE *txt;
05467    char date[256];
05468    int txtdes;
05469    int res = 0;
05470    int msgnum;
05471    int duration = 0;
05472    int ausemacro = 0;
05473    int ousemacro = 0;
05474    int ouseexten = 0;
05475    char tmpdur[16];
05476    char priority[16];
05477    char origtime[16];
05478    char dir[PATH_MAX];
05479    char tmpdir[PATH_MAX];
05480    char fn[PATH_MAX];
05481    char prefile[PATH_MAX] = "";
05482    char tempfile[PATH_MAX] = "";
05483    char ext_context[256] = "";
05484    char fmt[80];
05485    char *context;
05486    char ecodes[17] = "#";
05487    struct ast_str *tmp = ast_str_create(16);
05488    char *tmpptr;
05489    struct ast_vm_user *vmu;
05490    struct ast_vm_user svm;
05491    const char *category = NULL;
05492    const char *code;
05493    const char *alldtmf = "0123456789ABCD*#";
05494    char flag[80];
05495 
05496    if (!tmp) {
05497       return -1;
05498    }
05499 
05500    ast_str_set(&tmp, 0, "%s", ext);
05501    ext = ast_str_buffer(tmp);
05502    if ((context = strchr(ext, '@'))) {
05503       *context++ = '\0';
05504       tmpptr = strchr(context, '&');
05505    } else {
05506       tmpptr = strchr(ext, '&');
05507    }
05508 
05509    if (tmpptr)
05510       *tmpptr++ = '\0';
05511 
05512    ast_channel_lock(chan);
05513    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05514       category = ast_strdupa(category);
05515    }
05516    ast_channel_unlock(chan);
05517 
05518    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05519       ast_copy_string(flag, "Urgent", sizeof(flag));
05520    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05521       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05522    } else {
05523       flag[0] = '\0';
05524    }
05525 
05526    ast_debug(3, "Before find_user\n");
05527    if (!(vmu = find_user(&svm, context, ext))) {
05528       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05529       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05530       ast_free(tmp);
05531       return res;
05532    }
05533    /* Setup pre-file if appropriate */
05534    if (strcmp(vmu->context, "default"))
05535       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05536    else
05537       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05538 
05539    /* Set the path to the prefile. Will be one of 
05540       VM_SPOOL_DIRcontext/ext/busy
05541       VM_SPOOL_DIRcontext/ext/unavail
05542       Depending on the flag set in options.
05543    */
05544    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05545       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05546    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05547       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05548    }
05549    /* Set the path to the tmpfile as
05550       VM_SPOOL_DIR/context/ext/temp
05551       and attempt to create the folder structure.
05552    */
05553    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05554    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05555       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05556       ast_free(tmp);
05557       return -1;
05558    }
05559    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05560    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05561       ast_copy_string(prefile, tempfile, sizeof(prefile));
05562 
05563    DISPOSE(tempfile, -1);
05564    /* It's easier just to try to make it than to check for its existence */
05565 #ifndef IMAP_STORAGE
05566    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05567 #else
05568    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05569    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05570       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05571    }
05572 #endif
05573 
05574    /* Check current or macro-calling context for special extensions */
05575    if (ast_test_flag(vmu, VM_OPERATOR)) {
05576       if (!ast_strlen_zero(vmu->exit)) {
05577          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05578             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05579             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05580             ouseexten = 1;
05581          }
05582       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05583          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05584          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05585          ouseexten = 1;
05586       } else if (!ast_strlen_zero(chan->macrocontext)
05587          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05588             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05589          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05590          ousemacro = 1;
05591       }
05592    }
05593 
05594    if (!ast_strlen_zero(vmu->exit)) {
05595       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05596          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05597          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05598       }
05599    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05600       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05601       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05602    } else if (!ast_strlen_zero(chan->macrocontext)
05603       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05604          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05605       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05606       ausemacro = 1;
05607    }
05608 
05609    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05610       for (code = alldtmf; *code; code++) {
05611          char e[2] = "";
05612          e[0] = *code;
05613          if (strchr(ecodes, e[0]) == NULL
05614             && ast_canmatch_extension(chan, chan->context, e, 1,
05615                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05616             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05617          }
05618       }
05619    }
05620 
05621    /* Play the beginning intro if desired */
05622    if (!ast_strlen_zero(prefile)) {
05623 #ifdef ODBC_STORAGE
05624       int success = 
05625 #endif
05626          RETRIEVE(prefile, -1, ext, context);
05627       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05628          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05629             res = ast_waitstream(chan, ecodes);
05630 #ifdef ODBC_STORAGE
05631          if (success == -1) {
05632             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05633             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05634             store_file(prefile, vmu->mailbox, vmu->context, -1);
05635          }
05636 #endif
05637       } else {
05638          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05639          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05640       }
05641       DISPOSE(prefile, -1);
05642       if (res < 0) {
05643          ast_debug(1, "Hang up during prefile playback\n");
05644          free_user(vmu);
05645          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05646          ast_free(tmp);
05647          return -1;
05648       }
05649    }
05650    if (res == '#') {
05651       /* On a '#' we skip the instructions */
05652       ast_set_flag(options, OPT_SILENT);
05653       res = 0;
05654    }
05655    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05656    if (vmu->maxmsg == 0) {
05657       if (option_debug > 2)
05658          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05659       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05660       goto leave_vm_out;
05661    }
05662    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05663       res = ast_stream_and_wait(chan, INTRO, ecodes);
05664       if (res == '#') {
05665          ast_set_flag(options, OPT_SILENT);
05666          res = 0;
05667       }
05668    }
05669    if (res > 0)
05670       ast_stopstream(chan);
05671    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05672     other than the operator -- an automated attendant or mailbox login for example */
05673    if (res == '*') {
05674       chan->exten[0] = 'a';
05675       chan->exten[1] = '\0';
05676       if (!ast_strlen_zero(vmu->exit)) {
05677          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05678       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05679          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05680       }
05681       chan->priority = 0;
05682       free_user(vmu);
05683       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05684       ast_free(tmp);
05685       return 0;
05686    }
05687 
05688    /* Check for a '0' here */
05689    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05690    transfer:
05691       if (ouseexten || ousemacro) {
05692          chan->exten[0] = 'o';
05693          chan->exten[1] = '\0';
05694          if (!ast_strlen_zero(vmu->exit)) {
05695             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05696          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05697             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05698          }
05699          ast_play_and_wait(chan, "transfer");
05700          chan->priority = 0;
05701          free_user(vmu);
05702          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05703       }
05704       ast_free(tmp);
05705       return OPERATOR_EXIT;
05706    }
05707 
05708    /* Allow all other digits to exit Voicemail and return to the dialplan */
05709    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05710       if (!ast_strlen_zero(options->exitcontext))
05711          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05712       free_user(vmu);
05713       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05714       ast_free(tmp);
05715       return res;
05716    }
05717 
05718    if (res < 0) {
05719       free_user(vmu);
05720       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05721       ast_free(tmp);
05722       return -1;
05723    }
05724    /* The meat of recording the message...  All the announcements and beeps have been played*/
05725    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05726    if (!ast_strlen_zero(fmt)) {
05727       msgnum = 0;
05728 
05729 #ifdef IMAP_STORAGE
05730       /* Is ext a mailbox? */
05731       /* must open stream for this user to get info! */
05732       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05733       if (res < 0) {
05734          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05735          ast_free(tmp);
05736          return -1;
05737       }
05738       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05739       /* It is possible under certain circumstances that inboxcount did not
05740        * create a vm_state when it was needed. This is a catchall which will
05741        * rarely be used.
05742        */
05743          if (!(vms = create_vm_state_from_user(vmu))) {
05744             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05745             ast_free(tmp);
05746             return -1;
05747          }
05748       }
05749       vms->newmessages++;
05750       
05751       /* here is a big difference! We add one to it later */
05752       msgnum = newmsgs + oldmsgs;
05753       ast_debug(3, "Messagecount set to %d\n", msgnum);
05754       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05755       /* set variable for compatibility */
05756       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05757 
05758       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05759          goto leave_vm_out;
05760       }
05761 #else
05762       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05763          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05764          if (!res)
05765             res = ast_waitstream(chan, "");
05766          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05767          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05768          inprocess_count(vmu->mailbox, vmu->context, -1);
05769          goto leave_vm_out;
05770       }
05771 
05772 #endif
05773       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05774       txtdes = mkstemp(tmptxtfile);
05775       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05776       if (txtdes < 0) {
05777          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05778          if (!res)
05779             res = ast_waitstream(chan, "");
05780          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05781          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05782          inprocess_count(vmu->mailbox, vmu->context, -1);
05783          goto leave_vm_out;
05784       }
05785 
05786       /* Now play the beep once we have the message number for our next message. */
05787       if (res >= 0) {
05788          /* Unless we're *really* silent, try to send the beep */
05789          res = ast_stream_and_wait(chan, "beep", "");
05790       }
05791             
05792       /* Store information in real-time storage */
05793       if (ast_check_realtime("voicemail_data")) {
05794          snprintf(priority, sizeof(priority), "%d", chan->priority);
05795          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05796          get_date(date, sizeof(date));
05797          ast_callerid_merge(callerid, sizeof(callerid),
05798             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05799             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05800             "Unknown");
05801          ast_store_realtime("voicemail_data",
05802             "origmailbox", ext,
05803             "context", chan->context,
05804             "macrocontext", chan->macrocontext,
05805             "exten", chan->exten,
05806             "priority", priority,
05807             "callerchan", chan->name,
05808             "callerid", callerid,
05809             "origdate", date,
05810             "origtime", origtime,
05811             "category", S_OR(category, ""),
05812             "filename", tmptxtfile,
05813             SENTINEL);
05814       }
05815 
05816       /* Store information */
05817       txt = fdopen(txtdes, "w+");
05818       if (txt) {
05819          get_date(date, sizeof(date));
05820          ast_callerid_merge(callerid, sizeof(callerid),
05821             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05822             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05823             "Unknown");
05824          fprintf(txt, 
05825             ";\n"
05826             "; Message Information file\n"
05827             ";\n"
05828             "[message]\n"
05829             "origmailbox=%s\n"
05830             "context=%s\n"
05831             "macrocontext=%s\n"
05832             "exten=%s\n"
05833             "rdnis=%s\n"
05834             "priority=%d\n"
05835             "callerchan=%s\n"
05836             "callerid=%s\n"
05837             "origdate=%s\n"
05838             "origtime=%ld\n"
05839             "category=%s\n",
05840             ext,
05841             chan->context,
05842             chan->macrocontext, 
05843             chan->exten,
05844             S_COR(chan->redirecting.from.number.valid,
05845                chan->redirecting.from.number.str, "unknown"),
05846             chan->priority,
05847             chan->name,
05848             callerid,
05849             date, (long) time(NULL),
05850             category ? category : "");
05851       } else {
05852          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05853          inprocess_count(vmu->mailbox, vmu->context, -1);
05854          if (ast_check_realtime("voicemail_data")) {
05855             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05856          }
05857          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05858          goto leave_vm_out;
05859       }
05860       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05861 
05862       if (txt) {
05863          fprintf(txt, "flag=%s\n", flag);
05864          if (duration < vmu->minsecs) {
05865             fclose(txt);
05866             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05867             ast_filedelete(tmptxtfile, NULL);
05868             unlink(tmptxtfile);
05869             if (ast_check_realtime("voicemail_data")) {
05870                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05871             }
05872             inprocess_count(vmu->mailbox, vmu->context, -1);
05873          } else {
05874             fprintf(txt, "duration=%d\n", duration);
05875             fclose(txt);
05876             if (vm_lock_path(dir)) {
05877                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05878                /* Delete files */
05879                ast_filedelete(tmptxtfile, NULL);
05880                unlink(tmptxtfile);
05881                inprocess_count(vmu->mailbox, vmu->context, -1);
05882             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05883                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05884                unlink(tmptxtfile);
05885                ast_unlock_path(dir);
05886                inprocess_count(vmu->mailbox, vmu->context, -1);
05887                if (ast_check_realtime("voicemail_data")) {
05888                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05889                }
05890             } else {
05891 #ifndef IMAP_STORAGE
05892                msgnum = last_message_index(vmu, dir) + 1;
05893 #endif
05894                make_file(fn, sizeof(fn), dir, msgnum);
05895 
05896                /* assign a variable with the name of the voicemail file */ 
05897 #ifndef IMAP_STORAGE
05898                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05899 #else
05900                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05901 #endif
05902 
05903                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05904                ast_filerename(tmptxtfile, fn, NULL);
05905                rename(tmptxtfile, txtfile);
05906                inprocess_count(vmu->mailbox, vmu->context, -1);
05907 
05908                /* Properly set permissions on voicemail text descriptor file.
05909                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05910                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05911                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05912 
05913                ast_unlock_path(dir);
05914                if (ast_check_realtime("voicemail_data")) {
05915                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05916                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05917                }
05918                /* We must store the file first, before copying the message, because
05919                 * ODBC storage does the entire copy with SQL.
05920                 */
05921                if (ast_fileexists(fn, NULL, NULL) > 0) {
05922                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05923                }
05924 
05925                /* Are there to be more recipients of this message? */
05926                while (tmpptr) {
05927                   struct ast_vm_user recipu, *recip;
05928                   char *exten, *cntx;
05929                
05930                   exten = strsep(&tmpptr, "&");
05931                   cntx = strchr(exten, '@');
05932                   if (cntx) {
05933                      *cntx = '\0';
05934                      cntx++;
05935                   }
05936                   if ((recip = find_user(&recipu, cntx, exten))) {
05937                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05938                      free_user(recip);
05939                   }
05940                }
05941 #ifndef IMAP_STORAGE
05942                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05943                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05944                   char sfn[PATH_MAX];
05945                   char dfn[PATH_MAX];
05946                   int x;
05947                   /* It's easier just to try to make it than to check for its existence */
05948                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05949                   x = last_message_index(vmu, urgdir) + 1;
05950                   make_file(sfn, sizeof(sfn), dir, msgnum);
05951                   make_file(dfn, sizeof(dfn), urgdir, x);
05952                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05953                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05954                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05955                   ast_copy_string(fn, dfn, sizeof(fn));
05956                   msgnum = x;
05957                }
05958 #endif
05959                /* Notification needs to happen after the copy, though. */
05960                if (ast_fileexists(fn, NULL, NULL)) {
05961 #ifdef IMAP_STORAGE
05962                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
05963                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05964                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05965                      flag);
05966 #else
05967                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
05968                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05969                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05970                      flag);
05971 #endif
05972                }
05973 
05974                /* Disposal needs to happen after the optional move and copy */
05975                if (ast_fileexists(fn, NULL, NULL)) {
05976                   DISPOSE(dir, msgnum);
05977                }
05978             }
05979          }
05980       } else {
05981          inprocess_count(vmu->mailbox, vmu->context, -1);
05982       }
05983       if (res == '0') {
05984          goto transfer;
05985       } else if (res > 0 && res != 't')
05986          res = 0;
05987 
05988       if (duration < vmu->minsecs)
05989          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05990          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05991       else
05992          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05993    } else
05994       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05995 leave_vm_out:
05996    free_user(vmu);
05997 
05998 #ifdef IMAP_STORAGE
05999    /* expunge message - use UID Expunge if supported on IMAP server*/
06000    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06001    if (expungeonhangup == 1) {
06002       ast_mutex_lock(&vms->lock);
06003 #ifdef HAVE_IMAP_TK2006
06004       if (LEVELUIDPLUS (vms->mailstream)) {
06005          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06006       } else 
06007 #endif
06008          mail_expunge(vms->mailstream);
06009       ast_mutex_unlock(&vms->lock);
06010    }
06011 #endif
06012 
06013    ast_free(tmp);
06014    return res;
06015 }
06016 
06017 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06018 {
06019    int d;
06020    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06021    return d;
06022 }
06023 
06024 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06025 {
06026 #ifdef IMAP_STORAGE
06027    /* we must use mbox(x) folder names, and copy the message there */
06028    /* simple. huh? */
06029    char sequence[10];
06030    char mailbox[256];
06031    int res;
06032 
06033    /* get the real IMAP message number for this message */
06034    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06035    
06036    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06037    ast_mutex_lock(&vms->lock);
06038    /* if save to Old folder, put in INBOX as read */
06039    if (box == OLD_FOLDER) {
06040       mail_setflag(vms->mailstream, sequence, "\\Seen");
06041       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06042    } else if (box == NEW_FOLDER) {
06043       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06044       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06045    }
06046    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06047       ast_mutex_unlock(&vms->lock);
06048       return 0;
06049    }
06050    /* Create the folder if it don't exist */
06051    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06052    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06053    if (mail_create(vms->mailstream, mailbox) == NIL) 
06054       ast_debug(5, "Folder exists.\n");
06055    else
06056       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06057    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06058    ast_mutex_unlock(&vms->lock);
06059    return res;
06060 #else
06061    char *dir = vms->curdir;
06062    char *username = vms->username;
06063    char *context = vmu->context;
06064    char sfn[PATH_MAX];
06065    char dfn[PATH_MAX];
06066    char ddir[PATH_MAX];
06067    const char *dbox = mbox(vmu, box);
06068    int x, i;
06069    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06070 
06071    if (vm_lock_path(ddir))
06072       return ERROR_LOCK_PATH;
06073 
06074    x = last_message_index(vmu, ddir) + 1;
06075 
06076    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06077       x--;
06078       for (i = 1; i <= x; i++) {
06079          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06080          make_file(sfn, sizeof(sfn), ddir, i);
06081          make_file(dfn, sizeof(dfn), ddir, i - 1);
06082          if (EXISTS(ddir, i, sfn, NULL)) {
06083             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06084          } else
06085             break;
06086       }
06087    } else {
06088       if (x >= vmu->maxmsg) {
06089          ast_unlock_path(ddir);
06090          return -1;
06091       }
06092    }
06093    make_file(sfn, sizeof(sfn), dir, msg);
06094    make_file(dfn, sizeof(dfn), ddir, x);
06095    if (strcmp(sfn, dfn)) {
06096       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06097    }
06098    ast_unlock_path(ddir);
06099 #endif
06100    return 0;
06101 }
06102 
06103 static int adsi_logo(unsigned char *buf)
06104 {
06105    int bytes = 0;
06106    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06107    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06108    return bytes;
06109 }
06110 
06111 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06112 {
06113    unsigned char buf[256];
06114    int bytes = 0;
06115    int x;
06116    char num[5];
06117 
06118    *useadsi = 0;
06119    bytes += ast_adsi_data_mode(buf + bytes);
06120    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06121 
06122    bytes = 0;
06123    bytes += adsi_logo(buf);
06124    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06125 #ifdef DISPLAY
06126    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06127 #endif
06128    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06129    bytes += ast_adsi_data_mode(buf + bytes);
06130    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06131 
06132    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06133       bytes = 0;
06134       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06135       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06136       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06137       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06138       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06139       return 0;
06140    }
06141 
06142 #ifdef DISPLAY
06143    /* Add a dot */
06144    bytes = 0;
06145    bytes += ast_adsi_logo(buf);
06146    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06147    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06148    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06149    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06150 #endif
06151    bytes = 0;
06152    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06153    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06154    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06155    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06156    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06157    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06158    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06159 
06160 #ifdef DISPLAY
06161    /* Add another dot */
06162    bytes = 0;
06163    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06164    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06165 
06166    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06167    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06168 #endif
06169 
06170    bytes = 0;
06171    /* These buttons we load but don't use yet */
06172    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06173    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06174    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06175    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06176    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06177    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06178    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06179 
06180 #ifdef DISPLAY
06181    /* Add another dot */
06182    bytes = 0;
06183    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06184    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06185    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06186 #endif
06187 
06188    bytes = 0;
06189    for (x = 0; x < 5; x++) {
06190       snprintf(num, sizeof(num), "%d", x);
06191       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06192    }
06193    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06194    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06195 
06196 #ifdef DISPLAY
06197    /* Add another dot */
06198    bytes = 0;
06199    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06200    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06201    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06202 #endif
06203 
06204    if (ast_adsi_end_download(chan)) {
06205       bytes = 0;
06206       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06207       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06208       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06209       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06210       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06211       return 0;
06212    }
06213    bytes = 0;
06214    bytes += ast_adsi_download_disconnect(buf + bytes);
06215    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06216    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06217 
06218    ast_debug(1, "Done downloading scripts...\n");
06219 
06220 #ifdef DISPLAY
06221    /* Add last dot */
06222    bytes = 0;
06223    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06224    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06225 #endif
06226    ast_debug(1, "Restarting session...\n");
06227 
06228    bytes = 0;
06229    /* Load the session now */
06230    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06231       *useadsi = 1;
06232       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06233    } else
06234       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06235 
06236    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06237    return 0;
06238 }
06239 
06240 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06241 {
06242    int x;
06243    if (!ast_adsi_available(chan))
06244       return;
06245    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06246    if (x < 0)
06247       return;
06248    if (!x) {
06249       if (adsi_load_vmail(chan, useadsi)) {
06250          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06251          return;
06252       }
06253    } else
06254       *useadsi = 1;
06255 }
06256 
06257 static void adsi_login(struct ast_channel *chan)
06258 {
06259    unsigned char buf[256];
06260    int bytes = 0;
06261    unsigned char keys[8];
06262    int x;
06263    if (!ast_adsi_available(chan))
06264       return;
06265 
06266    for (x = 0; x < 8; x++)
06267       keys[x] = 0;
06268    /* Set one key for next */
06269    keys[3] = ADSI_KEY_APPS + 3;
06270 
06271    bytes += adsi_logo(buf + bytes);
06272    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06273    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06274    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06275    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06276    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06277    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06278    bytes += ast_adsi_set_keys(buf + bytes, keys);
06279    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06280    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06281 }
06282 
06283 static void adsi_password(struct ast_channel *chan)
06284 {
06285    unsigned char buf[256];
06286    int bytes = 0;
06287    unsigned char keys[8];
06288    int x;
06289    if (!ast_adsi_available(chan))
06290       return;
06291 
06292    for (x = 0; x < 8; x++)
06293       keys[x] = 0;
06294    /* Set one key for next */
06295    keys[3] = ADSI_KEY_APPS + 3;
06296 
06297    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06298    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06299    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06300    bytes += ast_adsi_set_keys(buf + bytes, keys);
06301    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06302    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06303 }
06304 
06305 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06306 {
06307    unsigned char buf[256];
06308    int bytes = 0;
06309    unsigned char keys[8];
06310    int x, y;
06311 
06312    if (!ast_adsi_available(chan))
06313       return;
06314 
06315    for (x = 0; x < 5; x++) {
06316       y = ADSI_KEY_APPS + 12 + start + x;
06317       if (y > ADSI_KEY_APPS + 12 + 4)
06318          y = 0;
06319       keys[x] = ADSI_KEY_SKT | y;
06320    }
06321    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06322    keys[6] = 0;
06323    keys[7] = 0;
06324 
06325    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06326    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06327    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06328    bytes += ast_adsi_set_keys(buf + bytes, keys);
06329    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06330 
06331    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06332 }
06333 
06334 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06335 {
06336    int bytes = 0;
06337    unsigned char buf[256]; 
06338    char buf1[256], buf2[256];
06339    char fn2[PATH_MAX];
06340 
06341    char cid[256] = "";
06342    char *val;
06343    char *name, *num;
06344    char datetime[21] = "";
06345    FILE *f;
06346 
06347    unsigned char keys[8];
06348 
06349    int x;
06350 
06351    if (!ast_adsi_available(chan))
06352       return;
06353 
06354    /* Retrieve important info */
06355    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06356    f = fopen(fn2, "r");
06357    if (f) {
06358       while (!feof(f)) {   
06359          if (!fgets((char *) buf, sizeof(buf), f)) {
06360             continue;
06361          }
06362          if (!feof(f)) {
06363             char *stringp = NULL;
06364             stringp = (char *) buf;
06365             strsep(&stringp, "=");
06366             val = strsep(&stringp, "=");
06367             if (!ast_strlen_zero(val)) {
06368                if (!strcmp((char *) buf, "callerid"))
06369                   ast_copy_string(cid, val, sizeof(cid));
06370                if (!strcmp((char *) buf, "origdate"))
06371                   ast_copy_string(datetime, val, sizeof(datetime));
06372             }
06373          }
06374       }
06375       fclose(f);
06376    }
06377    /* New meaning for keys */
06378    for (x = 0; x < 5; x++)
06379       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06380    keys[6] = 0x0;
06381    keys[7] = 0x0;
06382 
06383    if (!vms->curmsg) {
06384       /* No prev key, provide "Folder" instead */
06385       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06386    }
06387    if (vms->curmsg >= vms->lastmsg) {
06388       /* If last message ... */
06389       if (vms->curmsg) {
06390          /* but not only message, provide "Folder" instead */
06391          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06392          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06393 
06394       } else {
06395          /* Otherwise if only message, leave blank */
06396          keys[3] = 1;
06397       }
06398    }
06399 
06400    if (!ast_strlen_zero(cid)) {
06401       ast_callerid_parse(cid, &name, &num);
06402       if (!name)
06403          name = num;
06404    } else
06405       name = "Unknown Caller";
06406 
06407    /* If deleted, show "undeleted" */
06408 
06409    if (vms->deleted[vms->curmsg])
06410       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06411 
06412    /* Except "Exit" */
06413    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06414    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06415       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06416    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06417 
06418    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06419    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06420    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06421    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06422    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06423    bytes += ast_adsi_set_keys(buf + bytes, keys);
06424    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06425 
06426    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06427 }
06428 
06429 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06430 {
06431    int bytes = 0;
06432    unsigned char buf[256];
06433    unsigned char keys[8];
06434 
06435    int x;
06436 
06437    if (!ast_adsi_available(chan))
06438       return;
06439 
06440    /* New meaning for keys */
06441    for (x = 0; x < 5; x++)
06442       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06443 
06444    keys[6] = 0x0;
06445    keys[7] = 0x0;
06446 
06447    if (!vms->curmsg) {
06448       /* No prev key, provide "Folder" instead */
06449       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06450    }
06451    if (vms->curmsg >= vms->lastmsg) {
06452       /* If last message ... */
06453       if (vms->curmsg) {
06454          /* but not only message, provide "Folder" instead */
06455          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06456       } else {
06457          /* Otherwise if only message, leave blank */
06458          keys[3] = 1;
06459       }
06460    }
06461 
06462    /* If deleted, show "undeleted" */
06463    if (vms->deleted[vms->curmsg]) 
06464       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06465 
06466    /* Except "Exit" */
06467    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06468    bytes += ast_adsi_set_keys(buf + bytes, keys);
06469    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06470 
06471    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06472 }
06473 
06474 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06475 {
06476    unsigned char buf[256] = "";
06477    char buf1[256] = "", buf2[256] = "";
06478    int bytes = 0;
06479    unsigned char keys[8];
06480    int x;
06481 
06482    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06483    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06484    if (!ast_adsi_available(chan))
06485       return;
06486    if (vms->newmessages) {
06487       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06488       if (vms->oldmessages) {
06489          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06490          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06491       } else {
06492          snprintf(buf2, sizeof(buf2), "%s.", newm);
06493       }
06494    } else if (vms->oldmessages) {
06495       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06496       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06497    } else {
06498       strcpy(buf1, "You have no messages.");
06499       buf2[0] = ' ';
06500       buf2[1] = '\0';
06501    }
06502    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06503    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06504    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06505 
06506    for (x = 0; x < 6; x++)
06507       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06508    keys[6] = 0;
06509    keys[7] = 0;
06510 
06511    /* Don't let them listen if there are none */
06512    if (vms->lastmsg < 0)
06513       keys[0] = 1;
06514    bytes += ast_adsi_set_keys(buf + bytes, keys);
06515 
06516    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06517 
06518    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06519 }
06520 
06521 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06522 {
06523    unsigned char buf[256] = "";
06524    char buf1[256] = "", buf2[256] = "";
06525    int bytes = 0;
06526    unsigned char keys[8];
06527    int x;
06528 
06529    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06530 
06531    if (!ast_adsi_available(chan))
06532       return;
06533 
06534    /* Original command keys */
06535    for (x = 0; x < 6; x++)
06536       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06537 
06538    keys[6] = 0;
06539    keys[7] = 0;
06540 
06541    if ((vms->lastmsg + 1) < 1)
06542       keys[0] = 0;
06543 
06544    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06545       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06546 
06547    if (vms->lastmsg + 1)
06548       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06549    else
06550       strcpy(buf2, "no messages.");
06551    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06552    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06553    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06554    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06555    bytes += ast_adsi_set_keys(buf + bytes, keys);
06556 
06557    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06558 
06559    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06560    
06561 }
06562 
06563 /*
06564 static void adsi_clear(struct ast_channel *chan)
06565 {
06566    char buf[256];
06567    int bytes=0;
06568    if (!ast_adsi_available(chan))
06569       return;
06570    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06571    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06572 
06573    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06574 }
06575 */
06576 
06577 static void adsi_goodbye(struct ast_channel *chan)
06578 {
06579    unsigned char buf[256];
06580    int bytes = 0;
06581 
06582    if (!ast_adsi_available(chan))
06583       return;
06584    bytes += adsi_logo(buf + bytes);
06585    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06586    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06587    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06588    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06589 
06590    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06591 }
06592 
06593 /*!\brief get_folder: Folder menu
06594  * Plays "press 1 for INBOX messages" etc.
06595  * Should possibly be internationalized
06596  */
06597 static int get_folder(struct ast_channel *chan, int start)
06598 {
06599    int x;
06600    int d;
06601    char fn[PATH_MAX];
06602    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06603    if (d)
06604       return d;
06605    for (x = start; x < 5; x++) { /* For all folders */
06606       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06607          return d;
06608       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06609       if (d)
06610          return d;
06611       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06612       d = vm_play_folder_name(chan, fn);
06613       if (d)
06614          return d;
06615       d = ast_waitfordigit(chan, 500);
06616       if (d)
06617          return d;
06618    }
06619    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06620    if (d)
06621       return d;
06622    d = ast_waitfordigit(chan, 4000);
06623    return d;
06624 }
06625 
06626 /*!
06627  * \brief plays a prompt and waits for a keypress.
06628  * \param chan
06629  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06630  * \param start Does not appear to be used at this time.
06631  *
06632  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06633  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06634  * prompting for the number inputs that correspond to the available folders.
06635  * 
06636  * \return zero on success, or -1 on error.
06637  */
06638 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06639 {
06640    int res = 0;
06641    int loops = 0;
06642    res = ast_play_and_wait(chan, fn);  /* Folder name */
06643    while (((res < '0') || (res > '9')) &&
06644          (res != '#') && (res >= 0) &&
06645          loops < 4) {
06646       res = get_folder(chan, 0);
06647       loops++;
06648    }
06649    if (loops == 4) { /* give up */
06650       return '#';
06651    }
06652    return res;
06653 }
06654 
06655 /*!
06656  * \brief presents the option to prepend to an existing message when forwarding it.
06657  * \param chan
06658  * \param vmu
06659  * \param curdir
06660  * \param curmsg
06661  * \param vm_fmts
06662  * \param context
06663  * \param record_gain
06664  * \param duration
06665  * \param vms
06666  * \param flag 
06667  *
06668  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06669  *
06670  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06671  * \return zero on success, -1 on error.
06672  */
06673 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06674          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06675 {
06676 #ifdef IMAP_STORAGE
06677    int res;
06678 #endif
06679    int cmd = 0;
06680    int retries = 0, prepend_duration = 0, already_recorded = 0;
06681    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06682    char textfile[PATH_MAX];
06683    struct ast_config *msg_cfg;
06684    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06685 #ifndef IMAP_STORAGE
06686    signed char zero_gain = 0;
06687 #endif
06688    const char *duration_str;
06689 
06690    /* Must always populate duration correctly */
06691    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06692    strcpy(textfile, msgfile);
06693    strcpy(backup, msgfile);
06694    strcpy(backup_textfile, msgfile);
06695    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06696    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06697    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06698 
06699    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06700       *duration = atoi(duration_str);
06701    } else {
06702       *duration = 0;
06703    }
06704 
06705    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06706       if (cmd)
06707          retries = 0;
06708       switch (cmd) {
06709       case '1': 
06710 
06711 #ifdef IMAP_STORAGE
06712          /* Record new intro file */
06713          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06714          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06715          res = ast_play_and_wait(chan, INTRO);
06716          res = ast_play_and_wait(chan, "beep");
06717          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06718          cmd = 't';
06719 #else
06720 
06721          /* prepend a message to the current message, update the metadata and return */
06722 
06723          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06724          strcpy(textfile, msgfile);
06725          strncat(textfile, ".txt", sizeof(textfile) - 1);
06726          *duration = 0;
06727 
06728          /* if we can't read the message metadata, stop now */
06729          if (!msg_cfg) {
06730             cmd = 0;
06731             break;
06732          }
06733          
06734          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06735          if (already_recorded) {
06736             ast_filecopy(backup, msgfile, NULL);
06737             copy(backup_textfile, textfile);
06738          }
06739          else {
06740             ast_filecopy(msgfile, backup, NULL);
06741             copy(textfile,backup_textfile);
06742          }
06743          already_recorded = 1;
06744 
06745          if (record_gain)
06746             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06747 
06748          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06749          if (cmd == 'S') {
06750             ast_filerename(backup, msgfile, NULL);
06751          }
06752 
06753          if (record_gain)
06754             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06755 
06756          
06757          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06758             *duration = atoi(duration_str);
06759 
06760          if (prepend_duration) {
06761             struct ast_category *msg_cat;
06762             /* need enough space for a maximum-length message duration */
06763             char duration_buf[12];
06764 
06765             *duration += prepend_duration;
06766             msg_cat = ast_category_get(msg_cfg, "message");
06767             snprintf(duration_buf, 11, "%ld", *duration);
06768             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06769                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06770             }
06771          }
06772 
06773 #endif
06774          break;
06775       case '2': 
06776          /* NULL out introfile so we know there is no intro! */
06777 #ifdef IMAP_STORAGE
06778          *vms->introfn = '\0';
06779 #endif
06780          cmd = 't';
06781          break;
06782       case '*':
06783          cmd = '*';
06784          break;
06785       default: 
06786          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06787             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06788          if (!cmd)
06789             cmd = ast_play_and_wait(chan, "vm-starmain");
06790             /* "press star to return to the main menu" */
06791          if (!cmd)
06792             cmd = ast_waitfordigit(chan, 6000);
06793          if (!cmd)
06794             retries++;
06795          if (retries > 3)
06796             cmd = 't';
06797       }
06798    }
06799 
06800    if (msg_cfg)
06801       ast_config_destroy(msg_cfg);
06802    if (prepend_duration)
06803       *duration = prepend_duration;
06804 
06805    if (already_recorded && cmd == -1) {
06806       /* restore original message if prepention cancelled */
06807       ast_filerename(backup, msgfile, NULL);
06808       rename(backup_textfile, textfile);
06809    }
06810 
06811    if (cmd == 't' || cmd == 'S')
06812       cmd = 0;
06813    return cmd;
06814 }
06815 
06816 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06817 {
06818    struct ast_event *event;
06819    char *mailbox, *context;
06820 
06821    /* Strip off @default */
06822    context = mailbox = ast_strdupa(box);
06823    strsep(&context, "@");
06824    if (ast_strlen_zero(context))
06825       context = "default";
06826 
06827    if (!(event = ast_event_new(AST_EVENT_MWI,
06828          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06829          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06830          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06831          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06832          AST_EVENT_IE_END))) {
06833       return;
06834    }
06835 
06836    ast_event_queue_and_cache(event);
06837 }
06838 
06839 /*!
06840  * \brief Sends email notification that a user has a new voicemail waiting for them.
06841  * \param chan
06842  * \param vmu
06843  * \param vms
06844  * \param msgnum
06845  * \param duration
06846  * \param fmt
06847  * \param cidnum The Caller ID phone number value.
06848  * \param cidname The Caller ID name value.
06849  * \param flag
06850  *
06851  * \return zero on success, -1 on error.
06852  */
06853 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)
06854 {
06855    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06856    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06857    const char *category;
06858    char *myserveremail = serveremail;
06859 
06860    ast_channel_lock(chan);
06861    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06862       category = ast_strdupa(category);
06863    }
06864    ast_channel_unlock(chan);
06865 
06866 #ifndef IMAP_STORAGE
06867    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06868 #else
06869    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06870 #endif
06871    make_file(fn, sizeof(fn), todir, msgnum);
06872    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06873 
06874    if (!ast_strlen_zero(vmu->attachfmt)) {
06875       if (strstr(fmt, vmu->attachfmt))
06876          fmt = vmu->attachfmt;
06877       else
06878          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);
06879    }
06880 
06881    /* Attach only the first format */
06882    fmt = ast_strdupa(fmt);
06883    stringp = fmt;
06884    strsep(&stringp, "|");
06885 
06886    if (!ast_strlen_zero(vmu->serveremail))
06887       myserveremail = vmu->serveremail;
06888 
06889    if (!ast_strlen_zero(vmu->email)) {
06890       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06891 
06892       if (attach_user_voicemail)
06893          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06894 
06895       /* XXX possible imap issue, should category be NULL XXX */
06896       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06897 
06898       if (attach_user_voicemail)
06899          DISPOSE(todir, msgnum);
06900    }
06901 
06902    if (!ast_strlen_zero(vmu->pager)) {
06903       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
06904    }
06905 
06906    if (ast_test_flag(vmu, VM_DELETE))
06907       DELETE(todir, msgnum, fn, vmu);
06908 
06909    /* Leave voicemail for someone */
06910    if (ast_app_has_voicemail(ext_context, NULL)) 
06911       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06912 
06913    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06914 
06915    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);
06916    run_externnotify(vmu->context, vmu->mailbox, flag);
06917 
06918 #ifdef IMAP_STORAGE
06919    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06920    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06921       vm_imap_delete(NULL, vms->curmsg, vmu);
06922       vms->newmessages--;  /* Fix new message count */
06923    }
06924 #endif
06925 
06926    return 0;
06927 }
06928 
06929 /*!
06930  * \brief Sends a voicemail message to a mailbox recipient.
06931  * \param chan
06932  * \param context
06933  * \param vms
06934  * \param sender
06935  * \param fmt
06936  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06937  *             Will be 0 when called to forward an existing message (option 8)
06938  *             Will be 1 when called to leave a message (option 3->5)
06939  * \param record_gain 
06940  * \param urgent
06941  *
06942  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06943  * 
06944  * When in the leave message mode (is_new_message == 1):
06945  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06946  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06947  *
06948  * When in the forward message mode (is_new_message == 0):
06949  *   - retreives the current message to be forwarded
06950  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06951  *   - determines the target mailbox and folders
06952  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06953  *
06954  * \return zero on success, -1 on error.
06955  */
06956 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)
06957 {
06958 #ifdef IMAP_STORAGE
06959    int todircount = 0;
06960    struct vm_state *dstvms;
06961 #endif
06962    char username[70]="";
06963    char fn[PATH_MAX]; /* for playback of name greeting */
06964    char ecodes[16] = "#";
06965    int res = 0, cmd = 0;
06966    struct ast_vm_user *receiver = NULL, *vmtmp;
06967    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06968    char *stringp;
06969    const char *s;
06970    int saved_messages = 0;
06971    int valid_extensions = 0;
06972    char *dir;
06973    int curmsg;
06974    char urgent_str[7] = "";
06975    char tmptxtfile[PATH_MAX];
06976    int prompt_played = 0;
06977 #ifndef IMAP_STORAGE
06978    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06979 #endif
06980    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06981       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06982    }
06983 
06984    if (vms == NULL) return -1;
06985    dir = vms->curdir;
06986    curmsg = vms->curmsg;
06987 
06988    tmptxtfile[0] = '\0';
06989    while (!res && !valid_extensions) {
06990       int use_directory = 0;
06991       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06992          int done = 0;
06993          int retries = 0;
06994          cmd = 0;
06995          while ((cmd >= 0) && !done ){
06996             if (cmd)
06997                retries = 0;
06998             switch (cmd) {
06999             case '1': 
07000                use_directory = 0;
07001                done = 1;
07002                break;
07003             case '2': 
07004                use_directory = 1;
07005                done = 1;
07006                break;
07007             case '*': 
07008                cmd = 't';
07009                done = 1;
07010                break;
07011             default: 
07012                /* Press 1 to enter an extension press 2 to use the directory */
07013                cmd = ast_play_and_wait(chan, "vm-forward");
07014                if (!cmd)
07015                   cmd = ast_waitfordigit(chan, 3000);
07016                if (!cmd)
07017                   retries++;
07018                if (retries > 3) {
07019                   cmd = 't';
07020                   done = 1;
07021                }
07022                
07023             }
07024          }
07025          if (cmd < 0 || cmd == 't')
07026             break;
07027       }
07028       
07029       if (use_directory) {
07030          /* use app_directory */
07031          
07032          char old_context[sizeof(chan->context)];
07033          char old_exten[sizeof(chan->exten)];
07034          int old_priority;
07035          struct ast_app* directory_app;
07036 
07037          directory_app = pbx_findapp("Directory");
07038          if (directory_app) {
07039             char vmcontext[256];
07040             /* make backup copies */
07041             memcpy(old_context, chan->context, sizeof(chan->context));
07042             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07043             old_priority = chan->priority;
07044             
07045             /* call the the Directory, changes the channel */
07046             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07047             res = pbx_exec(chan, directory_app, vmcontext);
07048             
07049             ast_copy_string(username, chan->exten, sizeof(username));
07050             
07051             /* restore the old context, exten, and priority */
07052             memcpy(chan->context, old_context, sizeof(chan->context));
07053             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07054             chan->priority = old_priority;
07055          } else {
07056             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07057             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07058          }
07059       } else {
07060          /* Ask for an extension */
07061          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07062          prompt_played++;
07063          if (res || prompt_played > 4)
07064             break;
07065          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07066             break;
07067       }
07068       
07069       /* start all over if no username */
07070       if (ast_strlen_zero(username))
07071          continue;
07072       stringp = username;
07073       s = strsep(&stringp, "*");
07074       /* start optimistic */
07075       valid_extensions = 1;
07076       while (s) {
07077          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07078             int oldmsgs;
07079             int newmsgs;
07080             int capacity;
07081             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07082                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07083                /* Shouldn't happen, but allow trying another extension if it does */
07084                res = ast_play_and_wait(chan, "pbx-invalid");
07085                valid_extensions = 0;
07086                break;
07087             }
07088             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07089             if ((newmsgs + oldmsgs) >= capacity) {
07090                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07091                res = ast_play_and_wait(chan, "vm-mailboxfull");
07092                valid_extensions = 0;
07093                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07094                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07095                   free_user(vmtmp);
07096                }
07097                inprocess_count(receiver->mailbox, receiver->context, -1);
07098                break;
07099             }
07100             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07101          } else {
07102             /* XXX Optimization for the future.  When we encounter a single bad extension,
07103              * bailing out on all of the extensions may not be the way to go.  We should
07104              * probably just bail on that single extension, then allow the user to enter
07105              * several more. XXX
07106              */
07107             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07108                free_user(receiver);
07109             }
07110             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07111             /* "I am sorry, that's not a valid extension.  Please try again." */
07112             res = ast_play_and_wait(chan, "pbx-invalid");
07113             valid_extensions = 0;
07114             break;
07115          }
07116 
07117          /* play name if available, else play extension number */
07118          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07119          RETRIEVE(fn, -1, s, receiver->context);
07120          if (ast_fileexists(fn, NULL, NULL) > 0) {
07121             res = ast_stream_and_wait(chan, fn, ecodes);
07122             if (res) {
07123                DISPOSE(fn, -1);
07124                return res;
07125             }
07126          } else {
07127             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07128          }
07129          DISPOSE(fn, -1);
07130 
07131          s = strsep(&stringp, "*");
07132       }
07133       /* break from the loop of reading the extensions */
07134       if (valid_extensions)
07135          break;
07136    }
07137    /* check if we're clear to proceed */
07138    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07139       return res;
07140    if (is_new_message == 1) {
07141       struct leave_vm_options leave_options;
07142       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07143       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07144 
07145       /* Send VoiceMail */
07146       memset(&leave_options, 0, sizeof(leave_options));
07147       leave_options.record_gain = record_gain;
07148       cmd = leave_voicemail(chan, mailbox, &leave_options);
07149    } else {
07150       /* Forward VoiceMail */
07151       long duration = 0;
07152       struct vm_state vmstmp;
07153       int copy_msg_result = 0;
07154       memcpy(&vmstmp, vms, sizeof(vmstmp));
07155 
07156       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07157 
07158       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07159       if (!cmd) {
07160          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07161 #ifdef IMAP_STORAGE
07162             int attach_user_voicemail;
07163             char *myserveremail = serveremail;
07164             
07165             /* get destination mailbox */
07166             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07167             if (!dstvms) {
07168                dstvms = create_vm_state_from_user(vmtmp);
07169             }
07170             if (dstvms) {
07171                init_mailstream(dstvms, 0);
07172                if (!dstvms->mailstream) {
07173                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07174                } else {
07175                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07176                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07177                }
07178             } else {
07179                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07180             }
07181             if (!ast_strlen_zero(vmtmp->serveremail))
07182                myserveremail = vmtmp->serveremail;
07183             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07184             /* NULL category for IMAP storage */
07185             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07186                dstvms->curbox,
07187                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07188                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07189                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07190                NULL, urgent_str);
07191 #else
07192             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07193 #endif
07194             saved_messages++;
07195             AST_LIST_REMOVE_CURRENT(list);
07196             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07197             free_user(vmtmp);
07198             if (res)
07199                break;
07200          }
07201          AST_LIST_TRAVERSE_SAFE_END;
07202          if (saved_messages > 0 && !copy_msg_result) {
07203             /* give confirmation that the message was saved */
07204             /* commented out since we can't forward batches yet
07205             if (saved_messages == 1)
07206                res = ast_play_and_wait(chan, "vm-message");
07207             else
07208                res = ast_play_and_wait(chan, "vm-messages");
07209             if (!res)
07210                res = ast_play_and_wait(chan, "vm-saved"); */
07211 #ifdef IMAP_STORAGE
07212             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07213             if (ast_strlen_zero(vmstmp.introfn))
07214 #endif
07215             res = ast_play_and_wait(chan, "vm-msgsaved");
07216          }
07217 #ifndef IMAP_STORAGE
07218          else {
07219             /* with IMAP, mailbox full warning played by imap_check_limits */
07220             res = ast_play_and_wait(chan, "vm-mailboxfull");
07221          }
07222          /* Restore original message without prepended message if backup exists */
07223          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07224          strcpy(textfile, msgfile);
07225          strcpy(backup, msgfile);
07226          strcpy(backup_textfile, msgfile);
07227          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07228          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07229          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07230          if (ast_fileexists(backup, NULL, NULL) > 0) {
07231             ast_filerename(backup, msgfile, NULL);
07232             rename(backup_textfile, textfile);
07233          }
07234 #endif
07235       }
07236       DISPOSE(dir, curmsg);
07237 #ifndef IMAP_STORAGE
07238       if (cmd) { /* assuming hangup, cleanup backup file */
07239          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07240          strcpy(textfile, msgfile);
07241          strcpy(backup_textfile, msgfile);
07242          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07243          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07244          rename(backup_textfile, textfile);
07245       }
07246 #endif
07247    }
07248 
07249    /* If anything failed above, we still have this list to free */
07250    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07251       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07252       free_user(vmtmp);
07253    }
07254    return res ? res : cmd;
07255 }
07256 
07257 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07258 {
07259    int res;
07260    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07261       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07262    return res;
07263 }
07264 
07265 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07266 {
07267    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);
07268 }
07269 
07270 static int play_message_category(struct ast_channel *chan, const char *category)
07271 {
07272    int res = 0;
07273 
07274    if (!ast_strlen_zero(category))
07275       res = ast_play_and_wait(chan, category);
07276 
07277    if (res) {
07278       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07279       res = 0;
07280    }
07281 
07282    return res;
07283 }
07284 
07285 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07286 {
07287    int res = 0;
07288    struct vm_zone *the_zone = NULL;
07289    time_t t;
07290 
07291    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07292       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07293       return 0;
07294    }
07295 
07296    /* Does this user have a timezone specified? */
07297    if (!ast_strlen_zero(vmu->zonetag)) {
07298       /* Find the zone in the list */
07299       struct vm_zone *z;
07300       AST_LIST_LOCK(&zones);
07301       AST_LIST_TRAVERSE(&zones, z, list) {
07302          if (!strcmp(z->name, vmu->zonetag)) {
07303             the_zone = z;
07304             break;
07305          }
07306       }
07307       AST_LIST_UNLOCK(&zones);
07308    }
07309 
07310 /* No internal variable parsing for now, so we'll comment it out for the time being */
07311 #if 0
07312    /* Set the DIFF_* variables */
07313    ast_localtime(&t, &time_now, NULL);
07314    tv_now = ast_tvnow();
07315    ast_localtime(&tv_now, &time_then, NULL);
07316 
07317    /* Day difference */
07318    if (time_now.tm_year == time_then.tm_year)
07319       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07320    else
07321       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07322    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07323 
07324    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07325 #endif
07326    if (the_zone) {
07327       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07328    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07329       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07330    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07331       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07332    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07333       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);
07334    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07335       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07336    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07337       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07338    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07339       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07340    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07341       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);
07342    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07343       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07344    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07345       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07346    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07347       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);
07348    } else {
07349       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07350    }
07351 #if 0
07352    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07353 #endif
07354    return res;
07355 }
07356 
07357 
07358 
07359 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07360 {
07361    int res = 0;
07362    int i;
07363    char *callerid, *name;
07364    char prefile[PATH_MAX] = "";
07365    
07366 
07367    /* If voicemail cid is not enabled, or we didn't get cid or context from
07368     * the attribute file, leave now.
07369     *
07370     * TODO Still need to change this so that if this function is called by the
07371     * message envelope (and someone is explicitly requesting to hear the CID),
07372     * it does not check to see if CID is enabled in the config file.
07373     */
07374    if ((cid == NULL)||(context == NULL))
07375       return res;
07376 
07377    /* Strip off caller ID number from name */
07378    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07379    ast_callerid_parse(cid, &name, &callerid);
07380    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07381       /* Check for internal contexts and only */
07382       /* say extension when the call didn't come from an internal context in the list */
07383       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07384          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07385          if ((strcmp(cidinternalcontexts[i], context) == 0))
07386             break;
07387       }
07388       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07389          if (!res) {
07390             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07391             if (!ast_strlen_zero(prefile)) {
07392             /* See if we can find a recorded name for this person instead of their extension number */
07393                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07394                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07395                   if (!callback)
07396                      res = wait_file2(chan, vms, "vm-from");
07397                   res = ast_stream_and_wait(chan, prefile, "");
07398                } else {
07399                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07400                   /* Say "from extension" as one saying to sound smoother */
07401                   if (!callback)
07402                      res = wait_file2(chan, vms, "vm-from-extension");
07403                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07404                }
07405             }
07406          }
07407       } else if (!res) {
07408          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07409          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07410          if (!callback)
07411             res = wait_file2(chan, vms, "vm-from-phonenumber");
07412          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07413       }
07414    } else {
07415       /* Number unknown */
07416       ast_debug(1, "VM-CID: From an unknown number\n");
07417       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07418       res = wait_file2(chan, vms, "vm-unknown-caller");
07419    }
07420    return res;
07421 }
07422 
07423 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07424 {
07425    int res = 0;
07426    int durationm;
07427    int durations;
07428    /* Verify that we have a duration for the message */
07429    if (duration == NULL)
07430       return res;
07431 
07432    /* Convert from seconds to minutes */
07433    durations = atoi(duration);
07434    durationm = (durations / 60);
07435 
07436    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07437 
07438    if ((!res) && (durationm >= minduration)) {
07439       res = wait_file2(chan, vms, "vm-duration");
07440 
07441       /* POLISH syntax */
07442       if (!strncasecmp(chan->language, "pl", 2)) {
07443          div_t num = div(durationm, 10);
07444 
07445          if (durationm == 1) {
07446             res = ast_play_and_wait(chan, "digits/1z");
07447             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07448          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07449             if (num.rem == 2) {
07450                if (!num.quot) {
07451                   res = ast_play_and_wait(chan, "digits/2-ie");
07452                } else {
07453                   res = say_and_wait(chan, durationm - 2 , chan->language);
07454                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07455                }
07456             } else {
07457                res = say_and_wait(chan, durationm, chan->language);
07458             }
07459             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07460          } else {
07461             res = say_and_wait(chan, durationm, chan->language);
07462             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07463          }
07464       /* DEFAULT syntax */
07465       } else {
07466          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07467          res = wait_file2(chan, vms, "vm-minutes");
07468       }
07469    }
07470    return res;
07471 }
07472 
07473 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07474 {
07475    int res = 0;
07476    char filename[256], *cid;
07477    const char *origtime, *context, *category, *duration, *flag;
07478    struct ast_config *msg_cfg;
07479    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07480 
07481    vms->starting = 0;
07482    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07483    adsi_message(chan, vms);
07484    if (!vms->curmsg)
07485       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07486    else if (vms->curmsg == vms->lastmsg)
07487       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07488 
07489    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07490    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07491    msg_cfg = ast_config_load(filename, config_flags);
07492    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07493       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07494       return 0;
07495    }
07496    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07497 
07498    /* Play the word urgent if we are listening to urgent messages */
07499    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07500       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07501    }
07502 
07503    if (!res) {
07504       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07505       /* POLISH syntax */
07506       if (!strncasecmp(chan->language, "pl", 2)) {
07507          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07508             int ten, one;
07509             char nextmsg[256];
07510             ten = (vms->curmsg + 1) / 10;
07511             one = (vms->curmsg + 1) % 10;
07512 
07513             if (vms->curmsg < 20) {
07514                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07515                res = wait_file2(chan, vms, nextmsg);
07516             } else {
07517                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07518                res = wait_file2(chan, vms, nextmsg);
07519                if (one > 0) {
07520                   if (!res) {
07521                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07522                      res = wait_file2(chan, vms, nextmsg);
07523                   }
07524                }
07525             }
07526          }
07527          if (!res)
07528             res = wait_file2(chan, vms, "vm-message");
07529       /* HEBREW syntax */
07530       } else if (!strncasecmp(chan->language, "he", 2)) {
07531          if (!vms->curmsg) {
07532             res = wait_file2(chan, vms, "vm-message");
07533             res = wait_file2(chan, vms, "vm-first");
07534          } else if (vms->curmsg == vms->lastmsg) {
07535             res = wait_file2(chan, vms, "vm-message");
07536             res = wait_file2(chan, vms, "vm-last");
07537          } else {
07538             res = wait_file2(chan, vms, "vm-message");
07539             res = wait_file2(chan, vms, "vm-number");
07540             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07541          }
07542       /* VIETNAMESE syntax */
07543       } else if (!strncasecmp(chan->language, "vi", 2)) {
07544          if (!vms->curmsg) {
07545             res = wait_file2(chan, vms, "vm-message");
07546             res = wait_file2(chan, vms, "vm-first");
07547          } else if (vms->curmsg == vms->lastmsg) {
07548             res = wait_file2(chan, vms, "vm-message");
07549             res = wait_file2(chan, vms, "vm-last");
07550          } else {
07551             res = wait_file2(chan, vms, "vm-message");
07552             res = wait_file2(chan, vms, "vm-number");
07553             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07554          }
07555       } else {
07556          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07557             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07558          } else { /* DEFAULT syntax */
07559             res = wait_file2(chan, vms, "vm-message");
07560          }
07561          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07562             if (!res) {
07563                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07564             }
07565          }
07566       }
07567    }
07568 
07569    if (!msg_cfg) {
07570       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07571       return 0;
07572    }
07573 
07574    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07575       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07576       DISPOSE(vms->curdir, vms->curmsg);
07577       ast_config_destroy(msg_cfg);
07578       return 0;
07579    }
07580 
07581    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07582    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07583    category = ast_variable_retrieve(msg_cfg, "message", "category");
07584 
07585    context = ast_variable_retrieve(msg_cfg, "message", "context");
07586    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07587       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07588    if (!res) {
07589       res = play_message_category(chan, category);
07590    }
07591    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07592       res = play_message_datetime(chan, vmu, origtime, filename);
07593    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07594       res = play_message_callerid(chan, vms, cid, context, 0);
07595    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07596       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07597    /* Allow pressing '1' to skip envelope / callerid */
07598    if (res == '1')
07599       res = 0;
07600    ast_config_destroy(msg_cfg);
07601 
07602    if (!res) {
07603       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07604       vms->heard[vms->curmsg] = 1;
07605 #ifdef IMAP_STORAGE
07606       /*IMAP storage stores any prepended message from a forward
07607        * as a separate file from the rest of the message
07608        */
07609       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07610          wait_file(chan, vms, vms->introfn);
07611       }
07612 #endif
07613       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07614          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07615          res = 0;
07616       }
07617    }
07618    DISPOSE(vms->curdir, vms->curmsg);
07619    return res;
07620 }
07621 
07622 #ifdef IMAP_STORAGE
07623 static int imap_remove_file(char *dir, int msgnum)
07624 {
07625    char fn[PATH_MAX];
07626    char full_fn[PATH_MAX];
07627    char intro[PATH_MAX] = {0,};
07628    
07629    if (msgnum > -1) {
07630       make_file(fn, sizeof(fn), dir, msgnum);
07631       snprintf(intro, sizeof(intro), "%sintro", fn);
07632    } else
07633       ast_copy_string(fn, dir, sizeof(fn));
07634    
07635    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07636       ast_filedelete(fn, NULL);
07637       if (!ast_strlen_zero(intro)) {
07638          ast_filedelete(intro, NULL);
07639       }
07640       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07641       unlink(full_fn);
07642    }
07643    return 0;
07644 }
07645 
07646 
07647 
07648 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07649 {
07650    char *file, *filename;
07651    char *attachment;
07652    char arg[10];
07653    int i;
07654    BODY* body;
07655 
07656    file = strrchr(ast_strdupa(dir), '/');
07657    if (file) {
07658       *file++ = '\0';
07659    } else {
07660       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07661       return -1;
07662    }
07663 
07664    ast_mutex_lock(&vms->lock);
07665    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07666       mail_fetchstructure(vms->mailstream, i + 1, &body);
07667       /* We have the body, now we extract the file name of the first attachment. */
07668       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07669          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07670       } else {
07671          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07672          ast_mutex_unlock(&vms->lock);
07673          return -1;
07674       }
07675       filename = strsep(&attachment, ".");
07676       if (!strcmp(filename, file)) {
07677          sprintf(arg, "%d", i + 1);
07678          mail_setflag(vms->mailstream, arg, "\\DELETED");
07679       }
07680    }
07681    mail_expunge(vms->mailstream);
07682    ast_mutex_unlock(&vms->lock);
07683    return 0;
07684 }
07685 
07686 #elif !defined(IMAP_STORAGE)
07687 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07688 {
07689    int count_msg, last_msg;
07690 
07691    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07692 
07693    /* Rename the member vmbox HERE so that we don't try to return before
07694     * we know what's going on.
07695     */
07696    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07697 
07698    /* Faster to make the directory than to check if it exists. */
07699    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07700 
07701    /* traverses directory using readdir (or select query for ODBC) */
07702    count_msg = count_messages(vmu, vms->curdir);
07703    if (count_msg < 0) {
07704       return count_msg;
07705    } else {
07706       vms->lastmsg = count_msg - 1;
07707    }
07708 
07709    if (vm_allocate_dh(vms, vmu, count_msg)) {
07710       return -1;
07711    }
07712 
07713    /*
07714    The following test is needed in case sequencing gets messed up.
07715    There appears to be more than one way to mess up sequence, so
07716    we will not try to find all of the root causes--just fix it when
07717    detected.
07718    */
07719 
07720    if (vm_lock_path(vms->curdir)) {
07721       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07722       return ERROR_LOCK_PATH;
07723    }
07724 
07725    /* for local storage, checks directory for messages up to maxmsg limit */
07726    last_msg = last_message_index(vmu, vms->curdir);
07727    ast_unlock_path(vms->curdir);
07728 
07729    if (last_msg < -1) {
07730       return last_msg;
07731    } else if (vms->lastmsg != last_msg) {
07732       ast_log(LOG_NOTICE, "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);
07733    }
07734 
07735    return 0;
07736 }
07737 #endif
07738 
07739 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07740 {
07741    int x = 0;
07742 #ifndef IMAP_STORAGE
07743    int res = 0, nummsg;
07744    char fn2[PATH_MAX];
07745 #endif
07746 
07747    if (vms->lastmsg <= -1) {
07748       goto done;
07749    }
07750 
07751    vms->curmsg = -1;
07752 #ifndef IMAP_STORAGE
07753    /* Get the deleted messages fixed */
07754    if (vm_lock_path(vms->curdir)) {
07755       return ERROR_LOCK_PATH;
07756    }
07757 
07758    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07759    for (x = 0; x < vms->lastmsg + 1; x++) {
07760       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07761          /* Save this message.  It's not in INBOX or hasn't been heard */
07762          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07763          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07764             break;
07765          }
07766          vms->curmsg++;
07767          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07768          if (strcmp(vms->fn, fn2)) {
07769             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07770          }
07771       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07772          /* Move to old folder before deleting */
07773          res = save_to_folder(vmu, vms, x, 1);
07774          if (res == ERROR_LOCK_PATH) {
07775             /* If save failed do not delete the message */
07776             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07777             vms->deleted[x] = 0;
07778             vms->heard[x] = 0;
07779             --x;
07780          }
07781       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07782          /* Move to deleted folder */
07783          res = save_to_folder(vmu, vms, x, 10);
07784          if (res == ERROR_LOCK_PATH) {
07785             /* If save failed do not delete the message */
07786             vms->deleted[x] = 0;
07787             vms->heard[x] = 0;
07788             --x;
07789          }
07790       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07791          /* If realtime storage enabled - we should explicitly delete this message,
07792          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07793          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07794          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07795             DELETE(vms->curdir, x, vms->fn, vmu);
07796          }
07797       }
07798    }
07799 
07800    /* Delete ALL remaining messages */
07801    nummsg = x - 1;
07802    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07803       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07804       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07805          DELETE(vms->curdir, x, vms->fn, vmu);
07806       }
07807    }
07808    ast_unlock_path(vms->curdir);
07809 #else /* defined(IMAP_STORAGE) */
07810    if (vms->deleted) {
07811       /* Since we now expunge after each delete, deleting in reverse order
07812        * ensures that no reordering occurs between each step. */
07813       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07814          if (vms->deleted[x]) {
07815             ast_debug(3, "IMAP delete of %d\n", x);
07816             DELETE(vms->curdir, x, vms->fn, vmu);
07817          }
07818       }
07819    }
07820 #endif
07821 
07822 done:
07823    if (vms->deleted && vmu->maxmsg) {
07824       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07825    }
07826    if (vms->heard && vmu->maxmsg) {
07827       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07828    }
07829 
07830    return 0;
07831 }
07832 
07833 /* In Greek even though we CAN use a syntax like "friends messages"
07834  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07835  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07836  * syntax for the above three categories which is more elegant.
07837  */
07838 
07839 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07840 {
07841    int cmd;
07842    char *buf;
07843 
07844    buf = alloca(strlen(box) + 2);
07845    strcpy(buf, box);
07846    strcat(buf, "s");
07847 
07848    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07849       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07850       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07851    } else {
07852       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07853       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07854    }
07855 }
07856 
07857 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07858 {
07859    int cmd;
07860 
07861    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07862       if (!strcasecmp(box, "vm-INBOX"))
07863          cmd = ast_play_and_wait(chan, "vm-new-e");
07864       else
07865          cmd = ast_play_and_wait(chan, "vm-old-e");
07866       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07867    } else {
07868       cmd = ast_play_and_wait(chan, "vm-messages");
07869       return cmd ? cmd : ast_play_and_wait(chan, box);
07870    }
07871 }
07872 
07873 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07874 {
07875    int cmd;
07876 
07877    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07878       cmd = ast_play_and_wait(chan, "vm-messages");
07879       return cmd ? cmd : ast_play_and_wait(chan, box);
07880    } else {
07881       cmd = ast_play_and_wait(chan, box);
07882       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07883    }
07884 }
07885 
07886 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07887 {
07888    int cmd;
07889 
07890    if (  !strncasecmp(chan->language, "it", 2) ||
07891         !strncasecmp(chan->language, "es", 2) ||
07892         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07893       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07894       return cmd ? cmd : ast_play_and_wait(chan, box);
07895    } else if (!strncasecmp(chan->language, "gr", 2)) {
07896       return vm_play_folder_name_gr(chan, box);
07897    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07898       return ast_play_and_wait(chan, box);
07899    } else if (!strncasecmp(chan->language, "pl", 2)) {
07900       return vm_play_folder_name_pl(chan, box);
07901    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07902       return vm_play_folder_name_ua(chan, box);
07903    } else if (!strncasecmp(chan->language, "vi", 2)) {
07904       return ast_play_and_wait(chan, box);
07905    } else {  /* Default English */
07906       cmd = ast_play_and_wait(chan, box);
07907       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07908    }
07909 }
07910 
07911 /* GREEK SYNTAX
07912    In greek the plural for old/new is
07913    different so we need the following files
07914    We also need vm-denExeteMynhmata because
07915    this syntax is different.
07916 
07917    -> vm-Olds.wav : "Palia"
07918    -> vm-INBOXs.wav : "Nea"
07919    -> vm-denExeteMynhmata : "den exete mynhmata"
07920 */
07921 
07922 
07923 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07924 {
07925    int res = 0;
07926 
07927    if (vms->newmessages) {
07928       res = ast_play_and_wait(chan, "vm-youhave");
07929       if (!res) 
07930          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07931       if (!res) {
07932          if ((vms->newmessages == 1)) {
07933             res = ast_play_and_wait(chan, "vm-INBOX");
07934             if (!res)
07935                res = ast_play_and_wait(chan, "vm-message");
07936          } else {
07937             res = ast_play_and_wait(chan, "vm-INBOXs");
07938             if (!res)
07939                res = ast_play_and_wait(chan, "vm-messages");
07940          }
07941       }
07942    } else if (vms->oldmessages){
07943       res = ast_play_and_wait(chan, "vm-youhave");
07944       if (!res)
07945          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07946       if ((vms->oldmessages == 1)){
07947          res = ast_play_and_wait(chan, "vm-Old");
07948          if (!res)
07949             res = ast_play_and_wait(chan, "vm-message");
07950       } else {
07951          res = ast_play_and_wait(chan, "vm-Olds");
07952          if (!res)
07953             res = ast_play_and_wait(chan, "vm-messages");
07954       }
07955    } else if (!vms->oldmessages && !vms->newmessages) 
07956       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07957    return res;
07958 }
07959 
07960 /* Version of vm_intro() designed to work for many languages.
07961  *
07962  * It is hoped that this function can prevent the proliferation of 
07963  * language-specific vm_intro() functions and in time replace the language-
07964  * specific functions which already exist.  An examination of the language-
07965  * specific functions revealed that they all corrected the same deficiencies
07966  * in vm_intro_en() (which was the default function). Namely:
07967  *
07968  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07969  *     wording of the voicemail greeting hides this problem.  For example,
07970  *     vm-INBOX contains only the word "new".  This means that both of these
07971  *     sequences produce valid utterances:
07972  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07973  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07974  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07975  *     in many languages) the first utterance becomes "you have 1 the new message".
07976  *  2) The function contains hardcoded rules for pluralizing the word "message".
07977  *     These rules are correct for English, but not for many other languages.
07978  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07979  *     required in many languages.
07980  *  4) The gender of the word for "message" is not specified. This is a problem
07981  *     because in many languages the gender of the number in phrases such
07982  *     as "you have one new message" must match the gender of the word
07983  *     meaning "message".
07984  *
07985  * Fixing these problems for each new language has meant duplication of effort.
07986  * This new function solves the problems in the following general ways:
07987  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07988  *     and vm-Old respectively for those languages where it makes sense.
07989  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07990  *     on vm-message.
07991  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07992  *     prefix on vm-new and vm-old (none for English).
07993  *  4) Pass the gender of the language's word for "message" as an agument to
07994  *     this function which is can in turn pass on to the functions which 
07995  *     say numbers and put endings on nounds and adjectives.
07996  *
07997  * All languages require these messages:
07998  *  vm-youhave    "You have..."
07999  *  vm-and     "and"
08000  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08001  *
08002  * To use it for English, you will need these additional sound files:
08003  *  vm-new     "new"
08004  *  vm-message    "message", singular
08005  *  vm-messages      "messages", plural
08006  *
08007  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08008  *
08009  *  vm-newn    "novoye" (singular, neuter)
08010  *  vm-newx    "novikh" (counting plural form, genative plural)
08011  *  vm-message    "sobsheniye" (singular form)
08012  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08013  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08014  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08015  *  digits/2n     "dva" (neuter singular)
08016  */
08017 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08018 {
08019    int res;
08020    int lastnum = 0;
08021 
08022    res = ast_play_and_wait(chan, "vm-youhave");
08023 
08024    if (!res && vms->newmessages) {
08025       lastnum = vms->newmessages;
08026 
08027       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08028          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08029       }
08030 
08031       if (!res && vms->oldmessages) {
08032          res = ast_play_and_wait(chan, "vm-and");
08033       }
08034    }
08035 
08036    if (!res && vms->oldmessages) {
08037       lastnum = vms->oldmessages;
08038 
08039       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08040          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08041       }
08042    }
08043 
08044    if (!res) {
08045       if (lastnum == 0) {
08046          res = ast_play_and_wait(chan, "vm-no");
08047       }
08048       if (!res) {
08049          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08050       }
08051    }
08052 
08053    return res;
08054 }
08055 
08056 /* Default Hebrew syntax */
08057 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08058 {
08059    int res = 0;
08060 
08061    /* Introduce messages they have */
08062    if (!res) {
08063       if ((vms->newmessages) || (vms->oldmessages)) {
08064          res = ast_play_and_wait(chan, "vm-youhave");
08065       }
08066       /*
08067        * The word "shtei" refers to the number 2 in hebrew when performing a count
08068        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08069        * an element, this is one of them.
08070        */
08071       if (vms->newmessages) {
08072          if (!res) {
08073             if (vms->newmessages == 1) {
08074                res = ast_play_and_wait(chan, "vm-INBOX1");
08075             } else {
08076                if (vms->newmessages == 2) {
08077                   res = ast_play_and_wait(chan, "vm-shtei");
08078                } else {
08079                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08080                }
08081                res = ast_play_and_wait(chan, "vm-INBOX");
08082             }
08083          }
08084          if (vms->oldmessages && !res) {
08085             res = ast_play_and_wait(chan, "vm-and");
08086             if (vms->oldmessages == 1) {
08087                res = ast_play_and_wait(chan, "vm-Old1");
08088             } else {
08089                if (vms->oldmessages == 2) {
08090                   res = ast_play_and_wait(chan, "vm-shtei");
08091                } else {
08092                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08093                }
08094                res = ast_play_and_wait(chan, "vm-Old");
08095             }
08096          }
08097       }
08098       if (!res && vms->oldmessages && !vms->newmessages) {
08099          if (!res) {
08100             if (vms->oldmessages == 1) {
08101                res = ast_play_and_wait(chan, "vm-Old1");
08102             } else {
08103                if (vms->oldmessages == 2) {
08104                   res = ast_play_and_wait(chan, "vm-shtei");
08105                } else {
08106                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08107                }
08108                res = ast_play_and_wait(chan, "vm-Old");
08109             }
08110          }
08111       }
08112       if (!res) {
08113          if (!vms->oldmessages && !vms->newmessages) {
08114             if (!res) {
08115                res = ast_play_and_wait(chan, "vm-nomessages");
08116             }
08117          }
08118       }
08119    }
08120    return res;
08121 }
08122    
08123 /* Default English syntax */
08124 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08125 {
08126    int res;
08127 
08128    /* Introduce messages they have */
08129    res = ast_play_and_wait(chan, "vm-youhave");
08130    if (!res) {
08131       if (vms->urgentmessages) {
08132          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08133          if (!res)
08134             res = ast_play_and_wait(chan, "vm-Urgent");
08135          if ((vms->oldmessages || vms->newmessages) && !res) {
08136             res = ast_play_and_wait(chan, "vm-and");
08137          } else if (!res) {
08138             if ((vms->urgentmessages == 1))
08139                res = ast_play_and_wait(chan, "vm-message");
08140             else
08141                res = ast_play_and_wait(chan, "vm-messages");
08142          }
08143       }
08144       if (vms->newmessages) {
08145          res = say_and_wait(chan, vms->newmessages, chan->language);
08146          if (!res)
08147             res = ast_play_and_wait(chan, "vm-INBOX");
08148          if (vms->oldmessages && !res)
08149             res = ast_play_and_wait(chan, "vm-and");
08150          else if (!res) {
08151             if ((vms->newmessages == 1))
08152                res = ast_play_and_wait(chan, "vm-message");
08153             else
08154                res = ast_play_and_wait(chan, "vm-messages");
08155          }
08156             
08157       }
08158       if (!res && vms->oldmessages) {
08159          res = say_and_wait(chan, vms->oldmessages, chan->language);
08160          if (!res)
08161             res = ast_play_and_wait(chan, "vm-Old");
08162          if (!res) {
08163             if (vms->oldmessages == 1)
08164                res = ast_play_and_wait(chan, "vm-message");
08165             else
08166                res = ast_play_and_wait(chan, "vm-messages");
08167          }
08168       }
08169       if (!res) {
08170          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08171             res = ast_play_and_wait(chan, "vm-no");
08172             if (!res)
08173                res = ast_play_and_wait(chan, "vm-messages");
08174          }
08175       }
08176    }
08177    return res;
08178 }
08179 
08180 /* ITALIAN syntax */
08181 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08182 {
08183    /* Introduce messages they have */
08184    int res;
08185    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08186       res = ast_play_and_wait(chan, "vm-no") ||
08187          ast_play_and_wait(chan, "vm-message");
08188    else
08189       res = ast_play_and_wait(chan, "vm-youhave");
08190    if (!res && vms->newmessages) {
08191       res = (vms->newmessages == 1) ?
08192          ast_play_and_wait(chan, "digits/un") ||
08193          ast_play_and_wait(chan, "vm-nuovo") ||
08194          ast_play_and_wait(chan, "vm-message") :
08195          /* 2 or more new messages */
08196          say_and_wait(chan, vms->newmessages, chan->language) ||
08197          ast_play_and_wait(chan, "vm-nuovi") ||
08198          ast_play_and_wait(chan, "vm-messages");
08199       if (!res && vms->oldmessages)
08200          res = ast_play_and_wait(chan, "vm-and");
08201    }
08202    if (!res && vms->oldmessages) {
08203       res = (vms->oldmessages == 1) ?
08204          ast_play_and_wait(chan, "digits/un") ||
08205          ast_play_and_wait(chan, "vm-vecchio") ||
08206          ast_play_and_wait(chan, "vm-message") :
08207          /* 2 or more old messages */
08208          say_and_wait(chan, vms->oldmessages, chan->language) ||
08209          ast_play_and_wait(chan, "vm-vecchi") ||
08210          ast_play_and_wait(chan, "vm-messages");
08211    }
08212    return res;
08213 }
08214 
08215 /* POLISH syntax */
08216 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08217 {
08218    /* Introduce messages they have */
08219    int res;
08220    div_t num;
08221 
08222    if (!vms->oldmessages && !vms->newmessages) {
08223       res = ast_play_and_wait(chan, "vm-no");
08224       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08225       return res;
08226    } else {
08227       res = ast_play_and_wait(chan, "vm-youhave");
08228    }
08229 
08230    if (vms->newmessages) {
08231       num = div(vms->newmessages, 10);
08232       if (vms->newmessages == 1) {
08233          res = ast_play_and_wait(chan, "digits/1-a");
08234          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08235          res = res ? res : ast_play_and_wait(chan, "vm-message");
08236       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08237          if (num.rem == 2) {
08238             if (!num.quot) {
08239                res = ast_play_and_wait(chan, "digits/2-ie");
08240             } else {
08241                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08242                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08243             }
08244          } else {
08245             res = say_and_wait(chan, vms->newmessages, chan->language);
08246          }
08247          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08248          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08249       } else {
08250          res = say_and_wait(chan, vms->newmessages, chan->language);
08251          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08252          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08253       }
08254       if (!res && vms->oldmessages)
08255          res = ast_play_and_wait(chan, "vm-and");
08256    }
08257    if (!res && vms->oldmessages) {
08258       num = div(vms->oldmessages, 10);
08259       if (vms->oldmessages == 1) {
08260          res = ast_play_and_wait(chan, "digits/1-a");
08261          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08262          res = res ? res : ast_play_and_wait(chan, "vm-message");
08263       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08264          if (num.rem == 2) {
08265             if (!num.quot) {
08266                res = ast_play_and_wait(chan, "digits/2-ie");
08267             } else {
08268                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08269                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08270             }
08271          } else {
08272             res = say_and_wait(chan, vms->oldmessages, chan->language);
08273          }
08274          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08275          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08276       } else {
08277          res = say_and_wait(chan, vms->oldmessages, chan->language);
08278          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08279          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08280       }
08281    }
08282 
08283    return res;
08284 }
08285 
08286 /* SWEDISH syntax */
08287 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08288 {
08289    /* Introduce messages they have */
08290    int res;
08291 
08292    res = ast_play_and_wait(chan, "vm-youhave");
08293    if (res)
08294       return res;
08295 
08296    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08297       res = ast_play_and_wait(chan, "vm-no");
08298       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08299       return res;
08300    }
08301 
08302    if (vms->newmessages) {
08303       if ((vms->newmessages == 1)) {
08304          res = ast_play_and_wait(chan, "digits/ett");
08305          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08306          res = res ? res : ast_play_and_wait(chan, "vm-message");
08307       } else {
08308          res = say_and_wait(chan, vms->newmessages, chan->language);
08309          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08310          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08311       }
08312       if (!res && vms->oldmessages)
08313          res = ast_play_and_wait(chan, "vm-and");
08314    }
08315    if (!res && vms->oldmessages) {
08316       if (vms->oldmessages == 1) {
08317          res = ast_play_and_wait(chan, "digits/ett");
08318          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08319          res = res ? res : ast_play_and_wait(chan, "vm-message");
08320       } else {
08321          res = say_and_wait(chan, vms->oldmessages, chan->language);
08322          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08323          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08324       }
08325    }
08326 
08327    return res;
08328 }
08329 
08330 /* NORWEGIAN syntax */
08331 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08332 {
08333    /* Introduce messages they have */
08334    int res;
08335 
08336    res = ast_play_and_wait(chan, "vm-youhave");
08337    if (res)
08338       return res;
08339 
08340    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08341       res = ast_play_and_wait(chan, "vm-no");
08342       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08343       return res;
08344    }
08345 
08346    if (vms->newmessages) {
08347       if ((vms->newmessages == 1)) {
08348          res = ast_play_and_wait(chan, "digits/1");
08349          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08350          res = res ? res : ast_play_and_wait(chan, "vm-message");
08351       } else {
08352          res = say_and_wait(chan, vms->newmessages, chan->language);
08353          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08354          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08355       }
08356       if (!res && vms->oldmessages)
08357          res = ast_play_and_wait(chan, "vm-and");
08358    }
08359    if (!res && vms->oldmessages) {
08360       if (vms->oldmessages == 1) {
08361          res = ast_play_and_wait(chan, "digits/1");
08362          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08363          res = res ? res : ast_play_and_wait(chan, "vm-message");
08364       } else {
08365          res = say_and_wait(chan, vms->oldmessages, chan->language);
08366          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08367          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08368       }
08369    }
08370 
08371    return res;
08372 }
08373 
08374 /* GERMAN syntax */
08375 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08376 {
08377    /* Introduce messages they have */
08378    int res;
08379    res = ast_play_and_wait(chan, "vm-youhave");
08380    if (!res) {
08381       if (vms->newmessages) {
08382          if ((vms->newmessages == 1))
08383             res = ast_play_and_wait(chan, "digits/1F");
08384          else
08385             res = say_and_wait(chan, vms->newmessages, chan->language);
08386          if (!res)
08387             res = ast_play_and_wait(chan, "vm-INBOX");
08388          if (vms->oldmessages && !res)
08389             res = ast_play_and_wait(chan, "vm-and");
08390          else if (!res) {
08391             if ((vms->newmessages == 1))
08392                res = ast_play_and_wait(chan, "vm-message");
08393             else
08394                res = ast_play_and_wait(chan, "vm-messages");
08395          }
08396             
08397       }
08398       if (!res && vms->oldmessages) {
08399          if (vms->oldmessages == 1)
08400             res = ast_play_and_wait(chan, "digits/1F");
08401          else
08402             res = say_and_wait(chan, vms->oldmessages, chan->language);
08403          if (!res)
08404             res = ast_play_and_wait(chan, "vm-Old");
08405          if (!res) {
08406             if (vms->oldmessages == 1)
08407                res = ast_play_and_wait(chan, "vm-message");
08408             else
08409                res = ast_play_and_wait(chan, "vm-messages");
08410          }
08411       }
08412       if (!res) {
08413          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08414             res = ast_play_and_wait(chan, "vm-no");
08415             if (!res)
08416                res = ast_play_and_wait(chan, "vm-messages");
08417          }
08418       }
08419    }
08420    return res;
08421 }
08422 
08423 /* SPANISH syntax */
08424 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08425 {
08426    /* Introduce messages they have */
08427    int res;
08428    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08429       res = ast_play_and_wait(chan, "vm-youhaveno");
08430       if (!res)
08431          res = ast_play_and_wait(chan, "vm-messages");
08432    } else {
08433       res = ast_play_and_wait(chan, "vm-youhave");
08434    }
08435    if (!res) {
08436       if (vms->newmessages) {
08437          if (!res) {
08438             if ((vms->newmessages == 1)) {
08439                res = ast_play_and_wait(chan, "digits/1M");
08440                if (!res)
08441                   res = ast_play_and_wait(chan, "vm-message");
08442                if (!res)
08443                   res = ast_play_and_wait(chan, "vm-INBOXs");
08444             } else {
08445                res = say_and_wait(chan, vms->newmessages, chan->language);
08446                if (!res)
08447                   res = ast_play_and_wait(chan, "vm-messages");
08448                if (!res)
08449                   res = ast_play_and_wait(chan, "vm-INBOX");
08450             }
08451          }
08452          if (vms->oldmessages && !res)
08453             res = ast_play_and_wait(chan, "vm-and");
08454       }
08455       if (vms->oldmessages) {
08456          if (!res) {
08457             if (vms->oldmessages == 1) {
08458                res = ast_play_and_wait(chan, "digits/1M");
08459                if (!res)
08460                   res = ast_play_and_wait(chan, "vm-message");
08461                if (!res)
08462                   res = ast_play_and_wait(chan, "vm-Olds");
08463             } else {
08464                res = say_and_wait(chan, vms->oldmessages, chan->language);
08465                if (!res)
08466                   res = ast_play_and_wait(chan, "vm-messages");
08467                if (!res)
08468                   res = ast_play_and_wait(chan, "vm-Old");
08469             }
08470          }
08471       }
08472    }
08473 return res;
08474 }
08475 
08476 /* BRAZILIAN PORTUGUESE syntax */
08477 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08478    /* Introduce messages they have */
08479    int res;
08480    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08481       res = ast_play_and_wait(chan, "vm-nomessages");
08482       return res;
08483    } else {
08484       res = ast_play_and_wait(chan, "vm-youhave");
08485    }
08486    if (vms->newmessages) {
08487       if (!res)
08488          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08489       if ((vms->newmessages == 1)) {
08490          if (!res)
08491             res = ast_play_and_wait(chan, "vm-message");
08492          if (!res)
08493             res = ast_play_and_wait(chan, "vm-INBOXs");
08494       } else {
08495          if (!res)
08496             res = ast_play_and_wait(chan, "vm-messages");
08497          if (!res)
08498             res = ast_play_and_wait(chan, "vm-INBOX");
08499       }
08500       if (vms->oldmessages && !res)
08501          res = ast_play_and_wait(chan, "vm-and");
08502    }
08503    if (vms->oldmessages) {
08504       if (!res)
08505          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08506       if (vms->oldmessages == 1) {
08507          if (!res)
08508             res = ast_play_and_wait(chan, "vm-message");
08509          if (!res)
08510             res = ast_play_and_wait(chan, "vm-Olds");
08511       } else {
08512          if (!res)
08513             res = ast_play_and_wait(chan, "vm-messages");
08514          if (!res)
08515             res = ast_play_and_wait(chan, "vm-Old");
08516       }
08517    }
08518    return res;
08519 }
08520 
08521 /* FRENCH syntax */
08522 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08523 {
08524    /* Introduce messages they have */
08525    int res;
08526    res = ast_play_and_wait(chan, "vm-youhave");
08527    if (!res) {
08528       if (vms->newmessages) {
08529          res = say_and_wait(chan, vms->newmessages, chan->language);
08530          if (!res)
08531             res = ast_play_and_wait(chan, "vm-INBOX");
08532          if (vms->oldmessages && !res)
08533             res = ast_play_and_wait(chan, "vm-and");
08534          else if (!res) {
08535             if ((vms->newmessages == 1))
08536                res = ast_play_and_wait(chan, "vm-message");
08537             else
08538                res = ast_play_and_wait(chan, "vm-messages");
08539          }
08540             
08541       }
08542       if (!res && vms->oldmessages) {
08543          res = say_and_wait(chan, vms->oldmessages, chan->language);
08544          if (!res)
08545             res = ast_play_and_wait(chan, "vm-Old");
08546          if (!res) {
08547             if (vms->oldmessages == 1)
08548                res = ast_play_and_wait(chan, "vm-message");
08549             else
08550                res = ast_play_and_wait(chan, "vm-messages");
08551          }
08552       }
08553       if (!res) {
08554          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08555             res = ast_play_and_wait(chan, "vm-no");
08556             if (!res)
08557                res = ast_play_and_wait(chan, "vm-messages");
08558          }
08559       }
08560    }
08561    return res;
08562 }
08563 
08564 /* DUTCH syntax */
08565 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08566 {
08567    /* Introduce messages they have */
08568    int res;
08569    res = ast_play_and_wait(chan, "vm-youhave");
08570    if (!res) {
08571       if (vms->newmessages) {
08572          res = say_and_wait(chan, vms->newmessages, chan->language);
08573          if (!res) {
08574             if (vms->newmessages == 1)
08575                res = ast_play_and_wait(chan, "vm-INBOXs");
08576             else
08577                res = ast_play_and_wait(chan, "vm-INBOX");
08578          }
08579          if (vms->oldmessages && !res)
08580             res = ast_play_and_wait(chan, "vm-and");
08581          else if (!res) {
08582             if ((vms->newmessages == 1))
08583                res = ast_play_and_wait(chan, "vm-message");
08584             else
08585                res = ast_play_and_wait(chan, "vm-messages");
08586          }
08587             
08588       }
08589       if (!res && vms->oldmessages) {
08590          res = say_and_wait(chan, vms->oldmessages, chan->language);
08591          if (!res) {
08592             if (vms->oldmessages == 1)
08593                res = ast_play_and_wait(chan, "vm-Olds");
08594             else
08595                res = ast_play_and_wait(chan, "vm-Old");
08596          }
08597          if (!res) {
08598             if (vms->oldmessages == 1)
08599                res = ast_play_and_wait(chan, "vm-message");
08600             else
08601                res = ast_play_and_wait(chan, "vm-messages");
08602          }
08603       }
08604       if (!res) {
08605          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08606             res = ast_play_and_wait(chan, "vm-no");
08607             if (!res)
08608                res = ast_play_and_wait(chan, "vm-messages");
08609          }
08610       }
08611    }
08612    return res;
08613 }
08614 
08615 /* PORTUGUESE syntax */
08616 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08617 {
08618    /* Introduce messages they have */
08619    int res;
08620    res = ast_play_and_wait(chan, "vm-youhave");
08621    if (!res) {
08622       if (vms->newmessages) {
08623          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08624          if (!res) {
08625             if ((vms->newmessages == 1)) {
08626                res = ast_play_and_wait(chan, "vm-message");
08627                if (!res)
08628                   res = ast_play_and_wait(chan, "vm-INBOXs");
08629             } else {
08630                res = ast_play_and_wait(chan, "vm-messages");
08631                if (!res)
08632                   res = ast_play_and_wait(chan, "vm-INBOX");
08633             }
08634          }
08635          if (vms->oldmessages && !res)
08636             res = ast_play_and_wait(chan, "vm-and");
08637       }
08638       if (!res && vms->oldmessages) {
08639          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08640          if (!res) {
08641             if (vms->oldmessages == 1) {
08642                res = ast_play_and_wait(chan, "vm-message");
08643                if (!res)
08644                   res = ast_play_and_wait(chan, "vm-Olds");
08645             } else {
08646                res = ast_play_and_wait(chan, "vm-messages");
08647                if (!res)
08648                   res = ast_play_and_wait(chan, "vm-Old");
08649             }
08650          }
08651       }
08652       if (!res) {
08653          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08654             res = ast_play_and_wait(chan, "vm-no");
08655             if (!res)
08656                res = ast_play_and_wait(chan, "vm-messages");
08657          }
08658       }
08659    }
08660    return res;
08661 }
08662 
08663 
08664 /* CZECH syntax */
08665 /* in czech there must be declension of word new and message
08666  * czech        : english        : czech      : english
08667  * --------------------------------------------------------
08668  * vm-youhave   : you have 
08669  * vm-novou     : one new        : vm-zpravu  : message
08670  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08671  * vm-novych    : 5-infinite new : vm-zprav   : messages
08672  * vm-starou   : one old
08673  * vm-stare     : 2-4 old 
08674  * vm-starych   : 5-infinite old
08675  * jednu        : one   - falling 4. 
08676  * vm-no        : no  ( no messages )
08677  */
08678 
08679 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08680 {
08681    int res;
08682    res = ast_play_and_wait(chan, "vm-youhave");
08683    if (!res) {
08684       if (vms->newmessages) {
08685          if (vms->newmessages == 1) {
08686             res = ast_play_and_wait(chan, "digits/jednu");
08687          } else {
08688             res = say_and_wait(chan, vms->newmessages, chan->language);
08689          }
08690          if (!res) {
08691             if ((vms->newmessages == 1))
08692                res = ast_play_and_wait(chan, "vm-novou");
08693             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08694                res = ast_play_and_wait(chan, "vm-nove");
08695             if (vms->newmessages > 4)
08696                res = ast_play_and_wait(chan, "vm-novych");
08697          }
08698          if (vms->oldmessages && !res)
08699             res = ast_play_and_wait(chan, "vm-and");
08700          else if (!res) {
08701             if ((vms->newmessages == 1))
08702                res = ast_play_and_wait(chan, "vm-zpravu");
08703             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08704                res = ast_play_and_wait(chan, "vm-zpravy");
08705             if (vms->newmessages > 4)
08706                res = ast_play_and_wait(chan, "vm-zprav");
08707          }
08708       }
08709       if (!res && vms->oldmessages) {
08710          res = say_and_wait(chan, vms->oldmessages, chan->language);
08711          if (!res) {
08712             if ((vms->oldmessages == 1))
08713                res = ast_play_and_wait(chan, "vm-starou");
08714             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08715                res = ast_play_and_wait(chan, "vm-stare");
08716             if (vms->oldmessages > 4)
08717                res = ast_play_and_wait(chan, "vm-starych");
08718          }
08719          if (!res) {
08720             if ((vms->oldmessages == 1))
08721                res = ast_play_and_wait(chan, "vm-zpravu");
08722             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08723                res = ast_play_and_wait(chan, "vm-zpravy");
08724             if (vms->oldmessages > 4)
08725                res = ast_play_and_wait(chan, "vm-zprav");
08726          }
08727       }
08728       if (!res) {
08729          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08730             res = ast_play_and_wait(chan, "vm-no");
08731             if (!res)
08732                res = ast_play_and_wait(chan, "vm-zpravy");
08733          }
08734       }
08735    }
08736    return res;
08737 }
08738 
08739 /* CHINESE (Taiwan) syntax */
08740 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08741 {
08742    int res;
08743    /* Introduce messages they have */
08744    res = ast_play_and_wait(chan, "vm-you");
08745 
08746    if (!res && vms->newmessages) {
08747       res = ast_play_and_wait(chan, "vm-have");
08748       if (!res)
08749          res = say_and_wait(chan, vms->newmessages, chan->language);
08750       if (!res)
08751          res = ast_play_and_wait(chan, "vm-tong");
08752       if (!res)
08753          res = ast_play_and_wait(chan, "vm-INBOX");
08754       if (vms->oldmessages && !res)
08755          res = ast_play_and_wait(chan, "vm-and");
08756       else if (!res) 
08757          res = ast_play_and_wait(chan, "vm-messages");
08758    }
08759    if (!res && vms->oldmessages) {
08760       res = ast_play_and_wait(chan, "vm-have");
08761       if (!res)
08762          res = say_and_wait(chan, vms->oldmessages, chan->language);
08763       if (!res)
08764          res = ast_play_and_wait(chan, "vm-tong");
08765       if (!res)
08766          res = ast_play_and_wait(chan, "vm-Old");
08767       if (!res)
08768          res = ast_play_and_wait(chan, "vm-messages");
08769    }
08770    if (!res && !vms->oldmessages && !vms->newmessages) {
08771       res = ast_play_and_wait(chan, "vm-haveno");
08772       if (!res)
08773          res = ast_play_and_wait(chan, "vm-messages");
08774    }
08775    return res;
08776 }
08777 
08778 /* Vietnamese syntax */
08779 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08780 {
08781    int res;
08782 
08783    /* Introduce messages they have */
08784    res = ast_play_and_wait(chan, "vm-youhave");
08785    if (!res) {
08786       if (vms->newmessages) {
08787          res = say_and_wait(chan, vms->newmessages, chan->language);
08788          if (!res)
08789             res = ast_play_and_wait(chan, "vm-INBOX");
08790          if (vms->oldmessages && !res)
08791             res = ast_play_and_wait(chan, "vm-and");
08792       }
08793       if (!res && vms->oldmessages) {
08794          res = say_and_wait(chan, vms->oldmessages, chan->language);
08795          if (!res)
08796             res = ast_play_and_wait(chan, "vm-Old");        
08797       }
08798       if (!res) {
08799          if (!vms->oldmessages && !vms->newmessages) {
08800             res = ast_play_and_wait(chan, "vm-no");
08801             if (!res)
08802                res = ast_play_and_wait(chan, "vm-message");
08803          }
08804       }
08805    }
08806    return res;
08807 }
08808 
08809 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08810 {
08811    char prefile[256];
08812    
08813    /* Notify the user that the temp greeting is set and give them the option to remove it */
08814    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08815    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08816       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08817       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08818          ast_play_and_wait(chan, "vm-tempgreetactive");
08819       }
08820       DISPOSE(prefile, -1);
08821    }
08822 
08823    /* Play voicemail intro - syntax is different for different languages */
08824    if (0) {
08825       return 0;
08826    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08827       return vm_intro_cs(chan, vms);
08828    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08829       static int deprecation_warning = 0;
08830       if (deprecation_warning++ % 10 == 0) {
08831          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08832       }
08833       return vm_intro_cs(chan, vms);
08834    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08835       return vm_intro_de(chan, vms);
08836    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08837       return vm_intro_es(chan, vms);
08838    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08839       return vm_intro_fr(chan, vms);
08840    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08841       return vm_intro_gr(chan, vms);
08842    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08843       return vm_intro_he(chan, vms);
08844    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08845       return vm_intro_it(chan, vms);
08846    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08847       return vm_intro_nl(chan, vms);
08848    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08849       return vm_intro_no(chan, vms);
08850    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08851       return vm_intro_pl(chan, vms);
08852    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08853       return vm_intro_pt_BR(chan, vms);
08854    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08855       return vm_intro_pt(chan, vms);
08856    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08857       return vm_intro_multilang(chan, vms, "n");
08858    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08859       return vm_intro_se(chan, vms);
08860    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08861       return vm_intro_multilang(chan, vms, "n");
08862    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
08863       return vm_intro_vi(chan, vms);
08864    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08865       return vm_intro_zh(chan, vms);
08866    } else {                                             /* Default to ENGLISH */
08867       return vm_intro_en(chan, vms);
08868    }
08869 }
08870 
08871 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08872 {
08873    int res = 0;
08874    /* Play instructions and wait for new command */
08875    while (!res) {
08876       if (vms->starting) {
08877          if (vms->lastmsg > -1) {
08878             if (skipadvanced)
08879                res = ast_play_and_wait(chan, "vm-onefor-full");
08880             else
08881                res = ast_play_and_wait(chan, "vm-onefor");
08882             if (!res)
08883                res = vm_play_folder_name(chan, vms->vmbox);
08884          }
08885          if (!res) {
08886             if (skipadvanced)
08887                res = ast_play_and_wait(chan, "vm-opts-full");
08888             else
08889                res = ast_play_and_wait(chan, "vm-opts");
08890          }
08891       } else {
08892          /* Added for additional help */
08893          if (skipadvanced) {
08894             res = ast_play_and_wait(chan, "vm-onefor-full");
08895             if (!res)
08896                res = vm_play_folder_name(chan, vms->vmbox);
08897             res = ast_play_and_wait(chan, "vm-opts-full");
08898          }
08899          /* Logic:
08900           * If the current message is not the first OR
08901           * if we're listening to the first new message and there are
08902           * also urgent messages, then prompt for navigation to the
08903           * previous message
08904           */
08905          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08906             res = ast_play_and_wait(chan, "vm-prev");
08907          }
08908          if (!res && !skipadvanced)
08909             res = ast_play_and_wait(chan, "vm-advopts");
08910          if (!res)
08911             res = ast_play_and_wait(chan, "vm-repeat");
08912          /* Logic:
08913           * If we're not listening to the last message OR
08914           * we're listening to the last urgent message and there are
08915           * also new non-urgent messages, then prompt for navigation
08916           * to the next message
08917           */
08918          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08919             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08920             res = ast_play_and_wait(chan, "vm-next");
08921          }
08922          if (!res) {
08923             if (!vms->deleted[vms->curmsg])
08924                res = ast_play_and_wait(chan, "vm-delete");
08925             else
08926                res = ast_play_and_wait(chan, "vm-undelete");
08927             if (!res)
08928                res = ast_play_and_wait(chan, "vm-toforward");
08929             if (!res)
08930                res = ast_play_and_wait(chan, "vm-savemessage");
08931          }
08932       }
08933       if (!res) {
08934          res = ast_play_and_wait(chan, "vm-helpexit");
08935       }
08936       if (!res)
08937          res = ast_waitfordigit(chan, 6000);
08938       if (!res) {
08939          vms->repeats++;
08940          if (vms->repeats > 2) {
08941             res = 't';
08942          }
08943       }
08944    }
08945    return res;
08946 }
08947 
08948 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08949 {
08950    int res = 0;
08951    /* Play instructions and wait for new command */
08952    while (!res) {
08953       if (vms->lastmsg > -1) {
08954          res = ast_play_and_wait(chan, "vm-listen");
08955          if (!res)
08956             res = vm_play_folder_name(chan, vms->vmbox);
08957          if (!res)
08958             res = ast_play_and_wait(chan, "press");
08959          if (!res)
08960             res = ast_play_and_wait(chan, "digits/1");
08961       }
08962       if (!res)
08963          res = ast_play_and_wait(chan, "vm-opts");
08964       if (!res) {
08965          vms->starting = 0;
08966          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08967       }
08968    }
08969    return res;
08970 }
08971 
08972 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08973 {
08974    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08975       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08976    } else {             /* Default to ENGLISH */
08977       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08978    }
08979 }
08980 
08981 
08982 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08983 {
08984    int cmd = 0;
08985    int duration = 0;
08986    int tries = 0;
08987    char newpassword[80] = "";
08988    char newpassword2[80] = "";
08989    char prefile[PATH_MAX] = "";
08990    unsigned char buf[256];
08991    int bytes = 0;
08992 
08993    if (ast_adsi_available(chan)) {
08994       bytes += adsi_logo(buf + bytes);
08995       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08996       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08997       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08998       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08999       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09000    }
09001 
09002    /* First, have the user change their password 
09003       so they won't get here again */
09004    for (;;) {
09005       newpassword[1] = '\0';
09006       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09007       if (cmd == '#')
09008          newpassword[0] = '\0';
09009       if (cmd < 0 || cmd == 't' || cmd == '#')
09010          return cmd;
09011       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09012       if (cmd < 0 || cmd == 't' || cmd == '#')
09013          return cmd;
09014       cmd = check_password(vmu, newpassword); /* perform password validation */
09015       if (cmd != 0) {
09016          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09017          cmd = ast_play_and_wait(chan, vm_invalid_password);
09018       } else {
09019          newpassword2[1] = '\0';
09020          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09021          if (cmd == '#')
09022             newpassword2[0] = '\0';
09023          if (cmd < 0 || cmd == 't' || cmd == '#')
09024             return cmd;
09025          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09026          if (cmd < 0 || cmd == 't' || cmd == '#')
09027             return cmd;
09028          if (!strcmp(newpassword, newpassword2))
09029             break;
09030          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09031          cmd = ast_play_and_wait(chan, vm_mismatch);
09032       }
09033       if (++tries == 3)
09034          return -1;
09035       if (cmd != 0) {
09036          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09037       }
09038    }
09039    if (pwdchange & PWDCHANGE_INTERNAL)
09040       vm_change_password(vmu, newpassword);
09041    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09042       vm_change_password_shell(vmu, newpassword);
09043 
09044    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09045    cmd = ast_play_and_wait(chan, vm_passchanged);
09046 
09047    /* If forcename is set, have the user record their name */  
09048    if (ast_test_flag(vmu, VM_FORCENAME)) {
09049       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09050       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09051          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09052          if (cmd < 0 || cmd == 't' || cmd == '#')
09053             return cmd;
09054       }
09055    }
09056 
09057    /* If forcegreetings is set, have the user record their greetings */
09058    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09059       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09060       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09061          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09062          if (cmd < 0 || cmd == 't' || cmd == '#')
09063             return cmd;
09064       }
09065 
09066       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09067       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09068          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09069          if (cmd < 0 || cmd == 't' || cmd == '#')
09070             return cmd;
09071       }
09072    }
09073 
09074    return cmd;
09075 }
09076 
09077 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09078 {
09079    int cmd = 0;
09080    int retries = 0;
09081    int duration = 0;
09082    char newpassword[80] = "";
09083    char newpassword2[80] = "";
09084    char prefile[PATH_MAX] = "";
09085    unsigned char buf[256];
09086    int bytes = 0;
09087 
09088    if (ast_adsi_available(chan)) {
09089       bytes += adsi_logo(buf + bytes);
09090       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09091       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09092       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09093       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09094       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09095    }
09096    while ((cmd >= 0) && (cmd != 't')) {
09097       if (cmd)
09098          retries = 0;
09099       switch (cmd) {
09100       case '1': /* Record your unavailable message */
09101          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09102          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09103          break;
09104       case '2':  /* Record your busy message */
09105          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09106          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09107          break;
09108       case '3': /* Record greeting */
09109          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09110          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09111          break;
09112       case '4':  /* manage the temporary greeting */
09113          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09114          break;
09115       case '5': /* change password */
09116          if (vmu->password[0] == '-') {
09117             cmd = ast_play_and_wait(chan, "vm-no");
09118             break;
09119          }
09120          newpassword[1] = '\0';
09121          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09122          if (cmd == '#')
09123             newpassword[0] = '\0';
09124          else {
09125             if (cmd < 0)
09126                break;
09127             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09128                break;
09129             }
09130          }
09131          cmd = check_password(vmu, newpassword); /* perform password validation */
09132          if (cmd != 0) {
09133             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09134             cmd = ast_play_and_wait(chan, vm_invalid_password);
09135             if (!cmd) {
09136                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09137             }
09138             break;
09139          }
09140          newpassword2[1] = '\0';
09141          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09142          if (cmd == '#')
09143             newpassword2[0] = '\0';
09144          else {
09145             if (cmd < 0)
09146                break;
09147 
09148             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09149                break;
09150             }
09151          }
09152          if (strcmp(newpassword, newpassword2)) {
09153             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09154             cmd = ast_play_and_wait(chan, vm_mismatch);
09155             if (!cmd) {
09156                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09157             }
09158             break;
09159          }
09160          if (pwdchange & PWDCHANGE_INTERNAL)
09161             vm_change_password(vmu, newpassword);
09162          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09163             vm_change_password_shell(vmu, newpassword);
09164 
09165          ast_debug(1, "User %s set password to %s of length %d\n",
09166             vms->username, newpassword, (int) strlen(newpassword));
09167          cmd = ast_play_and_wait(chan, vm_passchanged);
09168          break;
09169       case '*': 
09170          cmd = 't';
09171          break;
09172       default: 
09173          cmd = 0;
09174          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09175          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09176          if (ast_fileexists(prefile, NULL, NULL)) {
09177             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09178          }
09179          DISPOSE(prefile, -1);
09180          if (!cmd) {
09181             cmd = ast_play_and_wait(chan, "vm-options");
09182          }
09183          if (!cmd) {
09184             cmd = ast_waitfordigit(chan, 6000);
09185          }
09186          if (!cmd) {
09187             retries++;
09188          }
09189          if (retries > 3) {
09190             cmd = 't';
09191          }
09192       }
09193    }
09194    if (cmd == 't')
09195       cmd = 0;
09196    return cmd;
09197 }
09198 
09199 /*!
09200  * \brief The handler for 'record a temporary greeting'. 
09201  * \param chan
09202  * \param vmu
09203  * \param vms
09204  * \param fmtc
09205  * \param record_gain
09206  *
09207  * This is option 4 from the mailbox options menu.
09208  * This function manages the following promptings:
09209  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09210  * 2: remove (delete) the temporary greeting.
09211  * *: return to the main menu.
09212  *
09213  * \return zero on success, -1 on error.
09214  */
09215 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09216 {
09217    int cmd = 0;
09218    int retries = 0;
09219    int duration = 0;
09220    char prefile[PATH_MAX] = "";
09221    unsigned char buf[256];
09222    int bytes = 0;
09223 
09224    if (ast_adsi_available(chan)) {
09225       bytes += adsi_logo(buf + bytes);
09226       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09227       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09228       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09229       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09230       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09231    }
09232 
09233    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09234    while ((cmd >= 0) && (cmd != 't')) {
09235       if (cmd)
09236          retries = 0;
09237       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09238       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09239          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09240          cmd = 't';  
09241       } else {
09242          switch (cmd) {
09243          case '1':
09244             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09245             break;
09246          case '2':
09247             DELETE(prefile, -1, prefile, vmu);
09248             ast_play_and_wait(chan, "vm-tempremoved");
09249             cmd = 't';  
09250             break;
09251          case '*': 
09252             cmd = 't';
09253             break;
09254          default:
09255             cmd = ast_play_and_wait(chan,
09256                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09257                   "vm-tempgreeting2" : "vm-tempgreeting");
09258             if (!cmd)
09259                cmd = ast_waitfordigit(chan, 6000);
09260             if (!cmd)
09261                retries++;
09262             if (retries > 3)
09263                cmd = 't';
09264          }
09265       }
09266       DISPOSE(prefile, -1);
09267    }
09268    if (cmd == 't')
09269       cmd = 0;
09270    return cmd;
09271 }
09272 
09273 /*!
09274  * \brief Greek syntax for 'You have N messages' greeting.
09275  * \param chan
09276  * \param vms
09277  * \param vmu
09278  *
09279  * \return zero on success, -1 on error.
09280  */   
09281 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09282 {
09283    int cmd = 0;
09284 
09285    if (vms->lastmsg > -1) {
09286       cmd = play_message(chan, vmu, vms);
09287    } else {
09288       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09289       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09290          if (!cmd) {
09291             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09292             cmd = ast_play_and_wait(chan, vms->fn);
09293          }
09294          if (!cmd)
09295             cmd = ast_play_and_wait(chan, "vm-messages");
09296       } else {
09297          if (!cmd)
09298             cmd = ast_play_and_wait(chan, "vm-messages");
09299          if (!cmd) {
09300             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09301             cmd = ast_play_and_wait(chan, vms->fn);
09302          }
09303       }
09304    } 
09305    return cmd;
09306 }
09307 
09308 /* Hebrew Syntax */
09309 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09310 {
09311    int cmd = 0;
09312 
09313    if (vms->lastmsg > -1) {
09314       cmd = play_message(chan, vmu, vms);
09315    } else {
09316       if (!strcasecmp(vms->fn, "INBOX")) {
09317          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09318       } else {
09319          cmd = ast_play_and_wait(chan, "vm-nomessages");
09320       }
09321    }
09322    return cmd;
09323 }
09324 
09325 /*! 
09326  * \brief Default English syntax for 'You have N messages' greeting.
09327  * \param chan
09328  * \param vms
09329  * \param vmu
09330  *
09331  * \return zero on success, -1 on error.
09332  */
09333 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09334 {
09335    int cmd = 0;
09336 
09337    if (vms->lastmsg > -1) {
09338       cmd = play_message(chan, vmu, vms);
09339    } else {
09340       cmd = ast_play_and_wait(chan, "vm-youhave");
09341       if (!cmd) 
09342          cmd = ast_play_and_wait(chan, "vm-no");
09343       if (!cmd) {
09344          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09345          cmd = ast_play_and_wait(chan, vms->fn);
09346       }
09347       if (!cmd)
09348          cmd = ast_play_and_wait(chan, "vm-messages");
09349    }
09350    return cmd;
09351 }
09352 
09353 /*! 
09354  *\brief Italian syntax for 'You have N messages' greeting.
09355  * \param chan
09356  * \param vms
09357  * \param vmu
09358  *
09359  * \return zero on success, -1 on error.
09360  */
09361 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09362 {
09363    int cmd;
09364 
09365    if (vms->lastmsg > -1) {
09366       cmd = play_message(chan, vmu, vms);
09367    } else {
09368       cmd = ast_play_and_wait(chan, "vm-no");
09369       if (!cmd)
09370          cmd = ast_play_and_wait(chan, "vm-message");
09371       if (!cmd) {
09372          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09373          cmd = ast_play_and_wait(chan, vms->fn);
09374       }
09375    }
09376    return cmd;
09377 }
09378 
09379 /*! 
09380  * \brief Spanish syntax for 'You have N messages' greeting.
09381  * \param chan
09382  * \param vms
09383  * \param vmu
09384  *
09385  * \return zero on success, -1 on error.
09386  */
09387 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09388 {
09389    int cmd;
09390 
09391    if (vms->lastmsg > -1) {
09392       cmd = play_message(chan, vmu, vms);
09393    } else {
09394       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09395       if (!cmd)
09396          cmd = ast_play_and_wait(chan, "vm-messages");
09397       if (!cmd) {
09398          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09399          cmd = ast_play_and_wait(chan, vms->fn);
09400       }
09401    }
09402    return cmd;
09403 }
09404 
09405 /*! 
09406  * \brief Portuguese syntax for 'You have N messages' greeting.
09407  * \param chan
09408  * \param vms
09409  * \param vmu
09410  *
09411  * \return zero on success, -1 on error.
09412  */
09413 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09414 {
09415    int cmd;
09416 
09417    if (vms->lastmsg > -1) {
09418       cmd = play_message(chan, vmu, vms);
09419    } else {
09420       cmd = ast_play_and_wait(chan, "vm-no");
09421       if (!cmd) {
09422          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09423          cmd = ast_play_and_wait(chan, vms->fn);
09424       }
09425       if (!cmd)
09426          cmd = ast_play_and_wait(chan, "vm-messages");
09427    }
09428    return cmd;
09429 }
09430 
09431 /*! 
09432  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09433  * \param chan
09434  * \param vms
09435  * \param vmu
09436  *
09437  * \return zero on success, -1 on error.
09438  */
09439 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09440 {
09441    int cmd;
09442 
09443    if (vms->lastmsg > -1) {
09444       cmd = play_message(chan, vmu, vms);
09445    } else {
09446       cmd = ast_play_and_wait(chan, "vm-you");
09447       if (!cmd) 
09448          cmd = ast_play_and_wait(chan, "vm-haveno");
09449       if (!cmd)
09450          cmd = ast_play_and_wait(chan, "vm-messages");
09451       if (!cmd) {
09452          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09453          cmd = ast_play_and_wait(chan, vms->fn);
09454       }
09455    }
09456    return cmd;
09457 }
09458 
09459 /*! 
09460  * \brief Vietnamese syntax for 'You have N messages' greeting.
09461  * \param chan
09462  * \param vms
09463  * \param vmu
09464  *
09465  * \return zero on success, -1 on error.
09466  */
09467 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09468 {
09469    int cmd = 0;
09470 
09471    if (vms->lastmsg > -1) {
09472       cmd = play_message(chan, vmu, vms);
09473    } else {
09474       cmd = ast_play_and_wait(chan, "vm-no");
09475       if (!cmd) {
09476          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09477          cmd = ast_play_and_wait(chan, vms->fn);
09478       }
09479    }
09480    return cmd;
09481 }
09482 
09483 /*!
09484  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09485  * \param chan The channel for the current user. We read the language property from this.
09486  * \param vms passed into the language-specific vm_browse_messages function.
09487  * \param vmu passed into the language-specific vm_browse_messages function.
09488  * 
09489  * The method to be invoked is determined by the value of language code property in the user's channel.
09490  * The default (when unable to match) is to use english.
09491  *
09492  * \return zero on success, -1 on error.
09493  */
09494 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09495 {
09496    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09497       return vm_browse_messages_es(chan, vms, vmu);
09498    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09499       return vm_browse_messages_gr(chan, vms, vmu);
09500    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09501       return vm_browse_messages_he(chan, vms, vmu);
09502    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09503       return vm_browse_messages_it(chan, vms, vmu);
09504    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09505       return vm_browse_messages_pt(chan, vms, vmu);
09506    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09507       return vm_browse_messages_vi(chan, vms, vmu);
09508    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09509       return vm_browse_messages_zh(chan, vms, vmu);
09510    } else {                                             /* Default to English syntax */
09511       return vm_browse_messages_en(chan, vms, vmu);
09512    }
09513 }
09514 
09515 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09516          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09517          int skipuser, int max_logins, int silent)
09518 {
09519    int useadsi = 0, valid = 0, logretries = 0;
09520    char password[AST_MAX_EXTENSION]="", *passptr;
09521    struct ast_vm_user vmus, *vmu = NULL;
09522 
09523    /* If ADSI is supported, setup login screen */
09524    adsi_begin(chan, &useadsi);
09525    if (!skipuser && useadsi)
09526       adsi_login(chan);
09527    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09528       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09529       return -1;
09530    }
09531    
09532    /* Authenticate them and get their mailbox/password */
09533    
09534    while (!valid && (logretries < max_logins)) {
09535       /* Prompt for, and read in the username */
09536       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09537          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09538          return -1;
09539       }
09540       if (ast_strlen_zero(mailbox)) {
09541          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09542             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09543          } else {
09544             ast_verb(3, "Username not entered\n"); 
09545             return -1;
09546          }
09547       } else if (mailbox[0] == '*') {
09548          /* user entered '*' */
09549          if (ast_exists_extension(chan, chan->context, "a", 1,
09550             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09551             return -1;
09552          }
09553          mailbox[0] = '\0';
09554       }
09555 
09556       if (useadsi)
09557          adsi_password(chan);
09558 
09559       if (!ast_strlen_zero(prefix)) {
09560          char fullusername[80] = "";
09561          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09562          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09563          ast_copy_string(mailbox, fullusername, mailbox_size);
09564       }
09565 
09566       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09567       vmu = find_user(&vmus, context, mailbox);
09568       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09569          /* saved password is blank, so don't bother asking */
09570          password[0] = '\0';
09571       } else {
09572          if (ast_streamfile(chan, vm_password, chan->language)) {
09573             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09574             return -1;
09575          }
09576          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09577             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09578             return -1;
09579          } else if (password[0] == '*') {
09580             /* user entered '*' */
09581             if (ast_exists_extension(chan, chan->context, "a", 1,
09582                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09583                mailbox[0] = '*';
09584                return -1;
09585             }
09586             mailbox[0] = '\0';
09587          }
09588       }
09589 
09590       if (vmu) {
09591          passptr = vmu->password;
09592          if (passptr[0] == '-') passptr++;
09593       }
09594       if (vmu && !strcmp(passptr, password))
09595          valid++;
09596       else {
09597          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09598          if (!ast_strlen_zero(prefix))
09599             mailbox[0] = '\0';
09600       }
09601       logretries++;
09602       if (!valid) {
09603          if (skipuser || logretries >= max_logins) {
09604             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09605                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09606                return -1;
09607             }
09608          } else {
09609             if (useadsi)
09610                adsi_login(chan);
09611             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09612                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09613                return -1;
09614             }
09615          }
09616          if (ast_waitstream(chan, "")) /* Channel is hung up */
09617             return -1;
09618       }
09619    }
09620    if (!valid && (logretries >= max_logins)) {
09621       ast_stopstream(chan);
09622       ast_play_and_wait(chan, "vm-goodbye");
09623       return -1;
09624    }
09625    if (vmu && !skipuser) {
09626       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09627    }
09628    return 0;
09629 }
09630 
09631 static int vm_execmain(struct ast_channel *chan, const char *data)
09632 {
09633    /* XXX This is, admittedly, some pretty horrendous code.  For some
09634       reason it just seemed a lot easier to do with GOTO's.  I feel
09635       like I'm back in my GWBASIC days. XXX */
09636    int res = -1;
09637    int cmd = 0;
09638    int valid = 0;
09639    char prefixstr[80] ="";
09640    char ext_context[256]="";
09641    int box;
09642    int useadsi = 0;
09643    int skipuser = 0;
09644    struct vm_state vms;
09645    struct ast_vm_user *vmu = NULL, vmus;
09646    char *context = NULL;
09647    int silentexit = 0;
09648    struct ast_flags flags = { 0 };
09649    signed char record_gain = 0;
09650    int play_auto = 0;
09651    int play_folder = 0;
09652    int in_urgent = 0;
09653 #ifdef IMAP_STORAGE
09654    int deleted = 0;
09655 #endif
09656 
09657    /* Add the vm_state to the active list and keep it active */
09658    memset(&vms, 0, sizeof(vms));
09659 
09660    vms.lastmsg = -1;
09661 
09662    memset(&vmus, 0, sizeof(vmus));
09663 
09664    if (chan->_state != AST_STATE_UP) {
09665       ast_debug(1, "Before ast_answer\n");
09666       ast_answer(chan);
09667    }
09668 
09669    if (!ast_strlen_zero(data)) {
09670       char *opts[OPT_ARG_ARRAY_SIZE];
09671       char *parse;
09672       AST_DECLARE_APP_ARGS(args,
09673          AST_APP_ARG(argv0);
09674          AST_APP_ARG(argv1);
09675       );
09676 
09677       parse = ast_strdupa(data);
09678 
09679       AST_STANDARD_APP_ARGS(args, parse);
09680 
09681       if (args.argc == 2) {
09682          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09683             return -1;
09684          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09685             int gain;
09686             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09687                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09688                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09689                   return -1;
09690                } else {
09691                   record_gain = (signed char) gain;
09692                }
09693             } else {
09694                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09695             }
09696          }
09697          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09698             play_auto = 1;
09699             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09700                /* See if it is a folder name first */
09701                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09702                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09703                      play_folder = -1;
09704                   }
09705                } else {
09706                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09707                }
09708             } else {
09709                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09710             }
09711             if (play_folder > 9 || play_folder < 0) {
09712                ast_log(AST_LOG_WARNING,
09713                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09714                   opts[OPT_ARG_PLAYFOLDER]);
09715                play_folder = 0;
09716             }
09717          }
09718       } else {
09719          /* old style options parsing */
09720          while (*(args.argv0)) {
09721             if (*(args.argv0) == 's')
09722                ast_set_flag(&flags, OPT_SILENT);
09723             else if (*(args.argv0) == 'p')
09724                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09725             else 
09726                break;
09727             (args.argv0)++;
09728          }
09729 
09730       }
09731 
09732       valid = ast_test_flag(&flags, OPT_SILENT);
09733 
09734       if ((context = strchr(args.argv0, '@')))
09735          *context++ = '\0';
09736 
09737       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09738          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09739       else
09740          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09741 
09742       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09743          skipuser++;
09744       else
09745          valid = 0;
09746    }
09747 
09748    if (!valid)
09749       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09750 
09751    ast_debug(1, "After vm_authenticate\n");
09752 
09753    if (vms.username[0] == '*') {
09754       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09755 
09756       /* user entered '*' */
09757       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09758          res = 0; /* prevent hangup */
09759          goto out;
09760       }
09761    }
09762 
09763    if (!res) {
09764       valid = 1;
09765       if (!skipuser)
09766          vmu = &vmus;
09767    } else {
09768       res = 0;
09769    }
09770 
09771    /* If ADSI is supported, setup login screen */
09772    adsi_begin(chan, &useadsi);
09773 
09774    if (!valid) {
09775       goto out;
09776    }
09777 
09778 #ifdef IMAP_STORAGE
09779    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09780    pthread_setspecific(ts_vmstate.key, &vms);
09781 
09782    vms.interactive = 1;
09783    vms.updated = 1;
09784    if (vmu)
09785       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09786    vmstate_insert(&vms);
09787    init_vm_state(&vms);
09788 #endif
09789    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09790    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09791       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09792       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09793       return -1;
09794    }
09795    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09796       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09797       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09798       return -1;
09799    }
09800    
09801    /* Set language from config to override channel language */
09802    if (!ast_strlen_zero(vmu->language))
09803       ast_string_field_set(chan, language, vmu->language);
09804 
09805    /* Retrieve urgent, old and new message counts */
09806    ast_debug(1, "Before open_mailbox\n");
09807    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09808    if (res < 0)
09809       goto out;
09810    vms.oldmessages = vms.lastmsg + 1;
09811    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
09812    /* check INBOX */
09813    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09814    if (res < 0)
09815       goto out;
09816    vms.newmessages = vms.lastmsg + 1;
09817    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
09818    /* Start in Urgent */
09819    in_urgent = 1;
09820    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09821    if (res < 0)
09822       goto out;
09823    vms.urgentmessages = vms.lastmsg + 1;
09824    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
09825 
09826    /* Select proper mailbox FIRST!! */
09827    if (play_auto) {
09828       if (vms.urgentmessages) {
09829          in_urgent = 1;
09830          res = open_mailbox(&vms, vmu, 11);
09831       } else {
09832          in_urgent = 0;
09833          res = open_mailbox(&vms, vmu, play_folder);
09834       }
09835       if (res < 0)
09836          goto out;
09837 
09838       /* If there are no new messages, inform the user and hangup */
09839       if (vms.lastmsg == -1) {
09840          in_urgent = 0;
09841          cmd = vm_browse_messages(chan, &vms, vmu);
09842          res = 0;
09843          goto out;
09844       }
09845    } else {
09846       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09847          /* If we only have old messages start here */
09848          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09849          in_urgent = 0;
09850          play_folder = 1;
09851          if (res < 0)
09852             goto out;
09853       } else if (!vms.urgentmessages && vms.newmessages) {
09854          /* If we have new messages but none are urgent */
09855          in_urgent = 0;
09856          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09857          if (res < 0)
09858             goto out;
09859       }
09860    }
09861 
09862    if (useadsi)
09863       adsi_status(chan, &vms);
09864    res = 0;
09865 
09866    /* Check to see if this is a new user */
09867    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09868       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09869       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09870          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09871       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09872       if ((cmd == 't') || (cmd == '#')) {
09873          /* Timeout */
09874          res = 0;
09875          goto out;
09876       } else if (cmd < 0) {
09877          /* Hangup */
09878          res = -1;
09879          goto out;
09880       }
09881    }
09882 #ifdef IMAP_STORAGE
09883       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
09884       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09885          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09886          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09887       }
09888       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09889       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09890          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09891          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09892       }
09893 #endif
09894    if (play_auto) {
09895       cmd = '1';
09896    } else {
09897       cmd = vm_intro(chan, vmu, &vms);
09898    }
09899 
09900    vms.repeats = 0;
09901    vms.starting = 1;
09902    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09903       /* Run main menu */
09904       switch (cmd) {
09905       case '1': /* First message */
09906          vms.curmsg = 0;
09907          /* Fall through */
09908       case '5': /* Play current message */
09909          cmd = vm_browse_messages(chan, &vms, vmu);
09910          break;
09911       case '2': /* Change folders */
09912          if (useadsi)
09913             adsi_folders(chan, 0, "Change to folder...");
09914          cmd = get_folder2(chan, "vm-changeto", 0);
09915          if (cmd == '#') {
09916             cmd = 0;
09917          } else if (cmd > 0) {
09918             cmd = cmd - '0';
09919             res = close_mailbox(&vms, vmu);
09920             if (res == ERROR_LOCK_PATH)
09921                goto out;
09922             /* If folder is not urgent, set in_urgent to zero! */
09923             if (cmd != 11) in_urgent = 0;
09924             res = open_mailbox(&vms, vmu, cmd);
09925             if (res < 0)
09926                goto out;
09927             play_folder = cmd;
09928             cmd = 0;
09929          }
09930          if (useadsi)
09931             adsi_status2(chan, &vms);
09932             
09933          if (!cmd)
09934             cmd = vm_play_folder_name(chan, vms.vmbox);
09935 
09936          vms.starting = 1;
09937          break;
09938       case '3': /* Advanced options */
09939          cmd = 0;
09940          vms.repeats = 0;
09941          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09942             switch (cmd) {
09943             case '1': /* Reply */
09944                if (vms.lastmsg > -1 && !vms.starting) {
09945                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09946                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09947                      res = cmd;
09948                      goto out;
09949                   }
09950                } else
09951                   cmd = ast_play_and_wait(chan, "vm-sorry");
09952                cmd = 't';
09953                break;
09954             case '2': /* Callback */
09955                if (!vms.starting)
09956                   ast_verb(3, "Callback Requested\n");
09957                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09958                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09959                   if (cmd == 9) {
09960                      silentexit = 1;
09961                      goto out;
09962                   } else if (cmd == ERROR_LOCK_PATH) {
09963                      res = cmd;
09964                      goto out;
09965                   }
09966                } else 
09967                   cmd = ast_play_and_wait(chan, "vm-sorry");
09968                cmd = 't';
09969                break;
09970             case '3': /* Envelope */
09971                if (vms.lastmsg > -1 && !vms.starting) {
09972                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09973                   if (cmd == ERROR_LOCK_PATH) {
09974                      res = cmd;
09975                      goto out;
09976                   }
09977                } else
09978                   cmd = ast_play_and_wait(chan, "vm-sorry");
09979                cmd = 't';
09980                break;
09981             case '4': /* Dialout */
09982                if (!ast_strlen_zero(vmu->dialout)) {
09983                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09984                   if (cmd == 9) {
09985                      silentexit = 1;
09986                      goto out;
09987                   }
09988                } else 
09989                   cmd = ast_play_and_wait(chan, "vm-sorry");
09990                cmd = 't';
09991                break;
09992 
09993             case '5': /* Leave VoiceMail */
09994                if (ast_test_flag(vmu, VM_SVMAIL)) {
09995                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09996                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09997                      res = cmd;
09998                      goto out;
09999                   }
10000                } else
10001                   cmd = ast_play_and_wait(chan, "vm-sorry");
10002                cmd = 't';
10003                break;
10004                
10005             case '*': /* Return to main menu */
10006                cmd = 't';
10007                break;
10008 
10009             default:
10010                cmd = 0;
10011                if (!vms.starting) {
10012                   cmd = ast_play_and_wait(chan, "vm-toreply");
10013                }
10014                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10015                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10016                }
10017                if (!cmd && !vms.starting) {
10018                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10019                }
10020                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10021                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10022                }
10023                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
10024                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10025                if (!cmd)
10026                   cmd = ast_play_and_wait(chan, "vm-starmain");
10027                if (!cmd)
10028                   cmd = ast_waitfordigit(chan, 6000);
10029                if (!cmd)
10030                   vms.repeats++;
10031                if (vms.repeats > 3)
10032                   cmd = 't';
10033             }
10034          }
10035          if (cmd == 't') {
10036             cmd = 0;
10037             vms.repeats = 0;
10038          }
10039          break;
10040       case '4': /* Go to the previous message */
10041          if (vms.curmsg > 0) {
10042             vms.curmsg--;
10043             cmd = play_message(chan, vmu, &vms);
10044          } else {
10045             /* Check if we were listening to new
10046                messages.  If so, go to Urgent messages
10047                instead of saying "no more messages"
10048             */
10049             if (in_urgent == 0 && vms.urgentmessages > 0) {
10050                /* Check for Urgent messages */
10051                in_urgent = 1;
10052                res = close_mailbox(&vms, vmu);
10053                if (res == ERROR_LOCK_PATH)
10054                   goto out;
10055                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10056                if (res < 0)
10057                   goto out;
10058                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10059                vms.curmsg = vms.lastmsg;
10060                if (vms.lastmsg < 0)
10061                   cmd = ast_play_and_wait(chan, "vm-nomore");
10062             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10063                vms.curmsg = vms.lastmsg;
10064                cmd = play_message(chan, vmu, &vms);
10065             } else {
10066                cmd = ast_play_and_wait(chan, "vm-nomore");
10067             }
10068          }
10069          break;
10070       case '6': /* Go to the next message */
10071          if (vms.curmsg < vms.lastmsg) {
10072             vms.curmsg++;
10073             cmd = play_message(chan, vmu, &vms);
10074          } else {
10075             if (in_urgent && vms.newmessages > 0) {
10076                /* Check if we were listening to urgent
10077                 * messages.  If so, go to regular new messages
10078                 * instead of saying "no more messages"
10079                 */
10080                in_urgent = 0;
10081                res = close_mailbox(&vms, vmu);
10082                if (res == ERROR_LOCK_PATH)
10083                   goto out;
10084                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10085                if (res < 0)
10086                   goto out;
10087                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10088                vms.curmsg = -1;
10089                if (vms.lastmsg < 0) {
10090                   cmd = ast_play_and_wait(chan, "vm-nomore");
10091                }
10092             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10093                vms.curmsg = 0;
10094                cmd = play_message(chan, vmu, &vms);
10095             } else {
10096                cmd = ast_play_and_wait(chan, "vm-nomore");
10097             }
10098          }
10099          break;
10100       case '7': /* Delete the current message */
10101          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10102             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10103             if (useadsi)
10104                adsi_delete(chan, &vms);
10105             if (vms.deleted[vms.curmsg]) {
10106                if (play_folder == 0) {
10107                   if (in_urgent) {
10108                      vms.urgentmessages--;
10109                   } else {
10110                      vms.newmessages--;
10111                   }
10112                }
10113                else if (play_folder == 1)
10114                   vms.oldmessages--;
10115                cmd = ast_play_and_wait(chan, "vm-deleted");
10116             } else {
10117                if (play_folder == 0) {
10118                   if (in_urgent) {
10119                      vms.urgentmessages++;
10120                   } else {
10121                      vms.newmessages++;
10122                   }
10123                }
10124                else if (play_folder == 1)
10125                   vms.oldmessages++;
10126                cmd = ast_play_and_wait(chan, "vm-undeleted");
10127             }
10128             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10129                if (vms.curmsg < vms.lastmsg) {
10130                   vms.curmsg++;
10131                   cmd = play_message(chan, vmu, &vms);
10132                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10133                   vms.curmsg = 0;
10134                   cmd = play_message(chan, vmu, &vms);
10135                } else {
10136                   /* Check if we were listening to urgent
10137                      messages.  If so, go to regular new messages
10138                      instead of saying "no more messages"
10139                   */
10140                   if (in_urgent == 1) {
10141                      /* Check for new messages */
10142                      in_urgent = 0;
10143                      res = close_mailbox(&vms, vmu);
10144                      if (res == ERROR_LOCK_PATH)
10145                         goto out;
10146                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10147                      if (res < 0)
10148                         goto out;
10149                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10150                      vms.curmsg = -1;
10151                      if (vms.lastmsg < 0)
10152                         cmd = ast_play_and_wait(chan, "vm-nomore");
10153                   } else {
10154                      cmd = ast_play_and_wait(chan, "vm-nomore");
10155                   }
10156                }
10157             }
10158          } else /* Delete not valid if we haven't selected a message */
10159             cmd = 0;
10160 #ifdef IMAP_STORAGE
10161          deleted = 1;
10162 #endif
10163          break;
10164    
10165       case '8': /* Forward the current messgae */
10166          if (vms.lastmsg > -1) {
10167             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10168             if (cmd == ERROR_LOCK_PATH) {
10169                res = cmd;
10170                goto out;
10171             }
10172          } else {
10173             /* Check if we were listening to urgent
10174                messages.  If so, go to regular new messages
10175                instead of saying "no more messages"
10176             */
10177             if (in_urgent == 1 && vms.newmessages > 0) {
10178                /* Check for new messages */
10179                in_urgent = 0;
10180                res = close_mailbox(&vms, vmu);
10181                if (res == ERROR_LOCK_PATH)
10182                   goto out;
10183                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10184                if (res < 0)
10185                   goto out;
10186                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10187                vms.curmsg = -1;
10188                if (vms.lastmsg < 0)
10189                   cmd = ast_play_and_wait(chan, "vm-nomore");
10190             } else {
10191                cmd = ast_play_and_wait(chan, "vm-nomore");
10192             }
10193          }
10194          break;
10195       case '9': /* Save message to folder */
10196          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10197             /* No message selected */
10198             cmd = 0;
10199             break;
10200          }
10201          if (useadsi)
10202             adsi_folders(chan, 1, "Save to folder...");
10203          cmd = get_folder2(chan, "vm-savefolder", 1);
10204          box = 0; /* Shut up compiler */
10205          if (cmd == '#') {
10206             cmd = 0;
10207             break;
10208          } else if (cmd > 0) {
10209             box = cmd = cmd - '0';
10210             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10211             if (cmd == ERROR_LOCK_PATH) {
10212                res = cmd;
10213                goto out;
10214 #ifndef IMAP_STORAGE
10215             } else if (!cmd) {
10216                vms.deleted[vms.curmsg] = 1;
10217 #endif
10218             } else {
10219                vms.deleted[vms.curmsg] = 0;
10220                vms.heard[vms.curmsg] = 0;
10221             }
10222          }
10223          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10224          if (useadsi)
10225             adsi_message(chan, &vms);
10226          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10227          if (!cmd) {
10228             cmd = ast_play_and_wait(chan, "vm-message");
10229             if (!cmd) 
10230                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10231             if (!cmd)
10232                cmd = ast_play_and_wait(chan, "vm-savedto");
10233             if (!cmd)
10234                cmd = vm_play_folder_name(chan, vms.fn);
10235          } else {
10236             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10237          }
10238          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10239             if (vms.curmsg < vms.lastmsg) {
10240                vms.curmsg++;
10241                cmd = play_message(chan, vmu, &vms);
10242             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10243                vms.curmsg = 0;
10244                cmd = play_message(chan, vmu, &vms);
10245             } else {
10246                /* Check if we were listening to urgent
10247                   messages.  If so, go to regular new messages
10248                   instead of saying "no more messages"
10249                */
10250                if (in_urgent == 1 && vms.newmessages > 0) {
10251                   /* Check for new messages */
10252                   in_urgent = 0;
10253                   res = close_mailbox(&vms, vmu);
10254                   if (res == ERROR_LOCK_PATH)
10255                      goto out;
10256                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10257                   if (res < 0)
10258                      goto out;
10259                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10260                   vms.curmsg = -1;
10261                   if (vms.lastmsg < 0)
10262                      cmd = ast_play_and_wait(chan, "vm-nomore");
10263                } else {
10264                   cmd = ast_play_and_wait(chan, "vm-nomore");
10265                }
10266             }
10267          }
10268          break;
10269       case '*': /* Help */
10270          if (!vms.starting) {
10271             cmd = ast_play_and_wait(chan, "vm-onefor");
10272             if (!strncasecmp(chan->language, "he", 2)) {
10273                cmd = ast_play_and_wait(chan, "vm-for");
10274             }
10275             if (!cmd)
10276                cmd = vm_play_folder_name(chan, vms.vmbox);
10277             if (!cmd)
10278                cmd = ast_play_and_wait(chan, "vm-opts");
10279             if (!cmd)
10280                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10281          } else
10282             cmd = 0;
10283          break;
10284       case '0': /* Mailbox options */
10285          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10286          if (useadsi)
10287             adsi_status(chan, &vms);
10288          break;
10289       default: /* Nothing */
10290          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10291          break;
10292       }
10293    }
10294    if ((cmd == 't') || (cmd == '#')) {
10295       /* Timeout */
10296       res = 0;
10297    } else {
10298       /* Hangup */
10299       res = -1;
10300    }
10301 
10302 out:
10303    if (res > -1) {
10304       ast_stopstream(chan);
10305       adsi_goodbye(chan);
10306       if (valid && res != OPERATOR_EXIT) {
10307          if (silentexit)
10308             res = ast_play_and_wait(chan, "vm-dialout");
10309          else 
10310             res = ast_play_and_wait(chan, "vm-goodbye");
10311       }
10312       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10313          res = 0;
10314       }
10315       if (useadsi)
10316          ast_adsi_unload_session(chan);
10317    }
10318    if (vmu)
10319       close_mailbox(&vms, vmu);
10320    if (valid) {
10321       int new = 0, old = 0, urgent = 0;
10322       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10323       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10324       /* Urgent flag not passwd to externnotify here */
10325       run_externnotify(vmu->context, vmu->mailbox, NULL);
10326       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10327       queue_mwi_event(ext_context, urgent, new, old);
10328    }
10329 #ifdef IMAP_STORAGE
10330    /* expunge message - use UID Expunge if supported on IMAP server*/
10331    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10332    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10333       ast_mutex_lock(&vms.lock);
10334 #ifdef HAVE_IMAP_TK2006
10335       if (LEVELUIDPLUS (vms.mailstream)) {
10336          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10337       } else 
10338 #endif
10339          mail_expunge(vms.mailstream);
10340       ast_mutex_unlock(&vms.lock);
10341    }
10342    /*  before we delete the state, we should copy pertinent info
10343     *  back to the persistent model */
10344    if (vmu) {
10345       vmstate_delete(&vms);
10346    }
10347 #endif
10348    if (vmu)
10349       free_user(vmu);
10350    if (vms.deleted)
10351       ast_free(vms.deleted);
10352    if (vms.heard)
10353       ast_free(vms.heard);
10354 
10355 #ifdef IMAP_STORAGE
10356    pthread_setspecific(ts_vmstate.key, NULL);
10357 #endif
10358    return res;
10359 }
10360 
10361 static int vm_exec(struct ast_channel *chan, const char *data)
10362 {
10363    int res = 0;
10364    char *tmp;
10365    struct leave_vm_options leave_options;
10366    struct ast_flags flags = { 0 };
10367    char *opts[OPT_ARG_ARRAY_SIZE];
10368    AST_DECLARE_APP_ARGS(args,
10369       AST_APP_ARG(argv0);
10370       AST_APP_ARG(argv1);
10371    );
10372    
10373    memset(&leave_options, 0, sizeof(leave_options));
10374 
10375    if (chan->_state != AST_STATE_UP)
10376       ast_answer(chan);
10377 
10378    if (!ast_strlen_zero(data)) {
10379       tmp = ast_strdupa(data);
10380       AST_STANDARD_APP_ARGS(args, tmp);
10381       if (args.argc == 2) {
10382          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10383             return -1;
10384          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10385          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10386             int gain;
10387 
10388             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10389                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10390                return -1;
10391             } else {
10392                leave_options.record_gain = (signed char) gain;
10393             }
10394          }
10395          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10396             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10397                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10398          }
10399       }
10400    } else {
10401       char temp[256];
10402       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10403       if (res < 0)
10404          return res;
10405       if (ast_strlen_zero(temp))
10406          return 0;
10407       args.argv0 = ast_strdupa(temp);
10408    }
10409 
10410    res = leave_voicemail(chan, args.argv0, &leave_options);
10411    if (res == 't') {
10412       ast_play_and_wait(chan, "vm-goodbye");
10413       res = 0;
10414    }
10415 
10416    if (res == OPERATOR_EXIT) {
10417       res = 0;
10418    }
10419 
10420    if (res == ERROR_LOCK_PATH) {
10421       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10422       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10423       res = 0;
10424    }
10425 
10426    return res;
10427 }
10428 
10429 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10430 {
10431    struct ast_vm_user *vmu;
10432 
10433    AST_LIST_TRAVERSE(&users, vmu, list) {
10434       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10435          if (strcasecmp(vmu->context, context)) {
10436             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10437                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10438                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10439                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10440          }
10441          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10442          return NULL;
10443       }
10444       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10445          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10446          return NULL;
10447       }
10448    }
10449    
10450    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10451       return NULL;
10452    
10453    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10454    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10455 
10456    AST_LIST_INSERT_TAIL(&users, vmu, list);
10457    
10458    return vmu;
10459 }
10460 
10461 static int append_mailbox(const char *context, const char *box, const char *data)
10462 {
10463    /* Assumes lock is already held */
10464    char *tmp;
10465    char *stringp;
10466    char *s;
10467    struct ast_vm_user *vmu;
10468    char *mailbox_full;
10469    int new = 0, old = 0, urgent = 0;
10470    char secretfn[PATH_MAX] = "";
10471 
10472    tmp = ast_strdupa(data);
10473 
10474    if (!(vmu = find_or_create(context, box)))
10475       return -1;
10476 
10477    populate_defaults(vmu);
10478 
10479    stringp = tmp;
10480    if ((s = strsep(&stringp, ","))) {
10481       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10482    }
10483    if (stringp && (s = strsep(&stringp, ","))) {
10484       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10485    }
10486    if (stringp && (s = strsep(&stringp, ","))) {
10487       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10488    }
10489    if (stringp && (s = strsep(&stringp, ","))) {
10490       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10491    }
10492    if (stringp && (s = strsep(&stringp, ","))) {
10493       apply_options(vmu, s);
10494    }
10495 
10496    switch (vmu->passwordlocation) {
10497    case OPT_PWLOC_SPOOLDIR:
10498       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10499       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10500    }
10501 
10502    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10503    strcpy(mailbox_full, box);
10504    strcat(mailbox_full, "@");
10505    strcat(mailbox_full, context);
10506 
10507    inboxcount2(mailbox_full, &urgent, &new, &old);
10508    queue_mwi_event(mailbox_full, urgent, new, old);
10509 
10510    return 0;
10511 }
10512 
10513 AST_TEST_DEFINE(test_voicemail_vmuser)
10514 {
10515    int res = 0;
10516    struct ast_vm_user *vmu;
10517    /* language parameter seems to only be used for display in manager action */
10518    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10519       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10520       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10521       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10522       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10523       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10524       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir";
10525 #ifdef IMAP_STORAGE
10526    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10527       "imapfolder=INBOX|imapvmshareid=6000";
10528 #endif
10529 
10530    switch (cmd) {
10531    case TEST_INIT:
10532       info->name = "vmuser";
10533       info->category = "/apps/app_voicemail/";
10534       info->summary = "Vmuser unit test";
10535       info->description =
10536          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10537       return AST_TEST_NOT_RUN;
10538    case TEST_EXECUTE:
10539       break;
10540    }
10541 
10542    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10543       return AST_TEST_NOT_RUN;
10544    }
10545    ast_set_flag(vmu, VM_ALLOCED);
10546 
10547    apply_options(vmu, options_string);
10548 
10549    if (!ast_test_flag(vmu, VM_ATTACH)) {
10550       ast_test_status_update(test, "Parse failure for attach option\n");
10551       res = 1;
10552    }
10553    if (strcasecmp(vmu->attachfmt, "wav49")) {
10554       ast_test_status_update(test, "Parse failure for attachftm option\n");
10555       res = 1;
10556    }
10557    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10558       ast_test_status_update(test, "Parse failure for serveremail option\n");
10559       res = 1;
10560    }
10561    if (strcasecmp(vmu->zonetag, "central")) {
10562       ast_test_status_update(test, "Parse failure for tz option\n");
10563       res = 1;
10564    }
10565    if (!ast_test_flag(vmu, VM_DELETE)) {
10566       ast_test_status_update(test, "Parse failure for delete option\n");
10567       res = 1;
10568    }
10569    if (!ast_test_flag(vmu, VM_SAYCID)) {
10570       ast_test_status_update(test, "Parse failure for saycid option\n");
10571       res = 1;
10572    }
10573    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10574       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10575       res = 1;
10576    }
10577    if (!ast_test_flag(vmu, VM_REVIEW)) {
10578       ast_test_status_update(test, "Parse failure for review option\n");
10579       res = 1;
10580    }
10581    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10582       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10583       res = 1;
10584    }
10585    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10586       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10587       res = 1;
10588    }
10589    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10590       ast_test_status_update(test, "Parse failure for operator option\n");
10591       res = 1;
10592    }
10593    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10594       ast_test_status_update(test, "Parse failure for envelope option\n");
10595       res = 1;
10596    }
10597    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10598       ast_test_status_update(test, "Parse failure for moveheard option\n");
10599       res = 1;
10600    }
10601    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10602       ast_test_status_update(test, "Parse failure for sayduration option\n");
10603       res = 1;
10604    }
10605    if (vmu->saydurationm != 5) {
10606       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10607       res = 1;
10608    }
10609    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10610       ast_test_status_update(test, "Parse failure for forcename option\n");
10611       res = 1;
10612    }
10613    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10614       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10615       res = 1;
10616    }
10617    if (strcasecmp(vmu->callback, "somecontext")) {
10618       ast_test_status_update(test, "Parse failure for callbacks option\n");
10619       res = 1;
10620    }
10621    if (strcasecmp(vmu->dialout, "somecontext2")) {
10622       ast_test_status_update(test, "Parse failure for dialout option\n");
10623       res = 1;
10624    }
10625    if (strcasecmp(vmu->exit, "somecontext3")) {
10626       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10627       res = 1;
10628    }
10629    if (vmu->minsecs != 10) {
10630       ast_test_status_update(test, "Parse failure for minsecs option\n");
10631       res = 1;
10632    }
10633    if (vmu->maxsecs != 100) {
10634       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10635       res = 1;
10636    }
10637    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10638       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10639       res = 1;
10640    }
10641    if (vmu->maxdeletedmsg != 50) {
10642       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10643       res = 1;
10644    }
10645    if (vmu->volgain != 1.3) {
10646       ast_test_status_update(test, "Parse failure for volgain option\n");
10647       res = 1;
10648    }
10649    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10650       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10651       res = 1;
10652    }
10653 #ifdef IMAP_STORAGE
10654    apply_options(vmu, option_string2);
10655 
10656    if (strcasecmp(vmu->imapuser, "imapuser")) {
10657       ast_test_status_update(test, "Parse failure for imapuser option\n");
10658       res = 1;
10659    }
10660    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10661       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10662       res = 1;
10663    }
10664    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10665       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10666       res = 1;
10667    }
10668    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10669       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10670       res = 1;
10671    }
10672 #endif
10673 
10674    free_user(vmu);
10675    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10676 }
10677 
10678 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10679 {
10680    struct ast_vm_user svm;
10681    char *context, *box;
10682    AST_DECLARE_APP_ARGS(args,
10683       AST_APP_ARG(mbox);
10684       AST_APP_ARG(options);
10685    );
10686    static int dep_warning = 0;
10687 
10688    if (ast_strlen_zero(data)) {
10689       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10690       return -1;
10691    }
10692 
10693    if (!dep_warning) {
10694       dep_warning = 1;
10695       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10696    }
10697 
10698    box = ast_strdupa(data);
10699 
10700    AST_STANDARD_APP_ARGS(args, box);
10701 
10702    if (args.options) {
10703    }
10704 
10705    if ((context = strchr(args.mbox, '@'))) {
10706       *context = '\0';
10707       context++;
10708    }
10709 
10710    if (find_user(&svm, context, args.mbox)) {
10711       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10712    } else
10713       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10714 
10715    return 0;
10716 }
10717 
10718 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10719 {
10720    struct ast_vm_user svm;
10721    AST_DECLARE_APP_ARGS(arg,
10722       AST_APP_ARG(mbox);
10723       AST_APP_ARG(context);
10724    );
10725 
10726    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10727 
10728    if (ast_strlen_zero(arg.mbox)) {
10729       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10730       return -1;
10731    }
10732 
10733    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10734    return 0;
10735 }
10736 
10737 static struct ast_custom_function mailbox_exists_acf = {
10738    .name = "MAILBOX_EXISTS",
10739    .read = acf_mailbox_exists,
10740 };
10741 
10742 static int vmauthenticate(struct ast_channel *chan, const char *data)
10743 {
10744    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
10745    struct ast_vm_user vmus;
10746    char *options = NULL;
10747    int silent = 0, skipuser = 0;
10748    int res = -1;
10749    
10750    if (data) {
10751       s = ast_strdupa(data);
10752       user = strsep(&s, ",");
10753       options = strsep(&s, ",");
10754       if (user) {
10755          s = user;
10756          user = strsep(&s, "@");
10757          context = strsep(&s, "");
10758          if (!ast_strlen_zero(user))
10759             skipuser++;
10760          ast_copy_string(mailbox, user, sizeof(mailbox));
10761       }
10762    }
10763 
10764    if (options) {
10765       silent = (strchr(options, 's')) != NULL;
10766    }
10767 
10768    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10769       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10770       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10771       ast_play_and_wait(chan, "auth-thankyou");
10772       res = 0;
10773    } else if (mailbox[0] == '*') {
10774       /* user entered '*' */
10775       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10776          res = 0; /* prevent hangup */
10777       }
10778    }
10779 
10780    return res;
10781 }
10782 
10783 static char *show_users_realtime(int fd, const char *context)
10784 {
10785    struct ast_config *cfg;
10786    const char *cat = NULL;
10787 
10788    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10789       "context", context, SENTINEL))) {
10790       return CLI_FAILURE;
10791    }
10792 
10793    ast_cli(fd,
10794       "\n"
10795       "=============================================================\n"
10796       "=== Configured Voicemail Users ==============================\n"
10797       "=============================================================\n"
10798       "===\n");
10799 
10800    while ((cat = ast_category_browse(cfg, cat))) {
10801       struct ast_variable *var = NULL;
10802       ast_cli(fd,
10803          "=== Mailbox ...\n"
10804          "===\n");
10805       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10806          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10807       ast_cli(fd,
10808          "===\n"
10809          "=== ---------------------------------------------------------\n"
10810          "===\n");
10811    }
10812 
10813    ast_cli(fd,
10814       "=============================================================\n"
10815       "\n");
10816 
10817    ast_config_destroy(cfg);
10818 
10819    return CLI_SUCCESS;
10820 }
10821 
10822 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10823 {
10824    int which = 0;
10825    int wordlen;
10826    struct ast_vm_user *vmu;
10827    const char *context = "";
10828 
10829    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10830    if (pos > 4)
10831       return NULL;
10832    if (pos == 3)
10833       return (state == 0) ? ast_strdup("for") : NULL;
10834    wordlen = strlen(word);
10835    AST_LIST_TRAVERSE(&users, vmu, list) {
10836       if (!strncasecmp(word, vmu->context, wordlen)) {
10837          if (context && strcmp(context, vmu->context) && ++which > state)
10838             return ast_strdup(vmu->context);
10839          /* ignore repeated contexts ? */
10840          context = vmu->context;
10841       }
10842    }
10843    return NULL;
10844 }
10845 
10846 /*! \brief Show a list of voicemail users in the CLI */
10847 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10848 {
10849    struct ast_vm_user *vmu;
10850 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10851    const char *context = NULL;
10852    int users_counter = 0;
10853 
10854    switch (cmd) {
10855    case CLI_INIT:
10856       e->command = "voicemail show users";
10857       e->usage =
10858          "Usage: voicemail show users [for <context>]\n"
10859          "       Lists all mailboxes currently set up\n";
10860       return NULL;
10861    case CLI_GENERATE:
10862       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10863    }  
10864 
10865    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10866       return CLI_SHOWUSAGE;
10867    if (a->argc == 5) {
10868       if (strcmp(a->argv[3],"for"))
10869          return CLI_SHOWUSAGE;
10870       context = a->argv[4];
10871    }
10872 
10873    if (ast_check_realtime("voicemail")) {
10874       if (!context) {
10875          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10876          return CLI_SHOWUSAGE;
10877       }
10878       return show_users_realtime(a->fd, context);
10879    }
10880 
10881    AST_LIST_LOCK(&users);
10882    if (AST_LIST_EMPTY(&users)) {
10883       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10884       AST_LIST_UNLOCK(&users);
10885       return CLI_FAILURE;
10886    }
10887    if (a->argc == 3)
10888       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10889    else {
10890       int count = 0;
10891       AST_LIST_TRAVERSE(&users, vmu, list) {
10892          if (!strcmp(context, vmu->context))
10893             count++;
10894       }
10895       if (count) {
10896          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10897       } else {
10898          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10899          AST_LIST_UNLOCK(&users);
10900          return CLI_FAILURE;
10901       }
10902    }
10903    AST_LIST_TRAVERSE(&users, vmu, list) {
10904       int newmsgs = 0, oldmsgs = 0;
10905       char count[12], tmp[256] = "";
10906 
10907       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10908          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10909          inboxcount(tmp, &newmsgs, &oldmsgs);
10910          snprintf(count, sizeof(count), "%d", newmsgs);
10911          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10912          users_counter++;
10913       }
10914    }
10915    AST_LIST_UNLOCK(&users);
10916    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10917    return CLI_SUCCESS;
10918 }
10919 
10920 /*! \brief Show a list of voicemail zones in the CLI */
10921 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10922 {
10923    struct vm_zone *zone;
10924 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10925    char *res = CLI_SUCCESS;
10926 
10927    switch (cmd) {
10928    case CLI_INIT:
10929       e->command = "voicemail show zones";
10930       e->usage =
10931          "Usage: voicemail show zones\n"
10932          "       Lists zone message formats\n";
10933       return NULL;
10934    case CLI_GENERATE:
10935       return NULL;
10936    }
10937 
10938    if (a->argc != 3)
10939       return CLI_SHOWUSAGE;
10940 
10941    AST_LIST_LOCK(&zones);
10942    if (!AST_LIST_EMPTY(&zones)) {
10943       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10944       AST_LIST_TRAVERSE(&zones, zone, list) {
10945          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10946       }
10947    } else {
10948       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10949       res = CLI_FAILURE;
10950    }
10951    AST_LIST_UNLOCK(&zones);
10952 
10953    return res;
10954 }
10955 
10956 /*! \brief Reload voicemail configuration from the CLI */
10957 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10958 {
10959    switch (cmd) {
10960    case CLI_INIT:
10961       e->command = "voicemail reload";
10962       e->usage =
10963          "Usage: voicemail reload\n"
10964          "       Reload voicemail configuration\n";
10965       return NULL;
10966    case CLI_GENERATE:
10967       return NULL;
10968    }
10969 
10970    if (a->argc != 2)
10971       return CLI_SHOWUSAGE;
10972 
10973    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10974    load_config(1);
10975    
10976    return CLI_SUCCESS;
10977 }
10978 
10979 static struct ast_cli_entry cli_voicemail[] = {
10980    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10981    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10982    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10983 };
10984 
10985 #ifdef IMAP_STORAGE
10986    #define DATA_EXPORT_VM_USERS(USER)              \
10987       USER(ast_vm_user, context, AST_DATA_STRING)        \
10988       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
10989       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
10990       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
10991       USER(ast_vm_user, email, AST_DATA_STRING)       \
10992       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
10993       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
10994       USER(ast_vm_user, pager, AST_DATA_STRING)       \
10995       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
10996       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
10997       USER(ast_vm_user, language, AST_DATA_STRING)       \
10998       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
10999       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11000       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11001       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11002       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11003       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11004       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11005       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11006       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11007       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11008       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11009       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11010       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11011       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11012       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11013 #else
11014    #define DATA_EXPORT_VM_USERS(USER)              \
11015       USER(ast_vm_user, context, AST_DATA_STRING)        \
11016       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11017       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11018       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11019       USER(ast_vm_user, email, AST_DATA_STRING)       \
11020       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11021       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11022       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11023       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11024       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11025       USER(ast_vm_user, language, AST_DATA_STRING)       \
11026       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11027       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11028       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11029       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11030       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11031       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11032       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11033       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11034       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11035       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11036       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11037       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11038 #endif
11039 
11040 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11041 
11042 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11043    ZONE(vm_zone, name, AST_DATA_STRING)      \
11044    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11045    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11046 
11047 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11048 
11049 /*!
11050  * \internal
11051  * \brief Add voicemail user to the data_root.
11052  * \param[in] search The search tree.
11053  * \param[in] data_root The main result node.
11054  * \param[in] user The voicemail user.
11055  */
11056 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11057     struct ast_data *data_root, struct ast_vm_user *user)
11058 {
11059    struct ast_data *data_user, *data_zone;
11060    struct ast_data *data_state;
11061    struct vm_zone *zone = NULL;
11062    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11063    char ext_context[256] = "";
11064 
11065    data_user = ast_data_add_node(data_root, "user");
11066    if (!data_user) {
11067       return -1;
11068    }
11069 
11070    ast_data_add_structure(ast_vm_user, data_user, user);
11071 
11072    AST_LIST_LOCK(&zones);
11073    AST_LIST_TRAVERSE(&zones, zone, list) {
11074       if (!strcmp(zone->name, user->zonetag)) {
11075          break;
11076       }
11077    }
11078    AST_LIST_UNLOCK(&zones);
11079 
11080    /* state */
11081    data_state = ast_data_add_node(data_user, "state");
11082    if (!data_state) {
11083       return -1;
11084    }
11085    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11086    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11087    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11088    ast_data_add_int(data_state, "newmsg", newmsg);
11089    ast_data_add_int(data_state, "oldmsg", oldmsg);
11090 
11091    if (zone) {
11092       data_zone = ast_data_add_node(data_user, "zone");
11093       ast_data_add_structure(vm_zone, data_zone, zone);
11094    }
11095 
11096    if (!ast_data_search_match(search, data_user)) {
11097       ast_data_remove_node(data_root, data_user);
11098    }
11099 
11100    return 0;
11101 }
11102 
11103 static int vm_users_data_provider_get(const struct ast_data_search *search,
11104    struct ast_data *data_root)
11105 {
11106    struct ast_vm_user *user;
11107 
11108    AST_LIST_LOCK(&users);
11109    AST_LIST_TRAVERSE(&users, user, list) {
11110       vm_users_data_provider_get_helper(search, data_root, user);
11111    }
11112    AST_LIST_UNLOCK(&users);
11113 
11114    return 0;
11115 }
11116 
11117 static const struct ast_data_handler vm_users_data_provider = {
11118    .version = AST_DATA_HANDLER_VERSION,
11119    .get = vm_users_data_provider_get
11120 };
11121 
11122 static const struct ast_data_entry vm_data_providers[] = {
11123    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11124 };
11125 
11126 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11127 {
11128    int new = 0, old = 0, urgent = 0;
11129 
11130    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11131 
11132    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11133       mwi_sub->old_urgent = urgent;
11134       mwi_sub->old_new = new;
11135       mwi_sub->old_old = old;
11136       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11137       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11138    }
11139 }
11140 
11141 static void poll_subscribed_mailboxes(void)
11142 {
11143    struct mwi_sub *mwi_sub;
11144 
11145    AST_RWLIST_RDLOCK(&mwi_subs);
11146    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11147       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11148          poll_subscribed_mailbox(mwi_sub);
11149       }
11150    }
11151    AST_RWLIST_UNLOCK(&mwi_subs);
11152 }
11153 
11154 static void *mb_poll_thread(void *data)
11155 {
11156    while (poll_thread_run) {
11157       struct timespec ts = { 0, };
11158       struct timeval wait;
11159 
11160       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11161       ts.tv_sec = wait.tv_sec;
11162       ts.tv_nsec = wait.tv_usec * 1000;
11163 
11164       ast_mutex_lock(&poll_lock);
11165       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11166       ast_mutex_unlock(&poll_lock);
11167 
11168       if (!poll_thread_run)
11169          break;
11170 
11171       poll_subscribed_mailboxes();
11172    }
11173 
11174    return NULL;
11175 }
11176 
11177 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11178 {
11179    ast_free(mwi_sub);
11180 }
11181 
11182 static int handle_unsubscribe(void *datap)
11183 {
11184    struct mwi_sub *mwi_sub;
11185    uint32_t *uniqueid = datap;
11186    
11187    AST_RWLIST_WRLOCK(&mwi_subs);
11188    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11189       if (mwi_sub->uniqueid == *uniqueid) {
11190          AST_LIST_REMOVE_CURRENT(entry);
11191          break;
11192       }
11193    }
11194    AST_RWLIST_TRAVERSE_SAFE_END
11195    AST_RWLIST_UNLOCK(&mwi_subs);
11196 
11197    if (mwi_sub)
11198       mwi_sub_destroy(mwi_sub);
11199 
11200    ast_free(uniqueid);  
11201    return 0;
11202 }
11203 
11204 static int handle_subscribe(void *datap)
11205 {
11206    unsigned int len;
11207    struct mwi_sub *mwi_sub;
11208    struct mwi_sub_task *p = datap;
11209 
11210    len = sizeof(*mwi_sub);
11211    if (!ast_strlen_zero(p->mailbox))
11212       len += strlen(p->mailbox);
11213 
11214    if (!ast_strlen_zero(p->context))
11215       len += strlen(p->context) + 1; /* Allow for seperator */
11216 
11217    if (!(mwi_sub = ast_calloc(1, len)))
11218       return -1;
11219 
11220    mwi_sub->uniqueid = p->uniqueid;
11221    if (!ast_strlen_zero(p->mailbox))
11222       strcpy(mwi_sub->mailbox, p->mailbox);
11223 
11224    if (!ast_strlen_zero(p->context)) {
11225       strcat(mwi_sub->mailbox, "@");
11226       strcat(mwi_sub->mailbox, p->context);
11227    }
11228 
11229    AST_RWLIST_WRLOCK(&mwi_subs);
11230    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11231    AST_RWLIST_UNLOCK(&mwi_subs);
11232    ast_free((void *) p->mailbox);
11233    ast_free((void *) p->context);
11234    ast_free(p);
11235    poll_subscribed_mailbox(mwi_sub);
11236    return 0;
11237 }
11238 
11239 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11240 {
11241    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11242    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11243       return;
11244 
11245    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11246       return;
11247 
11248    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11249    *uniqueid = u;
11250    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11251       ast_free(uniqueid);
11252    }
11253 }
11254 
11255 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11256 {
11257    struct mwi_sub_task *mwist;
11258    
11259    if (ast_event_get_type(event) != AST_EVENT_SUB)
11260       return;
11261 
11262    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11263       return;
11264 
11265    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11266       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11267       return;
11268    }
11269    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11270    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11271    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11272    
11273    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11274       ast_free(mwist);
11275    }
11276 }
11277 
11278 static void start_poll_thread(void)
11279 {
11280    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11281       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11282       AST_EVENT_IE_END);
11283 
11284    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11285       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11286       AST_EVENT_IE_END);
11287 
11288    if (mwi_sub_sub)
11289       ast_event_report_subs(mwi_sub_sub);
11290 
11291    poll_thread_run = 1;
11292 
11293    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11294 }
11295 
11296 static void stop_poll_thread(void)
11297 {
11298    poll_thread_run = 0;
11299 
11300    if (mwi_sub_sub) {
11301       ast_event_unsubscribe(mwi_sub_sub);
11302       mwi_sub_sub = NULL;
11303    }
11304 
11305    if (mwi_unsub_sub) {
11306       ast_event_unsubscribe(mwi_unsub_sub);
11307       mwi_unsub_sub = NULL;
11308    }
11309 
11310    ast_mutex_lock(&poll_lock);
11311    ast_cond_signal(&poll_cond);
11312    ast_mutex_unlock(&poll_lock);
11313 
11314    pthread_join(poll_thread, NULL);
11315 
11316    poll_thread = AST_PTHREADT_NULL;
11317 }
11318 
11319 /*! \brief Manager list voicemail users command */
11320 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11321 {
11322    struct ast_vm_user *vmu = NULL;
11323    const char *id = astman_get_header(m, "ActionID");
11324    char actionid[128] = "";
11325 
11326    if (!ast_strlen_zero(id))
11327       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11328 
11329    AST_LIST_LOCK(&users);
11330 
11331    if (AST_LIST_EMPTY(&users)) {
11332       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11333       AST_LIST_UNLOCK(&users);
11334       return RESULT_SUCCESS;
11335    }
11336    
11337    astman_send_ack(s, m, "Voicemail user list will follow");
11338    
11339    AST_LIST_TRAVERSE(&users, vmu, list) {
11340       char dirname[256];
11341 
11342 #ifdef IMAP_STORAGE
11343       int new, old;
11344       inboxcount(vmu->mailbox, &new, &old);
11345 #endif
11346       
11347       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11348       astman_append(s,
11349          "%s"
11350          "Event: VoicemailUserEntry\r\n"
11351          "VMContext: %s\r\n"
11352          "VoiceMailbox: %s\r\n"
11353          "Fullname: %s\r\n"
11354          "Email: %s\r\n"
11355          "Pager: %s\r\n"
11356          "ServerEmail: %s\r\n"
11357          "MailCommand: %s\r\n"
11358          "Language: %s\r\n"
11359          "TimeZone: %s\r\n"
11360          "Callback: %s\r\n"
11361          "Dialout: %s\r\n"
11362          "UniqueID: %s\r\n"
11363          "ExitContext: %s\r\n"
11364          "SayDurationMinimum: %d\r\n"
11365          "SayEnvelope: %s\r\n"
11366          "SayCID: %s\r\n"
11367          "AttachMessage: %s\r\n"
11368          "AttachmentFormat: %s\r\n"
11369          "DeleteMessage: %s\r\n"
11370          "VolumeGain: %.2f\r\n"
11371          "CanReview: %s\r\n"
11372          "CallOperator: %s\r\n"
11373          "MaxMessageCount: %d\r\n"
11374          "MaxMessageLength: %d\r\n"
11375          "NewMessageCount: %d\r\n"
11376 #ifdef IMAP_STORAGE
11377          "OldMessageCount: %d\r\n"
11378          "IMAPUser: %s\r\n"
11379 #endif
11380          "\r\n",
11381          actionid,
11382          vmu->context,
11383          vmu->mailbox,
11384          vmu->fullname,
11385          vmu->email,
11386          vmu->pager,
11387          vmu->serveremail,
11388          vmu->mailcmd,
11389          vmu->language,
11390          vmu->zonetag,
11391          vmu->callback,
11392          vmu->dialout,
11393          vmu->uniqueid,
11394          vmu->exit,
11395          vmu->saydurationm,
11396          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11397          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11398          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11399          vmu->attachfmt,
11400          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11401          vmu->volgain,
11402          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11403          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11404          vmu->maxmsg,
11405          vmu->maxsecs,
11406 #ifdef IMAP_STORAGE
11407          new, old, vmu->imapuser
11408 #else
11409          count_messages(vmu, dirname)
11410 #endif
11411          );
11412    }     
11413    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11414 
11415    AST_LIST_UNLOCK(&users);
11416 
11417    return RESULT_SUCCESS;
11418 }
11419 
11420 /*! \brief Free the users structure. */
11421 static void free_vm_users(void) 
11422 {
11423    struct ast_vm_user *current;
11424    AST_LIST_LOCK(&users);
11425    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11426       ast_set_flag(current, VM_ALLOCED);
11427       free_user(current);
11428    }
11429    AST_LIST_UNLOCK(&users);
11430 }
11431 
11432 /*! \brief Free the zones structure. */
11433 static void free_vm_zones(void)
11434 {
11435    struct vm_zone *zcur;
11436    AST_LIST_LOCK(&zones);
11437    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11438       free_zone(zcur);
11439    AST_LIST_UNLOCK(&zones);
11440 }
11441 
11442 static const char *substitute_escapes(const char *value)
11443 {
11444    char *current;
11445 
11446    /* Add 16 for fudge factor */
11447    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11448 
11449    ast_str_reset(str);
11450    
11451    /* Substitute strings \r, \n, and \t into the appropriate characters */
11452    for (current = (char *) value; *current; current++) {
11453       if (*current == '\\') {
11454          current++;
11455          if (!*current) {
11456             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11457             break;
11458          }
11459          switch (*current) {
11460          case 'r':
11461             ast_str_append(&str, 0, "\r");
11462             break;
11463          case 'n':
11464 #ifdef IMAP_STORAGE
11465             if (!str->used || str->str[str->used - 1] != '\r') {
11466                ast_str_append(&str, 0, "\r");
11467             }
11468 #endif
11469             ast_str_append(&str, 0, "\n");
11470             break;
11471          case 't':
11472             ast_str_append(&str, 0, "\t");
11473             break;
11474          default:
11475             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11476             break;
11477          }
11478       } else {
11479          ast_str_append(&str, 0, "%c", *current);
11480       }
11481    }
11482 
11483    return ast_str_buffer(str);
11484 }
11485 
11486 static int load_config(int reload)
11487 {
11488    struct ast_vm_user *current;
11489    struct ast_config *cfg, *ucfg;
11490    char *cat;
11491    struct ast_variable *var;
11492    const char *val;
11493    char *q, *stringp, *tmp;
11494    int x;
11495    int tmpadsi[4];
11496    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11497    char secretfn[PATH_MAX] = "";
11498 
11499    ast_unload_realtime("voicemail");
11500    ast_unload_realtime("voicemail_data");
11501 
11502    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11503       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11504          return 0;
11505       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11506          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11507          ucfg = NULL;
11508       }
11509       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11510       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11511          ast_config_destroy(ucfg);
11512          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11513          return 0;
11514       }
11515    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11516       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11517       return 0;
11518    } else {
11519       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11520       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11521          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11522          ucfg = NULL;
11523       }
11524    }
11525 #ifdef IMAP_STORAGE
11526    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11527 #endif
11528    /* set audio control prompts */
11529    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11530    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11531    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11532    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11533    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11534 
11535    /* Free all the users structure */  
11536    free_vm_users();
11537 
11538    /* Free all the zones structure */
11539    free_vm_zones();
11540 
11541    AST_LIST_LOCK(&users);  
11542 
11543    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11544    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11545 
11546    if (cfg) {
11547       /* General settings */
11548 
11549       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11550          val = "default";
11551       ast_copy_string(userscontext, val, sizeof(userscontext));
11552       /* Attach voice message to mail message ? */
11553       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11554          val = "yes";
11555       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11556 
11557       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11558          val = "no";
11559       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11560 
11561       volgain = 0.0;
11562       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11563          sscanf(val, "%30lf", &volgain);
11564 
11565 #ifdef ODBC_STORAGE
11566       strcpy(odbc_database, "asterisk");
11567       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11568          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11569       }
11570       strcpy(odbc_table, "voicemessages");
11571       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11572          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11573       }
11574 #endif      
11575       /* Mail command */
11576       strcpy(mailcmd, SENDMAIL);
11577       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11578          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11579 
11580       maxsilence = 0;
11581       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11582          maxsilence = atoi(val);
11583          if (maxsilence > 0)
11584             maxsilence *= 1000;
11585       }
11586       
11587       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11588          maxmsg = MAXMSG;
11589       } else {
11590          maxmsg = atoi(val);
11591          if (maxmsg < 0) {
11592             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11593             maxmsg = MAXMSG;
11594          } else if (maxmsg > MAXMSGLIMIT) {
11595             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11596             maxmsg = MAXMSGLIMIT;
11597          }
11598       }
11599 
11600       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11601          maxdeletedmsg = 0;
11602       } else {
11603          if (sscanf(val, "%30d", &x) == 1)
11604             maxdeletedmsg = x;
11605          else if (ast_true(val))
11606             maxdeletedmsg = MAXMSG;
11607          else
11608             maxdeletedmsg = 0;
11609 
11610          if (maxdeletedmsg < 0) {
11611             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11612             maxdeletedmsg = MAXMSG;
11613          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11614             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11615             maxdeletedmsg = MAXMSGLIMIT;
11616          }
11617       }
11618 
11619       /* Load date format config for voicemail mail */
11620       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11621          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11622       }
11623 
11624       /* Load date format config for voicemail pager mail */
11625       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11626          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11627       }
11628 
11629       /* External password changing command */
11630       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11631          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11632          pwdchange = PWDCHANGE_EXTERNAL;
11633       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11634          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11635          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11636       }
11637  
11638       /* External password validation command */
11639       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11640          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11641          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11642       }
11643 
11644 #ifdef IMAP_STORAGE
11645       /* IMAP server address */
11646       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11647          ast_copy_string(imapserver, val, sizeof(imapserver));
11648       } else {
11649          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11650       }
11651       /* IMAP server port */
11652       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11653          ast_copy_string(imapport, val, sizeof(imapport));
11654       } else {
11655          ast_copy_string(imapport, "143", sizeof(imapport));
11656       }
11657       /* IMAP server flags */
11658       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11659          ast_copy_string(imapflags, val, sizeof(imapflags));
11660       }
11661       /* IMAP server master username */
11662       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11663          ast_copy_string(authuser, val, sizeof(authuser));
11664       }
11665       /* IMAP server master password */
11666       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11667          ast_copy_string(authpassword, val, sizeof(authpassword));
11668       }
11669       /* Expunge on exit */
11670       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11671          if (ast_false(val))
11672             expungeonhangup = 0;
11673          else
11674             expungeonhangup = 1;
11675       } else {
11676          expungeonhangup = 1;
11677       }
11678       /* IMAP voicemail folder */
11679       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11680          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11681       } else {
11682          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11683       }
11684       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11685          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11686       }
11687       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11688          imapgreetings = ast_true(val);
11689       } else {
11690          imapgreetings = 0;
11691       }
11692       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11693          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11694       } else {
11695          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11696       }
11697 
11698       /* There is some very unorthodox casting done here. This is due
11699        * to the way c-client handles the argument passed in. It expects a 
11700        * void pointer and casts the pointer directly to a long without
11701        * first dereferencing it. */
11702       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11703          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11704       } else {
11705          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11706       }
11707 
11708       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11709          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11710       } else {
11711          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11712       }
11713 
11714       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11715          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11716       } else {
11717          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11718       }
11719 
11720       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11721          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11722       } else {
11723          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11724       }
11725 
11726       /* Increment configuration version */
11727       imapversion++;
11728 #endif
11729       /* External voicemail notify application */
11730       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11731          ast_copy_string(externnotify, val, sizeof(externnotify));
11732          ast_debug(1, "found externnotify: %s\n", externnotify);
11733       } else {
11734          externnotify[0] = '\0';
11735       }
11736 
11737       /* SMDI voicemail notification */
11738       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11739          ast_debug(1, "Enabled SMDI voicemail notification\n");
11740          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11741             smdi_iface = ast_smdi_interface_find(val);
11742          } else {
11743             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11744             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
11745          }
11746          if (!smdi_iface) {
11747             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11748          } 
11749       }
11750 
11751       /* Silence treshold */
11752       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11753       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11754          silencethreshold = atoi(val);
11755       
11756       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11757          val = ASTERISK_USERNAME;
11758       ast_copy_string(serveremail, val, sizeof(serveremail));
11759       
11760       vmmaxsecs = 0;
11761       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11762          if (sscanf(val, "%30d", &x) == 1) {
11763             vmmaxsecs = x;
11764          } else {
11765             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11766          }
11767       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11768          static int maxmessage_deprecate = 0;
11769          if (maxmessage_deprecate == 0) {
11770             maxmessage_deprecate = 1;
11771             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11772          }
11773          if (sscanf(val, "%30d", &x) == 1) {
11774             vmmaxsecs = x;
11775          } else {
11776             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11777          }
11778       }
11779 
11780       vmminsecs = 0;
11781       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11782          if (sscanf(val, "%30d", &x) == 1) {
11783             vmminsecs = x;
11784             if (maxsilence / 1000 >= vmminsecs) {
11785                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11786             }
11787          } else {
11788             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11789          }
11790       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11791          static int maxmessage_deprecate = 0;
11792          if (maxmessage_deprecate == 0) {
11793             maxmessage_deprecate = 1;
11794             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11795          }
11796          if (sscanf(val, "%30d", &x) == 1) {
11797             vmminsecs = x;
11798             if (maxsilence / 1000 >= vmminsecs) {
11799                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11800             }
11801          } else {
11802             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11803          }
11804       }
11805 
11806       val = ast_variable_retrieve(cfg, "general", "format");
11807       if (!val) {
11808          val = "wav";   
11809       } else {
11810          tmp = ast_strdupa(val);
11811          val = ast_format_str_reduce(tmp);
11812          if (!val) {
11813             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11814             val = "wav";
11815          }
11816       }
11817       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11818 
11819       skipms = 3000;
11820       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11821          if (sscanf(val, "%30d", &x) == 1) {
11822             maxgreet = x;
11823          } else {
11824             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11825          }
11826       }
11827 
11828       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11829          if (sscanf(val, "%30d", &x) == 1) {
11830             skipms = x;
11831          } else {
11832             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11833          }
11834       }
11835 
11836       maxlogins = 3;
11837       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11838          if (sscanf(val, "%30d", &x) == 1) {
11839             maxlogins = x;
11840          } else {
11841             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11842          }
11843       }
11844 
11845       minpassword = MINPASSWORD;
11846       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11847          if (sscanf(val, "%30d", &x) == 1) {
11848             minpassword = x;
11849          } else {
11850             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11851          }
11852       }
11853 
11854       /* Force new user to record name ? */
11855       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11856          val = "no";
11857       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11858 
11859       /* Force new user to record greetings ? */
11860       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11861          val = "no";
11862       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11863 
11864       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11865          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11866          stringp = ast_strdupa(val);
11867          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11868             if (!ast_strlen_zero(stringp)) {
11869                q = strsep(&stringp, ",");
11870                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11871                   q++;
11872                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11873                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11874             } else {
11875                cidinternalcontexts[x][0] = '\0';
11876             }
11877          }
11878       }
11879       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11880          ast_debug(1, "VM Review Option disabled globally\n");
11881          val = "no";
11882       }
11883       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11884 
11885       /* Temporary greeting reminder */
11886       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11887          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11888          val = "no";
11889       } else {
11890          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11891       }
11892       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11893       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11894          ast_debug(1, "VM next message wrap disabled globally\n");
11895          val = "no";
11896       }
11897       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11898 
11899       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11900          ast_debug(1, "VM Operator break disabled globally\n");
11901          val = "no";
11902       }
11903       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11904 
11905       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11906          ast_debug(1, "VM CID Info before msg disabled globally\n");
11907          val = "no";
11908       } 
11909       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11910 
11911       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
11912          ast_debug(1, "Send Voicemail msg disabled globally\n");
11913          val = "no";
11914       }
11915       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11916    
11917       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11918          ast_debug(1, "ENVELOPE before msg enabled globally\n");
11919          val = "yes";
11920       }
11921       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11922 
11923       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11924          ast_debug(1, "Move Heard enabled globally\n");
11925          val = "yes";
11926       }
11927       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11928 
11929       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11930          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11931          val = "no";
11932       }
11933       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11934 
11935       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11936          ast_debug(1, "Duration info before msg enabled globally\n");
11937          val = "yes";
11938       }
11939       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11940 
11941       saydurationminfo = 2;
11942       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11943          if (sscanf(val, "%30d", &x) == 1) {
11944             saydurationminfo = x;
11945          } else {
11946             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11947          }
11948       }
11949 
11950       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11951          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
11952          val = "no";
11953       }
11954       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11955 
11956       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11957          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11958          ast_debug(1, "found dialout context: %s\n", dialcontext);
11959       } else {
11960          dialcontext[0] = '\0';  
11961       }
11962       
11963       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11964          ast_copy_string(callcontext, val, sizeof(callcontext));
11965          ast_debug(1, "found callback context: %s\n", callcontext);
11966       } else {
11967          callcontext[0] = '\0';
11968       }
11969 
11970       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11971          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11972          ast_debug(1, "found operator context: %s\n", exitcontext);
11973       } else {
11974          exitcontext[0] = '\0';
11975       }
11976       
11977       /* load password sounds configuration */
11978       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11979          ast_copy_string(vm_password, val, sizeof(vm_password));
11980       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11981          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11982       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11983          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11984       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11985          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11986       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11987          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11988       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11989          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11990       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11991          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11992       }
11993       /* load configurable audio prompts */
11994       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11995          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11996       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11997          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11998       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11999          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12000       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12001          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12002       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12003          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12004 
12005       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12006          val = "no";
12007       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12008 
12009       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12010          val = "voicemail.conf";
12011       }
12012       if (!(strcmp(val, "spooldir"))) {
12013          passwordlocation = OPT_PWLOC_SPOOLDIR;
12014       } else {
12015          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12016       }
12017 
12018       poll_freq = DEFAULT_POLL_FREQ;
12019       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12020          if (sscanf(val, "%30u", &poll_freq) != 1) {
12021             poll_freq = DEFAULT_POLL_FREQ;
12022             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12023          }
12024       }
12025 
12026       poll_mailboxes = 0;
12027       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12028          poll_mailboxes = ast_true(val);
12029 
12030       if (ucfg) { 
12031          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12032             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12033                continue;
12034             if ((current = find_or_create(userscontext, cat))) {
12035                populate_defaults(current);
12036                apply_options_full(current, ast_variable_browse(ucfg, cat));
12037                ast_copy_string(current->context, userscontext, sizeof(current->context));
12038                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12039                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12040                }
12041 
12042                switch (current->passwordlocation) {
12043                case OPT_PWLOC_SPOOLDIR:
12044                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12045                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12046                }
12047             }
12048          }
12049          ast_config_destroy(ucfg);
12050       }
12051       cat = ast_category_browse(cfg, NULL);
12052       while (cat) {
12053          if (strcasecmp(cat, "general")) {
12054             var = ast_variable_browse(cfg, cat);
12055             if (strcasecmp(cat, "zonemessages")) {
12056                /* Process mailboxes in this context */
12057                while (var) {
12058                   append_mailbox(cat, var->name, var->value);
12059                   var = var->next;
12060                }
12061             } else {
12062                /* Timezones in this context */
12063                while (var) {
12064                   struct vm_zone *z;
12065                   if ((z = ast_malloc(sizeof(*z)))) {
12066                      char *msg_format, *tzone;
12067                      msg_format = ast_strdupa(var->value);
12068                      tzone = strsep(&msg_format, "|,");
12069                      if (msg_format) {
12070                         ast_copy_string(z->name, var->name, sizeof(z->name));
12071                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12072                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12073                         AST_LIST_LOCK(&zones);
12074                         AST_LIST_INSERT_HEAD(&zones, z, list);
12075                         AST_LIST_UNLOCK(&zones);
12076                      } else {
12077                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12078                         ast_free(z);
12079                      }
12080                   } else {
12081                      AST_LIST_UNLOCK(&users);
12082                      ast_config_destroy(cfg);
12083                      return -1;
12084                   }
12085                   var = var->next;
12086                }
12087             }
12088          }
12089          cat = ast_category_browse(cfg, cat);
12090       }
12091       memset(fromstring, 0, sizeof(fromstring));
12092       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12093       strcpy(charset, "ISO-8859-1");
12094       if (emailbody) {
12095          ast_free(emailbody);
12096          emailbody = NULL;
12097       }
12098       if (emailsubject) {
12099          ast_free(emailsubject);
12100          emailsubject = NULL;
12101       }
12102       if (pagerbody) {
12103          ast_free(pagerbody);
12104          pagerbody = NULL;
12105       }
12106       if (pagersubject) {
12107          ast_free(pagersubject);
12108          pagersubject = NULL;
12109       }
12110       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12111          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12112       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12113          ast_copy_string(fromstring, val, sizeof(fromstring));
12114       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12115          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12116       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12117          ast_copy_string(charset, val, sizeof(charset));
12118       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12119          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12120          for (x = 0; x < 4; x++) {
12121             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12122          }
12123       }
12124       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12125          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12126          for (x = 0; x < 4; x++) {
12127             memcpy(&adsisec[x], &tmpadsi[x], 1);
12128          }
12129       }
12130       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12131          if (atoi(val)) {
12132             adsiver = atoi(val);
12133          }
12134       }
12135       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12136          ast_copy_string(zonetag, val, sizeof(zonetag));
12137       }
12138       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12139          ast_copy_string(locale, val, sizeof(locale));
12140       }
12141       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12142          emailsubject = ast_strdup(val);
12143       }
12144       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12145          emailbody = ast_strdup(substitute_escapes(val));
12146       }
12147       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12148          pagersubject = ast_strdup(val);
12149       }
12150       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12151          pagerbody = ast_strdup(substitute_escapes(val));
12152       }
12153       AST_LIST_UNLOCK(&users);
12154       ast_config_destroy(cfg);
12155 
12156       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12157          start_poll_thread();
12158       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12159          stop_poll_thread();;
12160 
12161       return 0;
12162    } else {
12163       AST_LIST_UNLOCK(&users);
12164       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12165       if (ucfg)
12166          ast_config_destroy(ucfg);
12167       return 0;
12168    }
12169 }
12170 
12171 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12172 {
12173    int res = -1;
12174    char dir[PATH_MAX];
12175    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12176    ast_debug(2, "About to try retrieving name file %s\n", dir);
12177    RETRIEVE(dir, -1, mailbox, context);
12178    if (ast_fileexists(dir, NULL, NULL)) {
12179       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12180    }
12181    DISPOSE(dir, -1);
12182    return res;
12183 }
12184 
12185 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12186    struct ast_config *pwconf;
12187    struct ast_flags config_flags = { 0 };
12188 
12189    pwconf = ast_config_load(secretfn, config_flags);
12190    if (pwconf) {
12191       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12192       if (val) {
12193          ast_copy_string(password, val, passwordlen);
12194          return;
12195       }
12196    }
12197    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12198 }
12199 
12200 static int write_password_to_file(const char *secretfn, const char *password) {
12201    struct ast_config *conf;
12202    struct ast_category *cat;
12203    struct ast_variable *var;
12204 
12205    if (!(conf=ast_config_new())) {
12206       ast_log(LOG_ERROR, "Error creating new config structure\n");
12207       return -1;
12208    }
12209    if (!(cat=ast_category_new("general","",1))) {
12210       ast_log(LOG_ERROR, "Error creating new category structure\n");
12211       return -1;
12212    }
12213    if (!(var=ast_variable_new("password",password,""))) {
12214       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12215       return -1;
12216    }
12217    ast_category_append(conf,cat);
12218    ast_variable_append(cat,var);
12219    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12220       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12221       return -1;
12222    }
12223    return 0;
12224 }
12225 
12226 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12227 {
12228    char *context;
12229    char *args_copy;
12230    int res;
12231 
12232    if (ast_strlen_zero(data)) {
12233       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12234       return -1;
12235    }
12236 
12237    args_copy = ast_strdupa(data);
12238    if ((context = strchr(args_copy, '@'))) {
12239       *context++ = '\0';
12240    } else {
12241       context = "default";
12242    }
12243 
12244    if ((res = sayname(chan, args_copy, context) < 0)) {
12245       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12246       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12247       if (!res) {
12248          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12249       }
12250    }
12251 
12252    return res;
12253 }
12254 
12255 #ifdef TEST_FRAMEWORK
12256 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12257 {
12258    return 0;
12259 }
12260 
12261 static struct ast_frame *fake_read(struct ast_channel *ast)
12262 {
12263    return &ast_null_frame;
12264 }
12265 
12266 AST_TEST_DEFINE(test_voicemail_vmsayname)
12267 {
12268    char dir[PATH_MAX];
12269    char dir2[PATH_MAX];
12270    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12271    static const char TEST_EXTENSION[] = "1234";
12272 
12273    struct ast_channel *test_channel1 = NULL;
12274    int res = -1;
12275 
12276    static const struct ast_channel_tech fake_tech = {
12277       .write = fake_write,
12278       .read = fake_read,
12279    };
12280 
12281    switch (cmd) {
12282    case TEST_INIT:
12283       info->name = "vmsayname_exec";
12284       info->category = "/apps/app_voicemail/";
12285       info->summary = "Vmsayname unit test";
12286       info->description =
12287          "This tests passing various parameters to vmsayname";
12288       return AST_TEST_NOT_RUN;
12289    case TEST_EXECUTE:
12290       break;
12291    }
12292 
12293    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12294         NULL, NULL, 0, 0, "TestChannel1"))) {
12295       goto exit_vmsayname_test;
12296    }
12297 
12298    /* normally this is done in the channel driver */
12299    test_channel1->nativeformats = AST_FORMAT_GSM;
12300    test_channel1->writeformat = AST_FORMAT_GSM;
12301    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12302    test_channel1->readformat = AST_FORMAT_GSM;
12303    test_channel1->rawreadformat = AST_FORMAT_GSM;
12304    test_channel1->tech = &fake_tech;
12305 
12306    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12307    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12308    if (!(res = vmsayname_exec(test_channel1, dir))) {
12309       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12310       if (ast_fileexists(dir, NULL, NULL)) {
12311          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12312          res = -1;
12313          goto exit_vmsayname_test;
12314       } else {
12315          /* no greeting already exists as expected, let's create one to fully test sayname */
12316          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12317             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12318             goto exit_vmsayname_test;
12319          }
12320          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12321          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12322          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12323          if ((res = symlink(dir, dir2))) {
12324             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12325             goto exit_vmsayname_test;
12326          }
12327          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12328          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12329          res = vmsayname_exec(test_channel1, dir);
12330 
12331          /* TODO: there may be a better way to do this */
12332          unlink(dir2);
12333          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12334          rmdir(dir2);
12335          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12336          rmdir(dir2);
12337       }
12338    }
12339 
12340 exit_vmsayname_test:
12341 
12342    if (test_channel1) {
12343       ast_hangup(test_channel1);
12344    }
12345 
12346    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12347 }
12348 
12349 AST_TEST_DEFINE(test_voicemail_msgcount)
12350 {
12351    int i, j, res = AST_TEST_PASS, syserr;
12352    struct ast_vm_user *vmu;
12353    struct vm_state vms;
12354 #ifdef IMAP_STORAGE
12355    struct ast_channel *chan = NULL;
12356 #endif
12357    struct {
12358       char dir[256];
12359       char file[256];
12360       char txtfile[256];
12361    } tmp[3];
12362    char syscmd[256];
12363    const char origweasels[] = "tt-weasels";
12364    const char testcontext[] = "test";
12365    const char testmailbox[] = "00000000";
12366    const char testspec[] = "00000000@test";
12367    FILE *txt;
12368    int new, old, urgent;
12369    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12370    const int folder2mbox[3] = { 1, 11, 0 };
12371    const int expected_results[3][12] = {
12372       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12373       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12374       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12375       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12376    };
12377 
12378    switch (cmd) {
12379    case TEST_INIT:
12380       info->name = "test_voicemail_msgcount";
12381       info->category = "/apps/app_voicemail/";
12382       info->summary = "Test Voicemail status checks";
12383       info->description =
12384          "Verify that message counts are correct when retrieved through the public API";
12385       return AST_TEST_NOT_RUN;
12386    case TEST_EXECUTE:
12387       break;
12388    }
12389 
12390    /* Make sure the original path was completely empty */
12391    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12392    if ((syserr = ast_safe_system(syscmd))) {
12393       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12394          syserr > 0 ? strerror(syserr) : "unable to fork()");
12395       return AST_TEST_FAIL;
12396    }
12397 
12398 #ifdef IMAP_STORAGE
12399    if (!(chan = ast_dummy_channel_alloc())) {
12400       ast_test_status_update(test, "Unable to create dummy channel\n");
12401       return AST_TEST_FAIL;
12402    }
12403 #endif
12404 
12405    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12406       !(vmu = find_or_create(testcontext, testmailbox))) {
12407       ast_test_status_update(test, "Cannot create vmu structure\n");
12408       ast_unreplace_sigchld();
12409       return AST_TEST_FAIL;
12410    }
12411 
12412    populate_defaults(vmu);
12413    memset(&vms, 0, sizeof(vms));
12414 
12415    /* Create temporary voicemail */
12416    for (i = 0; i < 3; i++) {
12417       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12418       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12419       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12420 
12421       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12422          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12423             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12424          if ((syserr = ast_safe_system(syscmd))) {
12425             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12426                syserr > 0 ? strerror(syserr) : "unable to fork()");
12427             ast_unreplace_sigchld();
12428             return AST_TEST_FAIL;
12429          }
12430       }
12431 
12432       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12433          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12434          fclose(txt);
12435       } else {
12436          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12437          res = AST_TEST_FAIL;
12438          break;
12439       }
12440       open_mailbox(&vms, vmu, folder2mbox[i]);
12441       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12442 
12443       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12444       for (j = 0; j < 3; j++) {
12445          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12446          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12447             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12448                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12449             res = AST_TEST_FAIL;
12450          }
12451       }
12452 
12453       new = old = urgent = 0;
12454       if (ast_app_inboxcount(testspec, &new, &old)) {
12455          ast_test_status_update(test, "inboxcount returned failure\n");
12456          res = AST_TEST_FAIL;
12457       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12458          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12459             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12460          res = AST_TEST_FAIL;
12461       }
12462 
12463       new = old = urgent = 0;
12464       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12465          ast_test_status_update(test, "inboxcount2 returned failure\n");
12466          res = AST_TEST_FAIL;
12467       } else if (old != expected_results[i][6 + 0] ||
12468             urgent != expected_results[i][6 + 1] ||
12469                new != expected_results[i][6 + 2]    ) {
12470          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12471             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12472          res = AST_TEST_FAIL;
12473       }
12474 
12475       new = old = urgent = 0;
12476       for (j = 0; j < 3; j++) {
12477          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12478             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12479                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12480             res = AST_TEST_FAIL;
12481          }
12482       }
12483    }
12484 
12485    for (i = 0; i < 3; i++) {
12486       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12487        * server, in which case, the rm below will not affect the
12488        * voicemails. */
12489       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12490       DISPOSE(tmp[i].dir, 0);
12491    }
12492 
12493    if (vms.deleted) {
12494       ast_free(vms.deleted);
12495    }
12496    if (vms.heard) {
12497       ast_free(vms.heard);
12498    }
12499 
12500 #ifdef IMAP_STORAGE
12501    chan = ast_channel_release(chan);
12502 #endif
12503 
12504    /* And remove test directory */
12505    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12506    if ((syserr = ast_safe_system(syscmd))) {
12507       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12508          syserr > 0 ? strerror(syserr) : "unable to fork()");
12509    }
12510 
12511    return res;
12512 }
12513 
12514 AST_TEST_DEFINE(test_voicemail_notify_endl)
12515 {
12516    int res = AST_TEST_PASS;
12517    char testcontext[] = "test";
12518    char testmailbox[] = "00000000";
12519    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12520    char attach[256], attach2[256];
12521    char buf[256] = ""; /* No line should actually be longer than 80 */
12522    struct ast_channel *chan = NULL;
12523    struct ast_vm_user *vmu, vmus = {
12524       .flags = 0,
12525    };
12526    FILE *file;
12527    struct {
12528       char *name;
12529       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12530       void *location;
12531       union {
12532          int intval;
12533          char *strval;
12534       } u;
12535    } test_items[] = {
12536       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12537       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12538       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12539       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12540       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12541       { "attach2", STRPTR, attach2, .u.strval = "" },
12542       { "attach", STRPTR, attach, .u.strval = "" },
12543    };
12544    int which;
12545 
12546    switch (cmd) {
12547    case TEST_INIT:
12548       info->name = "test_voicemail_notify_endl";
12549       info->category = "/apps/app_voicemail/";
12550       info->summary = "Test Voicemail notification end-of-line";
12551       info->description =
12552          "Verify that notification emails use a consistent end-of-line character";
12553       return AST_TEST_NOT_RUN;
12554    case TEST_EXECUTE:
12555       break;
12556    }
12557 
12558    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12559    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12560 
12561    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12562       !(vmu = find_or_create(testcontext, testmailbox))) {
12563       ast_test_status_update(test, "Cannot create vmu structure\n");
12564       return AST_TEST_NOT_RUN;
12565    }
12566 
12567    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12568       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12569       return AST_TEST_NOT_RUN;
12570    }
12571 
12572    populate_defaults(vmu);
12573    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12574 #ifdef IMAP_STORAGE
12575    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12576 #endif
12577 
12578    file = tmpfile();
12579    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12580       /* Kill previous test, if any */
12581       rewind(file);
12582       if (ftruncate(fileno(file), 0)) {
12583          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12584          res = AST_TEST_FAIL;
12585          break;
12586       }
12587 
12588       /* Make each change, in order, to the test mailbox */
12589       if (test_items[which].type == INT) {
12590          *((int *) test_items[which].location) = test_items[which].u.intval;
12591       } else if (test_items[which].type == FLAGVAL) {
12592          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12593             ast_clear_flag(vmu, test_items[which].u.intval);
12594          } else {
12595             ast_set_flag(vmu, test_items[which].u.intval);
12596          }
12597       } else if (test_items[which].type == STATIC) {
12598          strcpy(test_items[which].location, test_items[which].u.strval);
12599       } else if (test_items[which].type == STRPTR) {
12600          test_items[which].location = test_items[which].u.strval;
12601       }
12602 
12603       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12604       rewind(file);
12605       while (fgets(buf, sizeof(buf), file)) {
12606          if (
12607 #ifdef IMAP_STORAGE
12608          buf[strlen(buf) - 2] != '\r'
12609 #else
12610          buf[strlen(buf) - 2] == '\r'
12611 #endif
12612          || buf[strlen(buf) - 1] != '\n') {
12613             res = AST_TEST_FAIL;
12614          }
12615       }
12616    }
12617    fclose(file);
12618    return res;
12619 }
12620 #endif /* defined(TEST_FRAMEWORK) */
12621 
12622 static int reload(void)
12623 {
12624    return load_config(1);
12625 }
12626 
12627 static int unload_module(void)
12628 {
12629    int res;
12630 
12631    res = ast_unregister_application(app);
12632    res |= ast_unregister_application(app2);
12633    res |= ast_unregister_application(app3);
12634    res |= ast_unregister_application(app4);
12635    res |= ast_unregister_application(sayname_app);
12636    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12637    res |= ast_manager_unregister("VoicemailUsersList");
12638    res |= ast_data_unregister(NULL);
12639 #ifdef TEST_FRAMEWORK
12640    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12641    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12642    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12643    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12644 #endif
12645    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12646    ast_uninstall_vm_functions();
12647    ao2_ref(inprocess_container, -1);
12648 
12649    if (poll_thread != AST_PTHREADT_NULL)
12650       stop_poll_thread();
12651 
12652    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12653    ast_unload_realtime("voicemail");
12654    ast_unload_realtime("voicemail_data");
12655 
12656    free_vm_users();
12657    free_vm_zones();
12658    return res;
12659 }
12660 
12661 static int load_module(void)
12662 {
12663    int res;
12664    my_umask = umask(0);
12665    umask(my_umask);
12666 
12667    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12668       return AST_MODULE_LOAD_DECLINE;
12669    }
12670 
12671    /* compute the location of the voicemail spool directory */
12672    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12673    
12674    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12675       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12676    }
12677 
12678    if ((res = load_config(0)))
12679       return res;
12680 
12681    res = ast_register_application_xml(app, vm_exec);
12682    res |= ast_register_application_xml(app2, vm_execmain);
12683    res |= ast_register_application_xml(app3, vm_box_exists);
12684    res |= ast_register_application_xml(app4, vmauthenticate);
12685    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12686    res |= ast_custom_function_register(&mailbox_exists_acf);
12687    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12688 #ifdef TEST_FRAMEWORK
12689    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12690    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12691    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12692    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12693 #endif
12694 
12695    if (res)
12696       return res;
12697 
12698    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12699    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12700 
12701    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12702    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12703    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12704 
12705    return res;
12706 }
12707 
12708 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
12709 {
12710    int cmd = 0;
12711    char destination[80] = "";
12712    int retries = 0;
12713 
12714    if (!num) {
12715       ast_verb(3, "Destination number will be entered manually\n");
12716       while (retries < 3 && cmd != 't') {
12717          destination[1] = '\0';
12718          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
12719          if (!cmd)
12720             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
12721          if (!cmd)
12722             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
12723          if (!cmd) {
12724             cmd = ast_waitfordigit(chan, 6000);
12725             if (cmd)
12726                destination[0] = cmd;
12727          }
12728          if (!cmd) {
12729             retries++;
12730          } else {
12731 
12732             if (cmd < 0)
12733                return 0;
12734             if (cmd == '*') {
12735                ast_verb(3, "User hit '*' to cancel outgoing call\n");
12736                return 0;
12737             }
12738             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
12739                retries++;
12740             else
12741                cmd = 't';
12742          }
12743       }
12744       if (retries >= 3) {
12745          return 0;
12746       }
12747       
12748    } else {
12749       if (option_verbose > 2)
12750          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
12751       ast_copy_string(destination, num, sizeof(destination));
12752    }
12753 
12754    if (!ast_strlen_zero(destination)) {
12755       if (destination[strlen(destination) -1 ] == '*')
12756          return 0; 
12757       if (option_verbose > 2)
12758          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
12759       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
12760       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
12761       chan->priority = 0;
12762       return 9;
12763    }
12764    return 0;
12765 }
12766 
12767 /*!
12768  * \brief The advanced options within a message.
12769  * \param chan
12770  * \param vmu 
12771  * \param vms
12772  * \param msg
12773  * \param option
12774  * \param record_gain
12775  *
12776  * Provides handling for the play message envelope, call the person back, or reply to message. 
12777  *
12778  * \return zero on success, -1 on error.
12779  */
12780 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)
12781 {
12782    int res = 0;
12783    char filename[PATH_MAX];
12784    struct ast_config *msg_cfg = NULL;
12785    const char *origtime, *context;
12786    char *name, *num;
12787    int retries = 0;
12788    char *cid;
12789    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
12790 
12791    vms->starting = 0; 
12792 
12793    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12794 
12795    /* Retrieve info from VM attribute file */
12796    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
12797    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
12798    msg_cfg = ast_config_load(filename, config_flags);
12799    DISPOSE(vms->curdir, vms->curmsg);
12800    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
12801       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
12802       return 0;
12803    }
12804 
12805    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
12806       ast_config_destroy(msg_cfg);
12807       return 0;
12808    }
12809 
12810    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
12811 
12812    context = ast_variable_retrieve(msg_cfg, "message", "context");
12813    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
12814       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
12815    switch (option) {
12816    case 3: /* Play message envelope */
12817       if (!res)
12818          res = play_message_datetime(chan, vmu, origtime, filename);
12819       if (!res)
12820          res = play_message_callerid(chan, vms, cid, context, 0);
12821 
12822       res = 't';
12823       break;
12824 
12825    case 2:  /* Call back */
12826 
12827       if (ast_strlen_zero(cid))
12828          break;
12829 
12830       ast_callerid_parse(cid, &name, &num);
12831       while ((res > -1) && (res != 't')) {
12832          switch (res) {
12833          case '1':
12834             if (num) {
12835                /* Dial the CID number */
12836                res = dialout(chan, vmu, num, vmu->callback);
12837                if (res) {
12838                   ast_config_destroy(msg_cfg);
12839                   return 9;
12840                }
12841             } else {
12842                res = '2';
12843             }
12844             break;
12845 
12846          case '2':
12847             /* Want to enter a different number, can only do this if there's a dialout context for this user */
12848             if (!ast_strlen_zero(vmu->dialout)) {
12849                res = dialout(chan, vmu, NULL, vmu->dialout);
12850                if (res) {
12851                   ast_config_destroy(msg_cfg);
12852                   return 9;
12853                }
12854             } else {
12855                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
12856                res = ast_play_and_wait(chan, "vm-sorry");
12857             }
12858             ast_config_destroy(msg_cfg);
12859             return res;
12860          case '*':
12861             res = 't';
12862             break;
12863          case '3':
12864          case '4':
12865          case '5':
12866          case '6':
12867          case '7':
12868          case '8':
12869          case '9':
12870          case '0':
12871 
12872             res = ast_play_and_wait(chan, "vm-sorry");
12873             retries++;
12874             break;
12875          default:
12876             if (num) {
12877                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
12878                res = ast_play_and_wait(chan, "vm-num-i-have");
12879                if (!res)
12880                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
12881                if (!res)
12882                   res = ast_play_and_wait(chan, "vm-tocallnum");
12883                /* Only prompt for a caller-specified number if there is a dialout context specified */
12884                if (!ast_strlen_zero(vmu->dialout)) {
12885                   if (!res)
12886                      res = ast_play_and_wait(chan, "vm-calldiffnum");
12887                }
12888             } else {
12889                res = ast_play_and_wait(chan, "vm-nonumber");
12890                if (!ast_strlen_zero(vmu->dialout)) {
12891                   if (!res)
12892                      res = ast_play_and_wait(chan, "vm-toenternumber");
12893                }
12894             }
12895             if (!res)
12896                res = ast_play_and_wait(chan, "vm-star-cancel");
12897             if (!res)
12898                res = ast_waitfordigit(chan, 6000);
12899             if (!res) {
12900                retries++;
12901                if (retries > 3)
12902                   res = 't';
12903             }
12904             break; 
12905             
12906          }
12907          if (res == 't')
12908             res = 0;
12909          else if (res == '*')
12910             res = -1;
12911       }
12912       break;
12913       
12914    case 1:  /* Reply */
12915       /* Send reply directly to sender */
12916       if (ast_strlen_zero(cid))
12917          break;
12918 
12919       ast_callerid_parse(cid, &name, &num);
12920       if (!num) {
12921          ast_verb(3, "No CID number available, no reply sent\n");
12922          if (!res)
12923             res = ast_play_and_wait(chan, "vm-nonumber");
12924          ast_config_destroy(msg_cfg);
12925          return res;
12926       } else {
12927          struct ast_vm_user vmu2;
12928          if (find_user(&vmu2, vmu->context, num)) {
12929             struct leave_vm_options leave_options;
12930             char mailbox[AST_MAX_EXTENSION * 2 + 2];
12931             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
12932 
12933             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
12934             
12935             memset(&leave_options, 0, sizeof(leave_options));
12936             leave_options.record_gain = record_gain;
12937             res = leave_voicemail(chan, mailbox, &leave_options);
12938             if (!res)
12939                res = 't';
12940             ast_config_destroy(msg_cfg);
12941             return res;
12942          } else {
12943             /* Sender has no mailbox, can't reply */
12944             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
12945             ast_play_and_wait(chan, "vm-nobox");
12946             res = 't';
12947             ast_config_destroy(msg_cfg);
12948             return res;
12949          }
12950       } 
12951       res = 0;
12952 
12953       break;
12954    }
12955 
12956 #ifndef IMAP_STORAGE
12957    ast_config_destroy(msg_cfg);
12958 
12959    if (!res) {
12960       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12961       vms->heard[msg] = 1;
12962       res = wait_file(chan, vms, vms->fn);
12963    }
12964 #endif
12965    return res;
12966 }
12967 
12968 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
12969          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
12970          signed char record_gain, struct vm_state *vms, char *flag)
12971 {
12972    /* Record message & let caller review or re-record it, or set options if applicable */
12973    int res = 0;
12974    int cmd = 0;
12975    int max_attempts = 3;
12976    int attempts = 0;
12977    int recorded = 0;
12978    int msg_exists = 0;
12979    signed char zero_gain = 0;
12980    char tempfile[PATH_MAX];
12981    char *acceptdtmf = "#";
12982    char *canceldtmf = "";
12983    int canceleddtmf = 0;
12984 
12985    /* Note that urgent and private are for flagging messages as such in the future */
12986 
12987    /* barf if no pointer passed to store duration in */
12988    if (duration == NULL) {
12989       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
12990       return -1;
12991    }
12992 
12993    if (!outsidecaller)
12994       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
12995    else
12996       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
12997 
12998    cmd = '3';  /* Want to start by recording */
12999 
13000    while ((cmd >= 0) && (cmd != 't')) {
13001       switch (cmd) {
13002       case '1':
13003          if (!msg_exists) {
13004             /* In this case, 1 is to record a message */
13005             cmd = '3';
13006             break;
13007          } else {
13008             /* Otherwise 1 is to save the existing message */
13009             ast_verb(3, "Saving message as is\n");
13010             if (!outsidecaller) 
13011                ast_filerename(tempfile, recordfile, NULL);
13012             ast_stream_and_wait(chan, "vm-msgsaved", "");
13013             if (!outsidecaller) {
13014                /* Saves to IMAP server only if imapgreeting=yes */
13015                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13016                DISPOSE(recordfile, -1);
13017             }
13018             cmd = 't';
13019             return res;
13020          }
13021       case '2':
13022          /* Review */
13023          ast_verb(3, "Reviewing the message\n");
13024          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13025          break;
13026       case '3':
13027          msg_exists = 0;
13028          /* Record */
13029          if (recorded == 1) 
13030             ast_verb(3, "Re-recording the message\n");
13031          else  
13032             ast_verb(3, "Recording the message\n");
13033          
13034          if (recorded && outsidecaller) {
13035             cmd = ast_play_and_wait(chan, INTRO);
13036             cmd = ast_play_and_wait(chan, "beep");
13037          }
13038          recorded = 1;
13039          /* 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 */
13040          if (record_gain)
13041             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13042          if (ast_test_flag(vmu, VM_OPERATOR))
13043             canceldtmf = "0";
13044          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13045          if (strchr(canceldtmf, cmd)) {
13046          /* need this flag here to distinguish between pressing '0' during message recording or after */
13047             canceleddtmf = 1;
13048          }
13049          if (record_gain)
13050             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13051          if (cmd == -1) {
13052             /* User has hung up, no options to give */
13053             if (!outsidecaller) {
13054                /* user was recording a greeting and they hung up, so let's delete the recording. */
13055                ast_filedelete(tempfile, NULL);
13056             }     
13057             return cmd;
13058          }
13059          if (cmd == '0') {
13060             break;
13061          } else if (cmd == '*') {
13062             break;
13063 #if 0
13064          } else if (vmu->review && (*duration < 5)) {
13065             /* Message is too short */
13066             ast_verb(3, "Message too short\n");
13067             cmd = ast_play_and_wait(chan, "vm-tooshort");
13068             cmd = ast_filedelete(tempfile, NULL);
13069             break;
13070          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13071             /* Message is all silence */
13072             ast_verb(3, "Nothing recorded\n");
13073             cmd = ast_filedelete(tempfile, NULL);
13074             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13075             if (!cmd)
13076                cmd = ast_play_and_wait(chan, "vm-speakup");
13077             break;
13078 #endif
13079          } else {
13080             /* If all is well, a message exists */
13081             msg_exists = 1;
13082             cmd = 0;
13083          }
13084          break;
13085       case '4':
13086          if (outsidecaller) {  /* only mark vm messages */
13087             /* Mark Urgent */
13088             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13089                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13090                res = ast_play_and_wait(chan, "vm-marked-urgent");
13091                strcpy(flag, "Urgent");
13092             } else if (flag) {
13093                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13094                res = ast_play_and_wait(chan, "vm-urgent-removed");
13095                strcpy(flag, "");
13096             } else {
13097                ast_play_and_wait(chan, "vm-sorry");
13098             }
13099             cmd = 0;
13100          } else {
13101             cmd = ast_play_and_wait(chan, "vm-sorry");
13102          }
13103          break;
13104       case '5':
13105       case '6':
13106       case '7':
13107       case '8':
13108       case '9':
13109       case '*':
13110       case '#':
13111          cmd = ast_play_and_wait(chan, "vm-sorry");
13112          break;
13113 #if 0 
13114 /*  XXX Commented out for the moment because of the dangers of deleting
13115     a message while recording (can put the message numbers out of sync) */
13116       case '*':
13117          /* Cancel recording, delete message, offer to take another message*/
13118          cmd = ast_play_and_wait(chan, "vm-deleted");
13119          cmd = ast_filedelete(tempfile, NULL);
13120          if (outsidecaller) {
13121             res = vm_exec(chan, NULL);
13122             return res;
13123          }
13124          else
13125             return 1;
13126 #endif
13127       case '0':
13128          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13129             cmd = ast_play_and_wait(chan, "vm-sorry");
13130             break;
13131          }
13132          if (msg_exists || recorded) {
13133             cmd = ast_play_and_wait(chan, "vm-saveoper");
13134             if (!cmd)
13135                cmd = ast_waitfordigit(chan, 3000);
13136             if (cmd == '1') {
13137                ast_filerename(tempfile, recordfile, NULL);
13138                ast_play_and_wait(chan, "vm-msgsaved");
13139                cmd = '0';
13140             } else if (cmd == '4') {
13141                if (flag) {
13142                   ast_play_and_wait(chan, "vm-marked-urgent");
13143                   strcpy(flag, "Urgent");
13144                }
13145                ast_play_and_wait(chan, "vm-msgsaved");
13146                cmd = '0';
13147             } else {
13148                ast_play_and_wait(chan, "vm-deleted");
13149                DELETE(tempfile, -1, tempfile, vmu);
13150                cmd = '0';
13151             }
13152          }
13153          return cmd;
13154       default:
13155          /* If the caller is an ouside caller, and the review option is enabled,
13156             allow them to review the message, but let the owner of the box review
13157             their OGM's */
13158          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13159             return cmd;
13160          if (msg_exists) {
13161             cmd = ast_play_and_wait(chan, "vm-review");
13162             if (!cmd && outsidecaller) {
13163                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13164                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13165                } else if (flag) {
13166                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13167                }
13168             }
13169          } else {
13170             cmd = ast_play_and_wait(chan, "vm-torerecord");
13171             if (!cmd)
13172                cmd = ast_waitfordigit(chan, 600);
13173          }
13174          
13175          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13176             cmd = ast_play_and_wait(chan, "vm-reachoper");
13177             if (!cmd)
13178                cmd = ast_waitfordigit(chan, 600);
13179          }
13180 #if 0
13181          if (!cmd)
13182             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13183 #endif
13184          if (!cmd)
13185             cmd = ast_waitfordigit(chan, 6000);
13186          if (!cmd) {
13187             attempts++;
13188          }
13189          if (attempts > max_attempts) {
13190             cmd = 't';
13191          }
13192       }
13193    }
13194    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13195       /* Hang up or timeout, so delete the recording. */
13196       ast_filedelete(tempfile, NULL);
13197    }
13198 
13199    if (cmd != 't' && outsidecaller)
13200       ast_play_and_wait(chan, "vm-goodbye");
13201 
13202    return cmd;
13203 }
13204 
13205 /* This is a workaround so that menuselect displays a proper description
13206  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13207  */
13208 
13209 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13210       .load = load_module,
13211       .unload = unload_module,
13212       .reload = reload,
13213       .nonoptreq = "res_adsi,res_smdi",
13214       );

Generated on Wed Apr 6 11:29:39 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7