Mon Jun 27 16:50:47 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, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043  ***/
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: 306967 $")
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    char extension[4];
03920    int stopcount = 0;
03921 
03922    /* Reading the entire directory into a file map scales better than
03923     * doing a stat repeatedly on a predicted sequence.  I suspect this
03924     * is partially due to stat(2) internally doing a readdir(2) itself to
03925     * find each file. */
03926    if (!(msgdir = opendir(dir))) {
03927       return -1;
03928    }
03929 
03930    while ((msgdirent = readdir(msgdir))) {
03931       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
03932          map[msgdirint] = 1;
03933          stopcount++;
03934          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
03935       }
03936    }
03937    closedir(msgdir);
03938 
03939    for (x = 0; x < vmu->maxmsg; x++) {
03940       if (map[x] == 1) {
03941          stopcount--;
03942       } else if (map[x] == 0 && !stopcount) {
03943          break;
03944       }
03945    }
03946 
03947    return x - 1;
03948 }
03949 
03950 #endif /* #ifndef IMAP_STORAGE */
03951 #endif /* #else of #ifdef ODBC_STORAGE */
03952 #ifndef IMAP_STORAGE
03953 /*!
03954  * \brief Utility function to copy a file.
03955  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03956  * \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.
03957  *
03958  * 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.
03959  * The copy operation copies up to 4096 bytes at once.
03960  *
03961  * \return zero on success, -1 on error.
03962  */
03963 static int copy(char *infile, char *outfile)
03964 {
03965    int ifd;
03966    int ofd;
03967    int res;
03968    int len;
03969    char buf[4096];
03970 
03971 #ifdef HARDLINK_WHEN_POSSIBLE
03972    /* Hard link if possible; saves disk space & is faster */
03973    if (link(infile, outfile)) {
03974 #endif
03975       if ((ifd = open(infile, O_RDONLY)) < 0) {
03976          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03977          return -1;
03978       }
03979       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03980          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03981          close(ifd);
03982          return -1;
03983       }
03984       do {
03985          len = read(ifd, buf, sizeof(buf));
03986          if (len < 0) {
03987             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03988             close(ifd);
03989             close(ofd);
03990             unlink(outfile);
03991          }
03992          if (len) {
03993             res = write(ofd, buf, len);
03994             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03995                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03996                close(ifd);
03997                close(ofd);
03998                unlink(outfile);
03999             }
04000          }
04001       } while (len);
04002       close(ifd);
04003       close(ofd);
04004       return 0;
04005 #ifdef HARDLINK_WHEN_POSSIBLE
04006    } else {
04007       /* Hard link succeeded */
04008       return 0;
04009    }
04010 #endif
04011 }
04012 
04013 /*!
04014  * \brief Copies a voicemail information (envelope) file.
04015  * \param frompath
04016  * \param topath 
04017  *
04018  * Every voicemail has the data (.wav) file, and the information file.
04019  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04020  * This is used by the COPY macro when not using IMAP storage.
04021  */
04022 static void copy_plain_file(char *frompath, char *topath)
04023 {
04024    char frompath2[PATH_MAX], topath2[PATH_MAX];
04025    struct ast_variable *tmp,*var = NULL;
04026    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04027    ast_filecopy(frompath, topath, NULL);
04028    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04029    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04030    if (ast_check_realtime("voicemail_data")) {
04031       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04032       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04033       for (tmp = var; tmp; tmp = tmp->next) {
04034          if (!strcasecmp(tmp->name, "origmailbox")) {
04035             origmailbox = tmp->value;
04036          } else if (!strcasecmp(tmp->name, "context")) {
04037             context = tmp->value;
04038          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04039             macrocontext = tmp->value;
04040          } else if (!strcasecmp(tmp->name, "exten")) {
04041             exten = tmp->value;
04042          } else if (!strcasecmp(tmp->name, "priority")) {
04043             priority = tmp->value;
04044          } else if (!strcasecmp(tmp->name, "callerchan")) {
04045             callerchan = tmp->value;
04046          } else if (!strcasecmp(tmp->name, "callerid")) {
04047             callerid = tmp->value;
04048          } else if (!strcasecmp(tmp->name, "origdate")) {
04049             origdate = tmp->value;
04050          } else if (!strcasecmp(tmp->name, "origtime")) {
04051             origtime = tmp->value;
04052          } else if (!strcasecmp(tmp->name, "category")) {
04053             category = tmp->value;
04054          } else if (!strcasecmp(tmp->name, "duration")) {
04055             duration = tmp->value;
04056          }
04057       }
04058       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);
04059    }
04060    copy(frompath2, topath2);
04061    ast_variables_destroy(var);
04062 }
04063 #endif
04064 
04065 /*! 
04066  * \brief Removes the voicemail sound and information file.
04067  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04068  *
04069  * This is used by the DELETE macro when voicemails are stored on the file system.
04070  *
04071  * \return zero on success, -1 on error.
04072  */
04073 static int vm_delete(char *file)
04074 {
04075    char *txt;
04076    int txtsize = 0;
04077 
04078    txtsize = (strlen(file) + 5)*sizeof(char);
04079    txt = alloca(txtsize);
04080    /* Sprintf here would safe because we alloca'd exactly the right length,
04081     * but trying to eliminate all sprintf's anyhow
04082     */
04083    if (ast_check_realtime("voicemail_data")) {
04084       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04085    }
04086    snprintf(txt, txtsize, "%s.txt", file);
04087    unlink(txt);
04088    return ast_filedelete(file, NULL);
04089 }
04090 
04091 /*!
04092  * \brief utility used by inchar(), for base_encode()
04093  */
04094 static int inbuf(struct baseio *bio, FILE *fi)
04095 {
04096    int l;
04097 
04098    if (bio->ateof)
04099       return 0;
04100 
04101    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04102       if (ferror(fi))
04103          return -1;
04104 
04105       bio->ateof = 1;
04106       return 0;
04107    }
04108 
04109    bio->iolen = l;
04110    bio->iocp = 0;
04111 
04112    return 1;
04113 }
04114 
04115 /*!
04116  * \brief utility used by base_encode()
04117  */
04118 static int inchar(struct baseio *bio, FILE *fi)
04119 {
04120    if (bio->iocp>=bio->iolen) {
04121       if (!inbuf(bio, fi))
04122          return EOF;
04123    }
04124 
04125    return bio->iobuf[bio->iocp++];
04126 }
04127 
04128 /*!
04129  * \brief utility used by base_encode()
04130  */
04131 static int ochar(struct baseio *bio, int c, FILE *so)
04132 {
04133    if (bio->linelength >= BASELINELEN) {
04134       if (fputs(ENDL, so) == EOF) {
04135          return -1;
04136       }
04137 
04138       bio->linelength = 0;
04139    }
04140 
04141    if (putc(((unsigned char) c), so) == EOF) {
04142       return -1;
04143    }
04144 
04145    bio->linelength++;
04146 
04147    return 1;
04148 }
04149 
04150 /*!
04151  * \brief Performs a base 64 encode algorithm on the contents of a File
04152  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04153  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04154  *
04155  * 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 ?
04156  *
04157  * \return zero on success, -1 on error.
04158  */
04159 static int base_encode(char *filename, FILE *so)
04160 {
04161    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04162       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04163       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04164       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04165    int i, hiteof = 0;
04166    FILE *fi;
04167    struct baseio bio;
04168 
04169    memset(&bio, 0, sizeof(bio));
04170    bio.iocp = BASEMAXINLINE;
04171 
04172    if (!(fi = fopen(filename, "rb"))) {
04173       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04174       return -1;
04175    }
04176 
04177    while (!hiteof){
04178       unsigned char igroup[3], ogroup[4];
04179       int c, n;
04180 
04181       memset(igroup, 0, sizeof(igroup));
04182 
04183       for (n = 0; n < 3; n++) {
04184          if ((c = inchar(&bio, fi)) == EOF) {
04185             hiteof = 1;
04186             break;
04187          }
04188 
04189          igroup[n] = (unsigned char) c;
04190       }
04191 
04192       if (n > 0) {
04193          ogroup[0]= dtable[igroup[0] >> 2];
04194          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04195          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04196          ogroup[3]= dtable[igroup[2] & 0x3F];
04197 
04198          if (n < 3) {
04199             ogroup[3] = '=';
04200 
04201             if (n < 2)
04202                ogroup[2] = '=';
04203          }
04204 
04205          for (i = 0; i < 4; i++)
04206             ochar(&bio, ogroup[i], so);
04207       }
04208    }
04209 
04210    fclose(fi);
04211    
04212    if (fputs(ENDL, so) == EOF) {
04213       return 0;
04214    }
04215 
04216    return 1;
04217 }
04218 
04219 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)
04220 {
04221    char callerid[256];
04222    char num[12];
04223    char fromdir[256], fromfile[256];
04224    struct ast_config *msg_cfg;
04225    const char *origcallerid, *origtime;
04226    char origcidname[80], origcidnum[80], origdate[80];
04227    int inttime;
04228    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04229 
04230    /* Prepare variables for substitution in email body and subject */
04231    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04232    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04233    snprintf(num, sizeof(num), "%d", msgnum);
04234    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04235    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04236    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04237    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04238       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04239    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04240    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04241    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04242    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04243    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04244 
04245    /* Retrieve info from VM attribute file */
04246    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04247    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04248    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04249       strcat(fromfile, ".txt");
04250    }
04251    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04252       if (option_debug > 0) {
04253          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04254       }
04255       return;
04256    }
04257 
04258    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04259       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04260       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04261       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04262       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04263    }
04264 
04265    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04266       struct timeval tv = { inttime, };
04267       struct ast_tm tm;
04268       ast_localtime(&tv, &tm, NULL);
04269       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04270       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04271    }
04272    ast_config_destroy(msg_cfg);
04273 }
04274 
04275 /*!
04276  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04277  * \param from The string to work with.
04278  * \param buf The buffer into which to write the modified quoted string.
04279  * \param maxlen Always zero, but see \see ast_str
04280  * 
04281  * \return The destination string with quotes wrapped on it (the to field).
04282  */
04283 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04284 {
04285    const char *ptr;
04286 
04287    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04288    ast_str_set(buf, maxlen, "\"");
04289    for (ptr = from; *ptr; ptr++) {
04290       if (*ptr == '"' || *ptr == '\\') {
04291          ast_str_append(buf, maxlen, "\\%c", *ptr);
04292       } else {
04293          ast_str_append(buf, maxlen, "%c", *ptr);
04294       }
04295    }
04296    ast_str_append(buf, maxlen, "\"");
04297 
04298    return ast_str_buffer(*buf);
04299 }
04300 
04301 /*! \brief
04302  * fill in *tm for current time according to the proper timezone, if any.
04303  * \return tm so it can be used as a function argument.
04304  */
04305 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04306 {
04307    const struct vm_zone *z = NULL;
04308    struct timeval t = ast_tvnow();
04309 
04310    /* Does this user have a timezone specified? */
04311    if (!ast_strlen_zero(vmu->zonetag)) {
04312       /* Find the zone in the list */
04313       AST_LIST_LOCK(&zones);
04314       AST_LIST_TRAVERSE(&zones, z, list) {
04315          if (!strcmp(z->name, vmu->zonetag))
04316             break;
04317       }
04318       AST_LIST_UNLOCK(&zones);
04319    }
04320    ast_localtime(&t, tm, z ? z->timezone : NULL);
04321    return tm;
04322 }
04323 
04324 /*!\brief Check if the string would need encoding within the MIME standard, to
04325  * avoid confusing certain mail software that expects messages to be 7-bit
04326  * clean.
04327  */
04328 static int check_mime(const char *str)
04329 {
04330    for (; *str; str++) {
04331       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04332          return 1;
04333       }
04334    }
04335    return 0;
04336 }
04337 
04338 /*!\brief Encode a string according to the MIME rules for encoding strings
04339  * that are not 7-bit clean or contain control characters.
04340  *
04341  * Additionally, if the encoded string would exceed the MIME limit of 76
04342  * characters per line, then the encoding will be broken up into multiple
04343  * sections, separated by a space character, in order to facilitate
04344  * breaking up the associated header across multiple lines.
04345  *
04346  * \param end An expandable buffer for holding the result
04347  * \param maxlen Always zero, but see \see ast_str
04348  * \param start A string to be encoded
04349  * \param preamble The length of the first line already used for this string,
04350  * to ensure that each line maintains a maximum length of 76 chars.
04351  * \param postamble the length of any additional characters appended to the
04352  * line, used to ensure proper field wrapping.
04353  * \retval The encoded string.
04354  */
04355 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04356 {
04357    struct ast_str *tmp = ast_str_alloca(80);
04358    int first_section = 1;
04359 
04360    ast_str_reset(*end);
04361    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04362    for (; *start; start++) {
04363       int need_encoding = 0;
04364       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04365          need_encoding = 1;
04366       }
04367       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04368          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04369          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04370          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04371          /* Start new line */
04372          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04373          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04374          first_section = 0;
04375       }
04376       if (need_encoding && *start == ' ') {
04377          ast_str_append(&tmp, -1, "_");
04378       } else if (need_encoding) {
04379          ast_str_append(&tmp, -1, "=%hhX", *start);
04380       } else {
04381          ast_str_append(&tmp, -1, "%c", *start);
04382       }
04383    }
04384    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04385    return ast_str_buffer(*end);
04386 }
04387 
04388 /*!
04389  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04390  * \param p The output file to generate the email contents into.
04391  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04392  * \param vmu The voicemail user who is sending the voicemail.
04393  * \param msgnum The message index in the mailbox folder.
04394  * \param context 
04395  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04396  * \param fromfolder
04397  * \param cidnum The caller ID number.
04398  * \param cidname The caller ID name.
04399  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04400  * \param attach2 
04401  * \param format The message sound file format. i.e. .wav
04402  * \param duration The time of the message content, in seconds.
04403  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04404  * \param chan
04405  * \param category
04406  * \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.
04407  * \param flag
04408  *
04409  * 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.
04410  */
04411 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)
04412 {
04413    char date[256];
04414    char host[MAXHOSTNAMELEN] = "";
04415    char who[256];
04416    char bound[256];
04417    char dur[256];
04418    struct ast_tm tm;
04419    char enc_cidnum[256] = "", enc_cidname[256] = "";
04420    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04421    char *greeting_attachment; 
04422    char filename[256];
04423 
04424    if (!str1 || !str2) {
04425       ast_free(str1);
04426       ast_free(str2);
04427       return;
04428    }
04429 
04430    if (cidnum) {
04431       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04432    }
04433    if (cidname) {
04434       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04435    }
04436    gethostname(host, sizeof(host) - 1);
04437 
04438    if (strchr(srcemail, '@')) {
04439       ast_copy_string(who, srcemail, sizeof(who));
04440    } else {
04441       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04442    }
04443 
04444    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04445    if (greeting_attachment) {
04446       *greeting_attachment++ = '\0';
04447    }
04448 
04449    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04450    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04451    fprintf(p, "Date: %s" ENDL, date);
04452 
04453    /* Set date format for voicemail mail */
04454    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04455 
04456    if (!ast_strlen_zero(fromstring)) {
04457       struct ast_channel *ast;
04458       if ((ast = ast_dummy_channel_alloc())) {
04459          char *ptr;
04460          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04461          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04462 
04463          if (check_mime(ast_str_buffer(str1))) {
04464             int first_line = 1;
04465             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04466             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04467                *ptr = '\0';
04468                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04469                first_line = 0;
04470                /* Substring is smaller, so this will never grow */
04471                ast_str_set(&str2, 0, "%s", ptr + 1);
04472             }
04473             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04474          } else {
04475             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04476          }
04477          ast = ast_channel_release(ast);
04478       } else {
04479          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04480       }
04481    } else {
04482       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04483    }
04484 
04485    if (check_mime(vmu->fullname)) {
04486       int first_line = 1;
04487       char *ptr;
04488       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04489       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04490          *ptr = '\0';
04491          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04492          first_line = 0;
04493          /* Substring is smaller, so this will never grow */
04494          ast_str_set(&str2, 0, "%s", ptr + 1);
04495       }
04496       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04497    } else {
04498       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04499    }
04500 
04501    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04502       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04503       struct ast_channel *ast;
04504       if ((ast = ast_dummy_channel_alloc())) {
04505          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04506          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04507          if (check_mime(ast_str_buffer(str1))) {
04508             int first_line = 1;
04509             char *ptr;
04510             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04511             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04512                *ptr = '\0';
04513                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04514                first_line = 0;
04515                /* Substring is smaller, so this will never grow */
04516                ast_str_set(&str2, 0, "%s", ptr + 1);
04517             }
04518             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04519          } else {
04520             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04521          }
04522          ast = ast_channel_release(ast);
04523       } else {
04524          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04525       }
04526    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04527       if (ast_strlen_zero(flag)) {
04528          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04529       } else {
04530          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04531       }
04532    } else {
04533       if (ast_strlen_zero(flag)) {
04534          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04535       } else {
04536          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04537       }
04538    }
04539 
04540    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04541       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04542    if (imap) {
04543       /* additional information needed for IMAP searching */
04544       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04545       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04546       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04547       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04548 #ifdef IMAP_STORAGE
04549       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04550 #else
04551       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04552 #endif
04553       /* flag added for Urgent */
04554       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04555       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04556       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04557       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04558       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04559       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04560       if (!ast_strlen_zero(category)) {
04561          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04562       } else {
04563          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04564       }
04565       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04566       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04567       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04568    }
04569    if (!ast_strlen_zero(cidnum)) {
04570       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04571    }
04572    if (!ast_strlen_zero(cidname)) {
04573       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04574    }
04575    fprintf(p, "MIME-Version: 1.0" ENDL);
04576    if (attach_user_voicemail) {
04577       /* Something unique. */
04578       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04579          (int) getpid(), (unsigned int) ast_random());
04580 
04581       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04582       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04583       fprintf(p, "--%s" ENDL, bound);
04584    }
04585    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04586    if (emailbody || vmu->emailbody) {
04587       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04588       struct ast_channel *ast;
04589       if ((ast = ast_dummy_channel_alloc())) {
04590          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04591          ast_str_substitute_variables(&str1, 0, ast, e_body);
04592 #ifdef IMAP_STORAGE
04593             {
04594                /* Convert body to native line terminators for IMAP backend */
04595                char *line = ast_str_buffer(str1), *next;
04596                do {
04597                   /* Terminate line before outputting it to the file */
04598                   if ((next = strchr(line, '\n'))) {
04599                      *next++ = '\0';
04600                   }
04601                   fprintf(p, "%s" ENDL, line);
04602                   line = next;
04603                } while (!ast_strlen_zero(line));
04604             }
04605 #else
04606          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04607 #endif
04608          ast = ast_channel_release(ast);
04609       } else {
04610          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04611       }
04612    } else if (msgnum > -1) {
04613       if (strcmp(vmu->mailbox, mailbox)) {
04614          /* Forwarded type */
04615          struct ast_config *msg_cfg;
04616          const char *v;
04617          int inttime;
04618          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04619          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04620          /* Retrieve info from VM attribute file */
04621          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04622          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04623          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04624             strcat(fromfile, ".txt");
04625          }
04626          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04627             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04628                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04629             }
04630 
04631             /* You might be tempted to do origdate, except that a) it's in the wrong
04632              * format, and b) it's missing for IMAP recordings. */
04633             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04634                struct timeval tv = { inttime, };
04635                struct ast_tm tm;
04636                ast_localtime(&tv, &tm, NULL);
04637                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04638             }
04639             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04640                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04641                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04642                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04643                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04644                date, origcallerid, origdate);
04645             ast_config_destroy(msg_cfg);
04646          } else {
04647             goto plain_message;
04648          }
04649       } else {
04650 plain_message:
04651          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04652             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04653             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04654             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04655             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04656       }
04657    } else {
04658       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04659             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04660    }
04661 
04662    if (imap || attach_user_voicemail) {
04663       if (!ast_strlen_zero(attach2)) {
04664          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04665          ast_debug(5, "creating second attachment filename %s\n", filename);
04666          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04667          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04668          ast_debug(5, "creating attachment filename %s\n", filename);
04669          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04670       } else {
04671          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04672          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04673          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04674       }
04675    }
04676    ast_free(str1);
04677    ast_free(str2);
04678 }
04679 
04680 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)
04681 {
04682    char tmpdir[256], newtmp[256];
04683    char fname[256];
04684    char tmpcmd[256];
04685    int tmpfd = -1;
04686 
04687    /* Eww. We want formats to tell us their own MIME type */
04688    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04689 
04690    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04691       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04692       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04693       tmpfd = mkstemp(newtmp);
04694       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04695       ast_debug(3, "newtmp: %s\n", newtmp);
04696       if (tmpfd > -1) {
04697          int soxstatus;
04698          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04699          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04700             attach = newtmp;
04701             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04702          } else {
04703             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04704                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04705             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04706          }
04707       }
04708    }
04709    fprintf(p, "--%s" ENDL, bound);
04710    if (msgnum > -1)
04711       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04712    else
04713       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04714    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04715    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04716    if (msgnum > -1)
04717       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04718    else
04719       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04720    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04721    base_encode(fname, p);
04722    if (last)
04723       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04724    if (tmpfd > -1) {
04725       unlink(fname);
04726       close(tmpfd);
04727       unlink(newtmp);
04728    }
04729    return 0;
04730 }
04731 
04732 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)
04733 {
04734    FILE *p = NULL;
04735    char tmp[80] = "/tmp/astmail-XXXXXX";
04736    char tmp2[256];
04737    char *stringp;
04738 
04739    if (vmu && ast_strlen_zero(vmu->email)) {
04740       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04741       return(0);
04742    }
04743 
04744    /* Mail only the first format */
04745    format = ast_strdupa(format);
04746    stringp = format;
04747    strsep(&stringp, "|");
04748 
04749    if (!strcmp(format, "wav49"))
04750       format = "WAV";
04751    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));
04752    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04753       command hangs */
04754    if ((p = vm_mkftemp(tmp)) == NULL) {
04755       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04756       return -1;
04757    } else {
04758       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04759       fclose(p);
04760       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04761       ast_safe_system(tmp2);
04762       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04763    }
04764    return 0;
04765 }
04766 
04767 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)
04768 {
04769    char enc_cidnum[256], enc_cidname[256];
04770    char date[256];
04771    char host[MAXHOSTNAMELEN] = "";
04772    char who[256];
04773    char dur[PATH_MAX];
04774    char tmp[80] = "/tmp/astmail-XXXXXX";
04775    char tmp2[PATH_MAX];
04776    struct ast_tm tm;
04777    FILE *p;
04778    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04779 
04780    if (!str1 || !str2) {
04781       ast_free(str1);
04782       ast_free(str2);
04783       return -1;
04784    }
04785 
04786    if (cidnum) {
04787       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04788    }
04789    if (cidname) {
04790       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04791    }
04792 
04793    if ((p = vm_mkftemp(tmp)) == NULL) {
04794       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04795       ast_free(str1);
04796       ast_free(str2);
04797       return -1;
04798    }
04799    gethostname(host, sizeof(host)-1);
04800    if (strchr(srcemail, '@')) {
04801       ast_copy_string(who, srcemail, sizeof(who));
04802    } else {
04803       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04804    }
04805    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04806    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04807    fprintf(p, "Date: %s\n", date);
04808 
04809    /* Reformat for custom pager format */
04810    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04811 
04812    if (!ast_strlen_zero(pagerfromstring)) {
04813       struct ast_channel *ast;
04814       if ((ast = ast_dummy_channel_alloc())) {
04815          char *ptr;
04816          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04817          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04818 
04819          if (check_mime(ast_str_buffer(str1))) {
04820             int first_line = 1;
04821             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04822             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04823                *ptr = '\0';
04824                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04825                first_line = 0;
04826                /* Substring is smaller, so this will never grow */
04827                ast_str_set(&str2, 0, "%s", ptr + 1);
04828             }
04829             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04830          } else {
04831             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04832          }
04833          ast = ast_channel_release(ast);
04834       } else {
04835          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04836       }
04837    } else {
04838       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04839    }
04840 
04841    if (check_mime(vmu->fullname)) {
04842       int first_line = 1;
04843       char *ptr;
04844       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04845       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04846          *ptr = '\0';
04847          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04848          first_line = 0;
04849          /* Substring is smaller, so this will never grow */
04850          ast_str_set(&str2, 0, "%s", ptr + 1);
04851       }
04852       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04853    } else {
04854       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04855    }
04856 
04857    if (!ast_strlen_zero(pagersubject)) {
04858       struct ast_channel *ast;
04859       if ((ast = ast_dummy_channel_alloc())) {
04860          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04861          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04862          if (check_mime(ast_str_buffer(str1))) {
04863             int first_line = 1;
04864             char *ptr;
04865             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04866             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04867                *ptr = '\0';
04868                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04869                first_line = 0;
04870                /* Substring is smaller, so this will never grow */
04871                ast_str_set(&str2, 0, "%s", ptr + 1);
04872             }
04873             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04874          } else {
04875             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04876          }
04877          ast = ast_channel_release(ast);
04878       } else {
04879          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04880       }
04881    } else {
04882       if (ast_strlen_zero(flag)) {
04883          fprintf(p, "Subject: New VM\n\n");
04884       } else {
04885          fprintf(p, "Subject: New %s VM\n\n", flag);
04886       }
04887    }
04888 
04889    if (pagerbody) {
04890       struct ast_channel *ast;
04891       if ((ast = ast_dummy_channel_alloc())) {
04892          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04893          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04894          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04895          ast = ast_channel_release(ast);
04896       } else {
04897          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04898       }
04899    } else {
04900       fprintf(p, "New %s long %s msg in box %s\n"
04901             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04902    }
04903 
04904    fclose(p);
04905    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04906    ast_safe_system(tmp2);
04907    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04908    ast_free(str1);
04909    ast_free(str2);
04910    return 0;
04911 }
04912 
04913 /*!
04914  * \brief Gets the current date and time, as formatted string.
04915  * \param s The buffer to hold the output formatted date.
04916  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04917  * 
04918  * The date format string used is "%a %b %e %r UTC %Y".
04919  * 
04920  * \return zero on success, -1 on error.
04921  */
04922 static int get_date(char *s, int len)
04923 {
04924    struct ast_tm tm;
04925    struct timeval t = ast_tvnow();
04926    
04927    ast_localtime(&t, &tm, "UTC");
04928 
04929    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04930 }
04931 
04932 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04933 {
04934    int res;
04935    char fn[PATH_MAX];
04936    char dest[PATH_MAX];
04937 
04938    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04939 
04940    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04941       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04942       return -1;
04943    }
04944 
04945    RETRIEVE(fn, -1, ext, context);
04946    if (ast_fileexists(fn, NULL, NULL) > 0) {
04947       res = ast_stream_and_wait(chan, fn, ecodes);
04948       if (res) {
04949          DISPOSE(fn, -1);
04950          return res;
04951       }
04952    } else {
04953       /* Dispose just in case */
04954       DISPOSE(fn, -1);
04955       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04956       if (res)
04957          return res;
04958       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04959       if (res)
04960          return res;
04961    }
04962    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04963    return res;
04964 }
04965 
04966 static void free_zone(struct vm_zone *z)
04967 {
04968    ast_free(z);
04969 }
04970 
04971 #ifdef ODBC_STORAGE
04972 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04973 {
04974    int x = -1;
04975    int res;
04976    SQLHSTMT stmt = NULL;
04977    char sql[PATH_MAX];
04978    char rowdata[20];
04979    char tmp[PATH_MAX] = "";
04980    struct odbc_obj *obj = NULL;
04981    char *context;
04982    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04983 
04984    if (newmsgs)
04985       *newmsgs = 0;
04986    if (oldmsgs)
04987       *oldmsgs = 0;
04988    if (urgentmsgs)
04989       *urgentmsgs = 0;
04990 
04991    /* If no mailbox, return immediately */
04992    if (ast_strlen_zero(mailbox))
04993       return 0;
04994 
04995    ast_copy_string(tmp, mailbox, sizeof(tmp));
04996 
04997    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04998       int u, n, o;
04999       char *next, *remaining = tmp;
05000       while ((next = strsep(&remaining, " ,"))) {
05001          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05002             return -1;
05003          }
05004          if (urgentmsgs) {
05005             *urgentmsgs += u;
05006          }
05007          if (newmsgs) {
05008             *newmsgs += n;
05009          }
05010          if (oldmsgs) {
05011             *oldmsgs += o;
05012          }
05013       }
05014       return 0;
05015    }
05016 
05017    context = strchr(tmp, '@');
05018    if (context) {
05019       *context = '\0';
05020       context++;
05021    } else
05022       context = "default";
05023 
05024    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05025       do {
05026          if (newmsgs) {
05027             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05028             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05029                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05030                break;
05031             }
05032             res = SQLFetch(stmt);
05033             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05034                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05035                break;
05036             }
05037             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05038             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05039                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05040                break;
05041             }
05042             *newmsgs = atoi(rowdata);
05043             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05044          }
05045 
05046          if (oldmsgs) {
05047             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05048             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05049                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05050                break;
05051             }
05052             res = SQLFetch(stmt);
05053             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05054                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05055                break;
05056             }
05057             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05058             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05059                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05060                break;
05061             }
05062             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05063             *oldmsgs = atoi(rowdata);
05064          }
05065 
05066          if (urgentmsgs) {
05067             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05068             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05069                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05070                break;
05071             }
05072             res = SQLFetch(stmt);
05073             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05074                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05075                break;
05076             }
05077             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05078             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05079                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05080                break;
05081             }
05082             *urgentmsgs = atoi(rowdata);
05083          }
05084 
05085          x = 0;
05086       } while (0);
05087    } else {
05088       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05089    }
05090 
05091    if (stmt) {
05092       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05093    }
05094    if (obj) {
05095       ast_odbc_release_obj(obj);
05096    }
05097 
05098    return x;
05099 }
05100 
05101 /*!
05102  * \brief Gets the number of messages that exist in a mailbox folder.
05103  * \param context
05104  * \param mailbox
05105  * \param folder
05106  * 
05107  * This method is used when ODBC backend is used.
05108  * \return The number of messages in this mailbox folder (zero or more).
05109  */
05110 static int messagecount(const char *context, const char *mailbox, const char *folder)
05111 {
05112    struct odbc_obj *obj = NULL;
05113    int nummsgs = 0;
05114    int res;
05115    SQLHSTMT stmt = NULL;
05116    char sql[PATH_MAX];
05117    char rowdata[20];
05118    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05119    if (!folder)
05120       folder = "INBOX";
05121    /* If no mailbox, return immediately */
05122    if (ast_strlen_zero(mailbox))
05123       return 0;
05124 
05125    obj = ast_odbc_request_obj(odbc_database, 0);
05126    if (obj) {
05127       if (!strcmp(folder, "INBOX")) {
05128          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);
05129       } else {
05130          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05131       }
05132       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05133       if (!stmt) {
05134          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05135          goto yuck;
05136       }
05137       res = SQLFetch(stmt);
05138       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05139          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05140          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05141          goto yuck;
05142       }
05143       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05144       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05145          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05146          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05147          goto yuck;
05148       }
05149       nummsgs = atoi(rowdata);
05150       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05151    } else
05152       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05153 
05154 yuck:
05155    if (obj)
05156       ast_odbc_release_obj(obj);
05157    return nummsgs;
05158 }
05159 
05160 /** 
05161  * \brief Determines if the given folder has messages.
05162  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05163  * 
05164  * This function is used when the mailbox is stored in an ODBC back end.
05165  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05166  * \return 1 if the folder has one or more messages. zero otherwise.
05167  */
05168 static int has_voicemail(const char *mailbox, const char *folder)
05169 {
05170    char tmp[256], *tmp2 = tmp, *box, *context;
05171    ast_copy_string(tmp, mailbox, sizeof(tmp));
05172    while ((context = box = strsep(&tmp2, ",&"))) {
05173       strsep(&context, "@");
05174       if (ast_strlen_zero(context))
05175          context = "default";
05176       if (messagecount(context, box, folder))
05177          return 1;
05178    }
05179    return 0;
05180 }
05181 #endif
05182 #ifndef IMAP_STORAGE
05183 /*! 
05184  * \brief Copies a message from one mailbox to another.
05185  * \param chan
05186  * \param vmu
05187  * \param imbox
05188  * \param msgnum
05189  * \param duration
05190  * \param recip
05191  * \param fmt
05192  * \param dir
05193  * \param flag
05194  *
05195  * This is only used by file storage based mailboxes.
05196  *
05197  * \return zero on success, -1 on error.
05198  */
05199 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)
05200 {
05201    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05202    const char *frombox = mbox(vmu, imbox);
05203    int recipmsgnum;
05204    int res = 0;
05205 
05206    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05207 
05208    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05209       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05210    } else {
05211       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05212    }
05213    
05214    if (!dir)
05215       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05216    else
05217       ast_copy_string(fromdir, dir, sizeof(fromdir));
05218 
05219    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05220    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05221 
05222    if (vm_lock_path(todir))
05223       return ERROR_LOCK_PATH;
05224 
05225    recipmsgnum = last_message_index(recip, todir) + 1;
05226    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05227       make_file(topath, sizeof(topath), todir, recipmsgnum);
05228 #ifndef ODBC_STORAGE
05229       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05230          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05231       } else {
05232 #endif
05233          /* If we are prepending a message for ODBC, then the message already
05234           * exists in the database, but we want to force copying from the
05235           * filesystem (since only the FS contains the prepend). */
05236          copy_plain_file(frompath, topath);
05237          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05238          vm_delete(topath);
05239 #ifndef ODBC_STORAGE
05240       }
05241 #endif
05242    } else {
05243       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05244       res = -1;
05245    }
05246    ast_unlock_path(todir);
05247    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05248       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05249       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05250       flag);
05251    
05252    return res;
05253 }
05254 #endif
05255 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05256 
05257 static int messagecount(const char *context, const char *mailbox, const char *folder)
05258 {
05259    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05260 }
05261 
05262 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05263 {
05264    DIR *dir;
05265    struct dirent *de;
05266    char fn[256];
05267    int ret = 0;
05268 
05269    /* If no mailbox, return immediately */
05270    if (ast_strlen_zero(mailbox))
05271       return 0;
05272 
05273    if (ast_strlen_zero(folder))
05274       folder = "INBOX";
05275    if (ast_strlen_zero(context))
05276       context = "default";
05277 
05278    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05279 
05280    if (!(dir = opendir(fn)))
05281       return 0;
05282 
05283    while ((de = readdir(dir))) {
05284       if (!strncasecmp(de->d_name, "msg", 3)) {
05285          if (shortcircuit) {
05286             ret = 1;
05287             break;
05288          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05289             ret++;
05290          }
05291       }
05292    }
05293 
05294    closedir(dir);
05295 
05296    return ret;
05297 }
05298 
05299 /** 
05300  * \brief Determines if the given folder has messages.
05301  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05302  * \param folder the folder to look in
05303  *
05304  * This function is used when the mailbox is stored in a filesystem back end.
05305  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05306  * \return 1 if the folder has one or more messages. zero otherwise.
05307  */
05308 static int has_voicemail(const char *mailbox, const char *folder)
05309 {
05310    char tmp[256], *tmp2 = tmp, *box, *context;
05311    ast_copy_string(tmp, mailbox, sizeof(tmp));
05312    if (ast_strlen_zero(folder)) {
05313       folder = "INBOX";
05314    }
05315    while ((box = strsep(&tmp2, ",&"))) {
05316       if ((context = strchr(box, '@')))
05317          *context++ = '\0';
05318       else
05319          context = "default";
05320       if (__has_voicemail(context, box, folder, 1))
05321          return 1;
05322       /* If we are checking INBOX, we should check Urgent as well */
05323       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05324          return 1;
05325       }
05326    }
05327    return 0;
05328 }
05329 
05330 
05331 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05332 {
05333    char tmp[256];
05334    char *context;
05335 
05336    /* If no mailbox, return immediately */
05337    if (ast_strlen_zero(mailbox))
05338       return 0;
05339 
05340    if (newmsgs)
05341       *newmsgs = 0;
05342    if (oldmsgs)
05343       *oldmsgs = 0;
05344    if (urgentmsgs)
05345       *urgentmsgs = 0;
05346 
05347    if (strchr(mailbox, ',')) {
05348       int tmpnew, tmpold, tmpurgent;
05349       char *mb, *cur;
05350 
05351       ast_copy_string(tmp, mailbox, sizeof(tmp));
05352       mb = tmp;
05353       while ((cur = strsep(&mb, ", "))) {
05354          if (!ast_strlen_zero(cur)) {
05355             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05356                return -1;
05357             else {
05358                if (newmsgs)
05359                   *newmsgs += tmpnew; 
05360                if (oldmsgs)
05361                   *oldmsgs += tmpold;
05362                if (urgentmsgs)
05363                   *urgentmsgs += tmpurgent;
05364             }
05365          }
05366       }
05367       return 0;
05368    }
05369 
05370    ast_copy_string(tmp, mailbox, sizeof(tmp));
05371    
05372    if ((context = strchr(tmp, '@')))
05373       *context++ = '\0';
05374    else
05375       context = "default";
05376 
05377    if (newmsgs)
05378       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05379    if (oldmsgs)
05380       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05381    if (urgentmsgs)
05382       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05383 
05384    return 0;
05385 }
05386 
05387 #endif
05388 
05389 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05390 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05391 {
05392    int urgentmsgs = 0;
05393    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05394    if (newmsgs) {
05395       *newmsgs += urgentmsgs;
05396    }
05397    return res;
05398 }
05399 
05400 static void run_externnotify(char *context, char *extension, const char *flag)
05401 {
05402    char arguments[255];
05403    char ext_context[256] = "";
05404    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05405    struct ast_smdi_mwi_message *mwi_msg;
05406 
05407    if (!ast_strlen_zero(context))
05408       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05409    else
05410       ast_copy_string(ext_context, extension, sizeof(ext_context));
05411 
05412    if (smdi_iface) {
05413       if (ast_app_has_voicemail(ext_context, NULL)) 
05414          ast_smdi_mwi_set(smdi_iface, extension);
05415       else
05416          ast_smdi_mwi_unset(smdi_iface, extension);
05417 
05418       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05419          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05420          if (!strncmp(mwi_msg->cause, "INV", 3))
05421             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05422          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05423             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05424          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05425          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05426       } else {
05427          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05428       }
05429    }
05430 
05431    if (!ast_strlen_zero(externnotify)) {
05432       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05433          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05434       } else {
05435          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05436          ast_debug(1, "Executing %s\n", arguments);
05437          ast_safe_system(arguments);
05438       }
05439    }
05440 }
05441 
05442 /*!
05443  * \brief Variables used for saving a voicemail.
05444  *
05445  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05446  */
05447 struct leave_vm_options {
05448    unsigned int flags;
05449    signed char record_gain;
05450    char *exitcontext;
05451 };
05452 
05453 /*!
05454  * \brief Prompts the user and records a voicemail to a mailbox.
05455  * \param chan
05456  * \param ext
05457  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05458  * 
05459  * 
05460  * 
05461  * \return zero on success, -1 on error.
05462  */
05463 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05464 {
05465 #ifdef IMAP_STORAGE
05466    int newmsgs, oldmsgs;
05467 #else
05468    char urgdir[PATH_MAX];
05469 #endif
05470    char txtfile[PATH_MAX];
05471    char tmptxtfile[PATH_MAX];
05472    struct vm_state *vms = NULL;
05473    char callerid[256];
05474    FILE *txt;
05475    char date[256];
05476    int txtdes;
05477    int res = 0;
05478    int msgnum;
05479    int duration = 0;
05480    int ausemacro = 0;
05481    int ousemacro = 0;
05482    int ouseexten = 0;
05483    char tmpdur[16];
05484    char priority[16];
05485    char origtime[16];
05486    char dir[PATH_MAX];
05487    char tmpdir[PATH_MAX];
05488    char fn[PATH_MAX];
05489    char prefile[PATH_MAX] = "";
05490    char tempfile[PATH_MAX] = "";
05491    char ext_context[256] = "";
05492    char fmt[80];
05493    char *context;
05494    char ecodes[17] = "#";
05495    struct ast_str *tmp = ast_str_create(16);
05496    char *tmpptr;
05497    struct ast_vm_user *vmu;
05498    struct ast_vm_user svm;
05499    const char *category = NULL;
05500    const char *code;
05501    const char *alldtmf = "0123456789ABCD*#";
05502    char flag[80];
05503 
05504    if (!tmp) {
05505       return -1;
05506    }
05507 
05508    ast_str_set(&tmp, 0, "%s", ext);
05509    ext = ast_str_buffer(tmp);
05510    if ((context = strchr(ext, '@'))) {
05511       *context++ = '\0';
05512       tmpptr = strchr(context, '&');
05513    } else {
05514       tmpptr = strchr(ext, '&');
05515    }
05516 
05517    if (tmpptr)
05518       *tmpptr++ = '\0';
05519 
05520    ast_channel_lock(chan);
05521    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05522       category = ast_strdupa(category);
05523    }
05524    ast_channel_unlock(chan);
05525 
05526    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05527       ast_copy_string(flag, "Urgent", sizeof(flag));
05528    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05529       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05530    } else {
05531       flag[0] = '\0';
05532    }
05533 
05534    ast_debug(3, "Before find_user\n");
05535    if (!(vmu = find_user(&svm, context, ext))) {
05536       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05537       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05538       ast_free(tmp);
05539       return res;
05540    }
05541    /* Setup pre-file if appropriate */
05542    if (strcmp(vmu->context, "default"))
05543       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05544    else
05545       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05546 
05547    /* Set the path to the prefile. Will be one of 
05548       VM_SPOOL_DIRcontext/ext/busy
05549       VM_SPOOL_DIRcontext/ext/unavail
05550       Depending on the flag set in options.
05551    */
05552    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05553       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05554    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05555       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05556    }
05557    /* Set the path to the tmpfile as
05558       VM_SPOOL_DIR/context/ext/temp
05559       and attempt to create the folder structure.
05560    */
05561    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05562    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05563       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05564       ast_free(tmp);
05565       return -1;
05566    }
05567    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05568    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05569       ast_copy_string(prefile, tempfile, sizeof(prefile));
05570 
05571    DISPOSE(tempfile, -1);
05572    /* It's easier just to try to make it than to check for its existence */
05573 #ifndef IMAP_STORAGE
05574    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05575 #else
05576    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05577    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05578       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05579    }
05580 #endif
05581 
05582    /* Check current or macro-calling context for special extensions */
05583    if (ast_test_flag(vmu, VM_OPERATOR)) {
05584       if (!ast_strlen_zero(vmu->exit)) {
05585          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05586             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05587             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05588             ouseexten = 1;
05589          }
05590       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05591          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05592          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05593          ouseexten = 1;
05594       } else if (!ast_strlen_zero(chan->macrocontext)
05595          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05596             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05597          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05598          ousemacro = 1;
05599       }
05600    }
05601 
05602    if (!ast_strlen_zero(vmu->exit)) {
05603       if (ast_exists_extension(chan, vmu->exit, "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       }
05607    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05608       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05609       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05610    } else if (!ast_strlen_zero(chan->macrocontext)
05611       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05612          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05613       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05614       ausemacro = 1;
05615    }
05616 
05617    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05618       for (code = alldtmf; *code; code++) {
05619          char e[2] = "";
05620          e[0] = *code;
05621          if (strchr(ecodes, e[0]) == NULL
05622             && ast_canmatch_extension(chan, chan->context, e, 1,
05623                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05624             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05625          }
05626       }
05627    }
05628 
05629    /* Play the beginning intro if desired */
05630    if (!ast_strlen_zero(prefile)) {
05631 #ifdef ODBC_STORAGE
05632       int success = 
05633 #endif
05634          RETRIEVE(prefile, -1, ext, context);
05635       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05636          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05637             res = ast_waitstream(chan, ecodes);
05638 #ifdef ODBC_STORAGE
05639          if (success == -1) {
05640             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05641             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05642             store_file(prefile, vmu->mailbox, vmu->context, -1);
05643          }
05644 #endif
05645       } else {
05646          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05647          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05648       }
05649       DISPOSE(prefile, -1);
05650       if (res < 0) {
05651          ast_debug(1, "Hang up during prefile playback\n");
05652          free_user(vmu);
05653          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05654          ast_free(tmp);
05655          return -1;
05656       }
05657    }
05658    if (res == '#') {
05659       /* On a '#' we skip the instructions */
05660       ast_set_flag(options, OPT_SILENT);
05661       res = 0;
05662    }
05663    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05664    if (vmu->maxmsg == 0) {
05665       if (option_debug > 2)
05666          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05667       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05668       goto leave_vm_out;
05669    }
05670    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05671       res = ast_stream_and_wait(chan, INTRO, ecodes);
05672       if (res == '#') {
05673          ast_set_flag(options, OPT_SILENT);
05674          res = 0;
05675       }
05676    }
05677    if (res > 0)
05678       ast_stopstream(chan);
05679    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05680     other than the operator -- an automated attendant or mailbox login for example */
05681    if (res == '*') {
05682       chan->exten[0] = 'a';
05683       chan->exten[1] = '\0';
05684       if (!ast_strlen_zero(vmu->exit)) {
05685          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05686       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05687          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05688       }
05689       chan->priority = 0;
05690       free_user(vmu);
05691       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05692       ast_free(tmp);
05693       return 0;
05694    }
05695 
05696    /* Check for a '0' here */
05697    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05698    transfer:
05699       if (ouseexten || ousemacro) {
05700          chan->exten[0] = 'o';
05701          chan->exten[1] = '\0';
05702          if (!ast_strlen_zero(vmu->exit)) {
05703             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05704          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05705             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05706          }
05707          ast_play_and_wait(chan, "transfer");
05708          chan->priority = 0;
05709          free_user(vmu);
05710          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05711       }
05712       ast_free(tmp);
05713       return OPERATOR_EXIT;
05714    }
05715 
05716    /* Allow all other digits to exit Voicemail and return to the dialplan */
05717    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05718       if (!ast_strlen_zero(options->exitcontext))
05719          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05720       free_user(vmu);
05721       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05722       ast_free(tmp);
05723       return res;
05724    }
05725 
05726    if (res < 0) {
05727       free_user(vmu);
05728       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05729       ast_free(tmp);
05730       return -1;
05731    }
05732    /* The meat of recording the message...  All the announcements and beeps have been played*/
05733    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05734    if (!ast_strlen_zero(fmt)) {
05735       msgnum = 0;
05736 
05737 #ifdef IMAP_STORAGE
05738       /* Is ext a mailbox? */
05739       /* must open stream for this user to get info! */
05740       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05741       if (res < 0) {
05742          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05743          ast_free(tmp);
05744          return -1;
05745       }
05746       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05747       /* It is possible under certain circumstances that inboxcount did not
05748        * create a vm_state when it was needed. This is a catchall which will
05749        * rarely be used.
05750        */
05751          if (!(vms = create_vm_state_from_user(vmu))) {
05752             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05753             ast_free(tmp);
05754             return -1;
05755          }
05756       }
05757       vms->newmessages++;
05758       
05759       /* here is a big difference! We add one to it later */
05760       msgnum = newmsgs + oldmsgs;
05761       ast_debug(3, "Messagecount set to %d\n", msgnum);
05762       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05763       /* set variable for compatibility */
05764       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05765 
05766       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05767          goto leave_vm_out;
05768       }
05769 #else
05770       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05771          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05772          if (!res)
05773             res = ast_waitstream(chan, "");
05774          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05775          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05776          inprocess_count(vmu->mailbox, vmu->context, -1);
05777          goto leave_vm_out;
05778       }
05779 
05780 #endif
05781       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05782       txtdes = mkstemp(tmptxtfile);
05783       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05784       if (txtdes < 0) {
05785          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05786          if (!res)
05787             res = ast_waitstream(chan, "");
05788          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05789          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05790          inprocess_count(vmu->mailbox, vmu->context, -1);
05791          goto leave_vm_out;
05792       }
05793 
05794       /* Now play the beep once we have the message number for our next message. */
05795       if (res >= 0) {
05796          /* Unless we're *really* silent, try to send the beep */
05797          res = ast_stream_and_wait(chan, "beep", "");
05798       }
05799             
05800       /* Store information in real-time storage */
05801       if (ast_check_realtime("voicemail_data")) {
05802          snprintf(priority, sizeof(priority), "%d", chan->priority);
05803          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05804          get_date(date, sizeof(date));
05805          ast_callerid_merge(callerid, sizeof(callerid),
05806             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05807             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05808             "Unknown");
05809          ast_store_realtime("voicemail_data",
05810             "origmailbox", ext,
05811             "context", chan->context,
05812             "macrocontext", chan->macrocontext,
05813             "exten", chan->exten,
05814             "priority", priority,
05815             "callerchan", chan->name,
05816             "callerid", callerid,
05817             "origdate", date,
05818             "origtime", origtime,
05819             "category", S_OR(category, ""),
05820             "filename", tmptxtfile,
05821             SENTINEL);
05822       }
05823 
05824       /* Store information */
05825       txt = fdopen(txtdes, "w+");
05826       if (txt) {
05827          get_date(date, sizeof(date));
05828          ast_callerid_merge(callerid, sizeof(callerid),
05829             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05830             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05831             "Unknown");
05832          fprintf(txt, 
05833             ";\n"
05834             "; Message Information file\n"
05835             ";\n"
05836             "[message]\n"
05837             "origmailbox=%s\n"
05838             "context=%s\n"
05839             "macrocontext=%s\n"
05840             "exten=%s\n"
05841             "rdnis=%s\n"
05842             "priority=%d\n"
05843             "callerchan=%s\n"
05844             "callerid=%s\n"
05845             "origdate=%s\n"
05846             "origtime=%ld\n"
05847             "category=%s\n",
05848             ext,
05849             chan->context,
05850             chan->macrocontext, 
05851             chan->exten,
05852             S_COR(chan->redirecting.from.number.valid,
05853                chan->redirecting.from.number.str, "unknown"),
05854             chan->priority,
05855             chan->name,
05856             callerid,
05857             date, (long) time(NULL),
05858             category ? category : "");
05859       } else {
05860          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05861          inprocess_count(vmu->mailbox, vmu->context, -1);
05862          if (ast_check_realtime("voicemail_data")) {
05863             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05864          }
05865          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05866          goto leave_vm_out;
05867       }
05868       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05869 
05870       if (txt) {
05871          fprintf(txt, "flag=%s\n", flag);
05872          if (duration < vmu->minsecs) {
05873             fclose(txt);
05874             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05875             ast_filedelete(tmptxtfile, NULL);
05876             unlink(tmptxtfile);
05877             if (ast_check_realtime("voicemail_data")) {
05878                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05879             }
05880             inprocess_count(vmu->mailbox, vmu->context, -1);
05881          } else {
05882             fprintf(txt, "duration=%d\n", duration);
05883             fclose(txt);
05884             if (vm_lock_path(dir)) {
05885                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05886                /* Delete files */
05887                ast_filedelete(tmptxtfile, NULL);
05888                unlink(tmptxtfile);
05889                inprocess_count(vmu->mailbox, vmu->context, -1);
05890             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05891                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05892                unlink(tmptxtfile);
05893                ast_unlock_path(dir);
05894                inprocess_count(vmu->mailbox, vmu->context, -1);
05895                if (ast_check_realtime("voicemail_data")) {
05896                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05897                }
05898             } else {
05899 #ifndef IMAP_STORAGE
05900                msgnum = last_message_index(vmu, dir) + 1;
05901 #endif
05902                make_file(fn, sizeof(fn), dir, msgnum);
05903 
05904                /* assign a variable with the name of the voicemail file */ 
05905 #ifndef IMAP_STORAGE
05906                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05907 #else
05908                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05909 #endif
05910 
05911                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05912                ast_filerename(tmptxtfile, fn, NULL);
05913                rename(tmptxtfile, txtfile);
05914                inprocess_count(vmu->mailbox, vmu->context, -1);
05915 
05916                /* Properly set permissions on voicemail text descriptor file.
05917                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05918                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05919                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05920 
05921                ast_unlock_path(dir);
05922                if (ast_check_realtime("voicemail_data")) {
05923                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05924                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05925                }
05926                /* We must store the file first, before copying the message, because
05927                 * ODBC storage does the entire copy with SQL.
05928                 */
05929                if (ast_fileexists(fn, NULL, NULL) > 0) {
05930                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05931                }
05932 
05933                /* Are there to be more recipients of this message? */
05934                while (tmpptr) {
05935                   struct ast_vm_user recipu, *recip;
05936                   char *exten, *cntx;
05937                
05938                   exten = strsep(&tmpptr, "&");
05939                   cntx = strchr(exten, '@');
05940                   if (cntx) {
05941                      *cntx = '\0';
05942                      cntx++;
05943                   }
05944                   if ((recip = find_user(&recipu, cntx, exten))) {
05945                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05946                      free_user(recip);
05947                   }
05948                }
05949 #ifndef IMAP_STORAGE
05950                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05951                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05952                   char sfn[PATH_MAX];
05953                   char dfn[PATH_MAX];
05954                   int x;
05955                   /* It's easier just to try to make it than to check for its existence */
05956                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05957                   x = last_message_index(vmu, urgdir) + 1;
05958                   make_file(sfn, sizeof(sfn), dir, msgnum);
05959                   make_file(dfn, sizeof(dfn), urgdir, x);
05960                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05961                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05962                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05963                   ast_copy_string(fn, dfn, sizeof(fn));
05964                   msgnum = x;
05965                }
05966 #endif
05967                /* Notification needs to happen after the copy, though. */
05968                if (ast_fileexists(fn, NULL, NULL)) {
05969 #ifdef IMAP_STORAGE
05970                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
05971                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05972                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05973                      flag);
05974 #else
05975                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
05976                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05977                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05978                      flag);
05979 #endif
05980                }
05981 
05982                /* Disposal needs to happen after the optional move and copy */
05983                if (ast_fileexists(fn, NULL, NULL)) {
05984                   DISPOSE(dir, msgnum);
05985                }
05986             }
05987          }
05988       } else {
05989          inprocess_count(vmu->mailbox, vmu->context, -1);
05990       }
05991       if (res == '0') {
05992          goto transfer;
05993       } else if (res > 0 && res != 't')
05994          res = 0;
05995 
05996       if (duration < vmu->minsecs)
05997          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05998          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05999       else
06000          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06001    } else
06002       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06003 leave_vm_out:
06004    free_user(vmu);
06005 
06006 #ifdef IMAP_STORAGE
06007    /* expunge message - use UID Expunge if supported on IMAP server*/
06008    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06009    if (expungeonhangup == 1) {
06010       ast_mutex_lock(&vms->lock);
06011 #ifdef HAVE_IMAP_TK2006
06012       if (LEVELUIDPLUS (vms->mailstream)) {
06013          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06014       } else 
06015 #endif
06016          mail_expunge(vms->mailstream);
06017       ast_mutex_unlock(&vms->lock);
06018    }
06019 #endif
06020 
06021    ast_free(tmp);
06022    return res;
06023 }
06024 
06025 #if !defined(IMAP_STORAGE) && !defined(ODBC_STORAGE)
06026 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06027 {
06028     /* we know the actual number of messages, so stop process when number is hit */
06029 
06030     int x, dest;
06031     char sfn[PATH_MAX];
06032     char dfn[PATH_MAX];
06033 
06034     if (vm_lock_path(dir))
06035         return ERROR_LOCK_PATH;
06036 
06037     for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06038         make_file(sfn, sizeof(sfn), dir, x);
06039         if (EXISTS(dir, x, sfn, NULL)) {
06040 
06041             if (x != dest) {
06042                 make_file(dfn, sizeof(dfn), dir, dest);
06043                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06044             }
06045 
06046             dest++;
06047         }
06048     }
06049     ast_unlock_path(dir);
06050 
06051     return dest;
06052 }
06053 #endif
06054 
06055 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06056 {
06057    int d;
06058    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06059    return d;
06060 }
06061 
06062 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06063 {
06064 #ifdef IMAP_STORAGE
06065    /* we must use mbox(x) folder names, and copy the message there */
06066    /* simple. huh? */
06067    char sequence[10];
06068    char mailbox[256];
06069    int res;
06070 
06071    /* get the real IMAP message number for this message */
06072    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06073    
06074    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06075    ast_mutex_lock(&vms->lock);
06076    /* if save to Old folder, put in INBOX as read */
06077    if (box == OLD_FOLDER) {
06078       mail_setflag(vms->mailstream, sequence, "\\Seen");
06079       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06080    } else if (box == NEW_FOLDER) {
06081       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06082       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06083    }
06084    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06085       ast_mutex_unlock(&vms->lock);
06086       return 0;
06087    }
06088    /* Create the folder if it don't exist */
06089    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06090    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06091    if (mail_create(vms->mailstream, mailbox) == NIL) 
06092       ast_debug(5, "Folder exists.\n");
06093    else
06094       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06095    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06096    ast_mutex_unlock(&vms->lock);
06097    return res;
06098 #else
06099    char *dir = vms->curdir;
06100    char *username = vms->username;
06101    char *context = vmu->context;
06102    char sfn[PATH_MAX];
06103    char dfn[PATH_MAX];
06104    char ddir[PATH_MAX];
06105    const char *dbox = mbox(vmu, box);
06106    int x, i;
06107    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06108 
06109    if (vm_lock_path(ddir))
06110       return ERROR_LOCK_PATH;
06111 
06112    x = last_message_index(vmu, ddir) + 1;
06113 
06114    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06115       x--;
06116       for (i = 1; i <= x; i++) {
06117          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06118          make_file(sfn, sizeof(sfn), ddir, i);
06119          make_file(dfn, sizeof(dfn), ddir, i - 1);
06120          if (EXISTS(ddir, i, sfn, NULL)) {
06121             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06122          } else
06123             break;
06124       }
06125    } else {
06126       if (x >= vmu->maxmsg) {
06127          ast_unlock_path(ddir);
06128          return -1;
06129       }
06130    }
06131    make_file(sfn, sizeof(sfn), dir, msg);
06132    make_file(dfn, sizeof(dfn), ddir, x);
06133    if (strcmp(sfn, dfn)) {
06134       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06135    }
06136    ast_unlock_path(ddir);
06137 #endif
06138    return 0;
06139 }
06140 
06141 static int adsi_logo(unsigned char *buf)
06142 {
06143    int bytes = 0;
06144    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06145    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06146    return bytes;
06147 }
06148 
06149 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06150 {
06151    unsigned char buf[256];
06152    int bytes = 0;
06153    int x;
06154    char num[5];
06155 
06156    *useadsi = 0;
06157    bytes += ast_adsi_data_mode(buf + bytes);
06158    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06159 
06160    bytes = 0;
06161    bytes += adsi_logo(buf);
06162    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06163 #ifdef DISPLAY
06164    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06165 #endif
06166    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06167    bytes += ast_adsi_data_mode(buf + bytes);
06168    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06169 
06170    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06171       bytes = 0;
06172       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06173       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06174       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06175       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06176       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06177       return 0;
06178    }
06179 
06180 #ifdef DISPLAY
06181    /* Add a dot */
06182    bytes = 0;
06183    bytes += ast_adsi_logo(buf);
06184    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06185    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06186    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06187    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06188 #endif
06189    bytes = 0;
06190    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06191    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06192    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06193    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06194    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06195    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06196    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06197 
06198 #ifdef DISPLAY
06199    /* Add another dot */
06200    bytes = 0;
06201    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06202    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06203 
06204    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06205    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06206 #endif
06207 
06208    bytes = 0;
06209    /* These buttons we load but don't use yet */
06210    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06211    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06212    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06213    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06214    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06215    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06216    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06217 
06218 #ifdef DISPLAY
06219    /* Add another dot */
06220    bytes = 0;
06221    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06222    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06223    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06224 #endif
06225 
06226    bytes = 0;
06227    for (x = 0; x < 5; x++) {
06228       snprintf(num, sizeof(num), "%d", x);
06229       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06230    }
06231    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06232    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06233 
06234 #ifdef DISPLAY
06235    /* Add another dot */
06236    bytes = 0;
06237    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06238    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06239    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06240 #endif
06241 
06242    if (ast_adsi_end_download(chan)) {
06243       bytes = 0;
06244       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06245       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06246       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06247       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06248       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06249       return 0;
06250    }
06251    bytes = 0;
06252    bytes += ast_adsi_download_disconnect(buf + bytes);
06253    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06254    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06255 
06256    ast_debug(1, "Done downloading scripts...\n");
06257 
06258 #ifdef DISPLAY
06259    /* Add last dot */
06260    bytes = 0;
06261    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06262    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06263 #endif
06264    ast_debug(1, "Restarting session...\n");
06265 
06266    bytes = 0;
06267    /* Load the session now */
06268    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06269       *useadsi = 1;
06270       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06271    } else
06272       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06273 
06274    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06275    return 0;
06276 }
06277 
06278 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06279 {
06280    int x;
06281    if (!ast_adsi_available(chan))
06282       return;
06283    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06284    if (x < 0)
06285       return;
06286    if (!x) {
06287       if (adsi_load_vmail(chan, useadsi)) {
06288          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06289          return;
06290       }
06291    } else
06292       *useadsi = 1;
06293 }
06294 
06295 static void adsi_login(struct ast_channel *chan)
06296 {
06297    unsigned char buf[256];
06298    int bytes = 0;
06299    unsigned char keys[8];
06300    int x;
06301    if (!ast_adsi_available(chan))
06302       return;
06303 
06304    for (x = 0; x < 8; x++)
06305       keys[x] = 0;
06306    /* Set one key for next */
06307    keys[3] = ADSI_KEY_APPS + 3;
06308 
06309    bytes += adsi_logo(buf + bytes);
06310    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06311    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06312    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06313    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06314    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06315    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06316    bytes += ast_adsi_set_keys(buf + bytes, keys);
06317    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06318    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06319 }
06320 
06321 static void adsi_password(struct ast_channel *chan)
06322 {
06323    unsigned char buf[256];
06324    int bytes = 0;
06325    unsigned char keys[8];
06326    int x;
06327    if (!ast_adsi_available(chan))
06328       return;
06329 
06330    for (x = 0; x < 8; x++)
06331       keys[x] = 0;
06332    /* Set one key for next */
06333    keys[3] = ADSI_KEY_APPS + 3;
06334 
06335    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06336    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06337    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06338    bytes += ast_adsi_set_keys(buf + bytes, keys);
06339    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06340    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06341 }
06342 
06343 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06344 {
06345    unsigned char buf[256];
06346    int bytes = 0;
06347    unsigned char keys[8];
06348    int x, y;
06349 
06350    if (!ast_adsi_available(chan))
06351       return;
06352 
06353    for (x = 0; x < 5; x++) {
06354       y = ADSI_KEY_APPS + 12 + start + x;
06355       if (y > ADSI_KEY_APPS + 12 + 4)
06356          y = 0;
06357       keys[x] = ADSI_KEY_SKT | y;
06358    }
06359    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06360    keys[6] = 0;
06361    keys[7] = 0;
06362 
06363    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06364    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06365    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06366    bytes += ast_adsi_set_keys(buf + bytes, keys);
06367    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06368 
06369    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06370 }
06371 
06372 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06373 {
06374    int bytes = 0;
06375    unsigned char buf[256]; 
06376    char buf1[256], buf2[256];
06377    char fn2[PATH_MAX];
06378 
06379    char cid[256] = "";
06380    char *val;
06381    char *name, *num;
06382    char datetime[21] = "";
06383    FILE *f;
06384 
06385    unsigned char keys[8];
06386 
06387    int x;
06388 
06389    if (!ast_adsi_available(chan))
06390       return;
06391 
06392    /* Retrieve important info */
06393    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06394    f = fopen(fn2, "r");
06395    if (f) {
06396       while (!feof(f)) {   
06397          if (!fgets((char *) buf, sizeof(buf), f)) {
06398             continue;
06399          }
06400          if (!feof(f)) {
06401             char *stringp = NULL;
06402             stringp = (char *) buf;
06403             strsep(&stringp, "=");
06404             val = strsep(&stringp, "=");
06405             if (!ast_strlen_zero(val)) {
06406                if (!strcmp((char *) buf, "callerid"))
06407                   ast_copy_string(cid, val, sizeof(cid));
06408                if (!strcmp((char *) buf, "origdate"))
06409                   ast_copy_string(datetime, val, sizeof(datetime));
06410             }
06411          }
06412       }
06413       fclose(f);
06414    }
06415    /* New meaning for keys */
06416    for (x = 0; x < 5; x++)
06417       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06418    keys[6] = 0x0;
06419    keys[7] = 0x0;
06420 
06421    if (!vms->curmsg) {
06422       /* No prev key, provide "Folder" instead */
06423       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06424    }
06425    if (vms->curmsg >= vms->lastmsg) {
06426       /* If last message ... */
06427       if (vms->curmsg) {
06428          /* but not only message, provide "Folder" instead */
06429          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06430          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06431 
06432       } else {
06433          /* Otherwise if only message, leave blank */
06434          keys[3] = 1;
06435       }
06436    }
06437 
06438    if (!ast_strlen_zero(cid)) {
06439       ast_callerid_parse(cid, &name, &num);
06440       if (!name)
06441          name = num;
06442    } else
06443       name = "Unknown Caller";
06444 
06445    /* If deleted, show "undeleted" */
06446 
06447    if (vms->deleted[vms->curmsg])
06448       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06449 
06450    /* Except "Exit" */
06451    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06452    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06453       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06454    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06455 
06456    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06457    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06458    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06459    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06460    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06461    bytes += ast_adsi_set_keys(buf + bytes, keys);
06462    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06463 
06464    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06465 }
06466 
06467 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06468 {
06469    int bytes = 0;
06470    unsigned char buf[256];
06471    unsigned char keys[8];
06472 
06473    int x;
06474 
06475    if (!ast_adsi_available(chan))
06476       return;
06477 
06478    /* New meaning for keys */
06479    for (x = 0; x < 5; x++)
06480       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06481 
06482    keys[6] = 0x0;
06483    keys[7] = 0x0;
06484 
06485    if (!vms->curmsg) {
06486       /* No prev key, provide "Folder" instead */
06487       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06488    }
06489    if (vms->curmsg >= vms->lastmsg) {
06490       /* If last message ... */
06491       if (vms->curmsg) {
06492          /* but not only message, provide "Folder" instead */
06493          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06494       } else {
06495          /* Otherwise if only message, leave blank */
06496          keys[3] = 1;
06497       }
06498    }
06499 
06500    /* If deleted, show "undeleted" */
06501    if (vms->deleted[vms->curmsg]) 
06502       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06503 
06504    /* Except "Exit" */
06505    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06506    bytes += ast_adsi_set_keys(buf + bytes, keys);
06507    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06508 
06509    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06510 }
06511 
06512 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06513 {
06514    unsigned char buf[256] = "";
06515    char buf1[256] = "", buf2[256] = "";
06516    int bytes = 0;
06517    unsigned char keys[8];
06518    int x;
06519 
06520    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06521    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06522    if (!ast_adsi_available(chan))
06523       return;
06524    if (vms->newmessages) {
06525       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06526       if (vms->oldmessages) {
06527          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06528          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06529       } else {
06530          snprintf(buf2, sizeof(buf2), "%s.", newm);
06531       }
06532    } else if (vms->oldmessages) {
06533       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06534       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06535    } else {
06536       strcpy(buf1, "You have no messages.");
06537       buf2[0] = ' ';
06538       buf2[1] = '\0';
06539    }
06540    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06541    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06542    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06543 
06544    for (x = 0; x < 6; x++)
06545       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06546    keys[6] = 0;
06547    keys[7] = 0;
06548 
06549    /* Don't let them listen if there are none */
06550    if (vms->lastmsg < 0)
06551       keys[0] = 1;
06552    bytes += ast_adsi_set_keys(buf + bytes, keys);
06553 
06554    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06555 
06556    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06557 }
06558 
06559 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06560 {
06561    unsigned char buf[256] = "";
06562    char buf1[256] = "", buf2[256] = "";
06563    int bytes = 0;
06564    unsigned char keys[8];
06565    int x;
06566 
06567    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06568 
06569    if (!ast_adsi_available(chan))
06570       return;
06571 
06572    /* Original command keys */
06573    for (x = 0; x < 6; x++)
06574       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06575 
06576    keys[6] = 0;
06577    keys[7] = 0;
06578 
06579    if ((vms->lastmsg + 1) < 1)
06580       keys[0] = 0;
06581 
06582    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06583       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06584 
06585    if (vms->lastmsg + 1)
06586       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06587    else
06588       strcpy(buf2, "no messages.");
06589    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06590    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06591    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06592    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06593    bytes += ast_adsi_set_keys(buf + bytes, keys);
06594 
06595    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06596 
06597    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06598    
06599 }
06600 
06601 /*
06602 static void adsi_clear(struct ast_channel *chan)
06603 {
06604    char buf[256];
06605    int bytes=0;
06606    if (!ast_adsi_available(chan))
06607       return;
06608    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06609    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06610 
06611    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06612 }
06613 */
06614 
06615 static void adsi_goodbye(struct ast_channel *chan)
06616 {
06617    unsigned char buf[256];
06618    int bytes = 0;
06619 
06620    if (!ast_adsi_available(chan))
06621       return;
06622    bytes += adsi_logo(buf + bytes);
06623    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06624    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06625    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06626    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06627 
06628    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06629 }
06630 
06631 /*!\brief get_folder: Folder menu
06632  * Plays "press 1 for INBOX messages" etc.
06633  * Should possibly be internationalized
06634  */
06635 static int get_folder(struct ast_channel *chan, int start)
06636 {
06637    int x;
06638    int d;
06639    char fn[PATH_MAX];
06640    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06641    if (d)
06642       return d;
06643    for (x = start; x < 5; x++) { /* For all folders */
06644       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06645          return d;
06646       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06647       if (d)
06648          return d;
06649       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06650       d = vm_play_folder_name(chan, fn);
06651       if (d)
06652          return d;
06653       d = ast_waitfordigit(chan, 500);
06654       if (d)
06655          return d;
06656    }
06657    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06658    if (d)
06659       return d;
06660    d = ast_waitfordigit(chan, 4000);
06661    return d;
06662 }
06663 
06664 /*!
06665  * \brief plays a prompt and waits for a keypress.
06666  * \param chan
06667  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06668  * \param start Does not appear to be used at this time.
06669  *
06670  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06671  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06672  * prompting for the number inputs that correspond to the available folders.
06673  * 
06674  * \return zero on success, or -1 on error.
06675  */
06676 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06677 {
06678    int res = 0;
06679    int loops = 0;
06680    res = ast_play_and_wait(chan, fn);  /* Folder name */
06681    while (((res < '0') || (res > '9')) &&
06682          (res != '#') && (res >= 0) &&
06683          loops < 4) {
06684       res = get_folder(chan, 0);
06685       loops++;
06686    }
06687    if (loops == 4) { /* give up */
06688       return '#';
06689    }
06690    return res;
06691 }
06692 
06693 /*!
06694  * \brief presents the option to prepend to an existing message when forwarding it.
06695  * \param chan
06696  * \param vmu
06697  * \param curdir
06698  * \param curmsg
06699  * \param vm_fmts
06700  * \param context
06701  * \param record_gain
06702  * \param duration
06703  * \param vms
06704  * \param flag 
06705  *
06706  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06707  *
06708  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06709  * \return zero on success, -1 on error.
06710  */
06711 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06712          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06713 {
06714 #ifdef IMAP_STORAGE
06715    int res;
06716 #endif
06717    int cmd = 0;
06718    int retries = 0, prepend_duration = 0, already_recorded = 0;
06719    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06720    char textfile[PATH_MAX];
06721    struct ast_config *msg_cfg;
06722    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06723 #ifndef IMAP_STORAGE
06724    signed char zero_gain = 0;
06725 #endif
06726    const char *duration_str;
06727 
06728    /* Must always populate duration correctly */
06729    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06730    strcpy(textfile, msgfile);
06731    strcpy(backup, msgfile);
06732    strcpy(backup_textfile, msgfile);
06733    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06734    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06735    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06736 
06737    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06738       *duration = atoi(duration_str);
06739    } else {
06740       *duration = 0;
06741    }
06742 
06743    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06744       if (cmd)
06745          retries = 0;
06746       switch (cmd) {
06747       case '1': 
06748 
06749 #ifdef IMAP_STORAGE
06750          /* Record new intro file */
06751          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06752          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06753          res = ast_play_and_wait(chan, INTRO);
06754          res = ast_play_and_wait(chan, "beep");
06755          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06756          cmd = 't';
06757 #else
06758 
06759          /* prepend a message to the current message, update the metadata and return */
06760 
06761          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06762          strcpy(textfile, msgfile);
06763          strncat(textfile, ".txt", sizeof(textfile) - 1);
06764          *duration = 0;
06765 
06766          /* if we can't read the message metadata, stop now */
06767          if (!msg_cfg) {
06768             cmd = 0;
06769             break;
06770          }
06771          
06772          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06773 #ifndef IMAP_STORAGE
06774          if (already_recorded) {
06775             ast_filecopy(backup, msgfile, NULL);
06776             copy(backup_textfile, textfile);
06777          }
06778          else {
06779             ast_filecopy(msgfile, backup, NULL);
06780             copy(textfile, backup_textfile);
06781          }
06782 #endif
06783          already_recorded = 1;
06784 
06785          if (record_gain)
06786             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06787 
06788          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06789          if (cmd == 'S') {
06790             ast_filerename(backup, msgfile, NULL);
06791          }
06792 
06793          if (record_gain)
06794             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06795 
06796          
06797          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06798             *duration = atoi(duration_str);
06799 
06800          if (prepend_duration) {
06801             struct ast_category *msg_cat;
06802             /* need enough space for a maximum-length message duration */
06803             char duration_buf[12];
06804 
06805             *duration += prepend_duration;
06806             msg_cat = ast_category_get(msg_cfg, "message");
06807             snprintf(duration_buf, 11, "%ld", *duration);
06808             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06809                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06810             }
06811          }
06812 
06813 #endif
06814          break;
06815       case '2': 
06816          /* NULL out introfile so we know there is no intro! */
06817 #ifdef IMAP_STORAGE
06818          *vms->introfn = '\0';
06819 #endif
06820          cmd = 't';
06821          break;
06822       case '*':
06823          cmd = '*';
06824          break;
06825       default: 
06826          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06827             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06828          if (!cmd)
06829             cmd = ast_play_and_wait(chan, "vm-starmain");
06830             /* "press star to return to the main menu" */
06831          if (!cmd)
06832             cmd = ast_waitfordigit(chan, 6000);
06833          if (!cmd)
06834             retries++;
06835          if (retries > 3)
06836             cmd = 't';
06837       }
06838    }
06839 
06840    if (msg_cfg)
06841       ast_config_destroy(msg_cfg);
06842    if (prepend_duration)
06843       *duration = prepend_duration;
06844 
06845    if (already_recorded && cmd == -1) {
06846       /* restore original message if prepention cancelled */
06847       ast_filerename(backup, msgfile, NULL);
06848       rename(backup_textfile, textfile);
06849    }
06850 
06851    if (cmd == 't' || cmd == 'S')
06852       cmd = 0;
06853    return cmd;
06854 }
06855 
06856 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06857 {
06858    struct ast_event *event;
06859    char *mailbox, *context;
06860 
06861    /* Strip off @default */
06862    context = mailbox = ast_strdupa(box);
06863    strsep(&context, "@");
06864    if (ast_strlen_zero(context))
06865       context = "default";
06866 
06867    if (!(event = ast_event_new(AST_EVENT_MWI,
06868          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06869          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06870          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06871          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06872          AST_EVENT_IE_END))) {
06873       return;
06874    }
06875 
06876    ast_event_queue_and_cache(event);
06877 }
06878 
06879 /*!
06880  * \brief Sends email notification that a user has a new voicemail waiting for them.
06881  * \param chan
06882  * \param vmu
06883  * \param vms
06884  * \param msgnum
06885  * \param duration
06886  * \param fmt
06887  * \param cidnum The Caller ID phone number value.
06888  * \param cidname The Caller ID name value.
06889  * \param flag
06890  *
06891  * \return zero on success, -1 on error.
06892  */
06893 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)
06894 {
06895    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06896    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06897    const char *category;
06898    char *myserveremail = serveremail;
06899 
06900    ast_channel_lock(chan);
06901    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06902       category = ast_strdupa(category);
06903    }
06904    ast_channel_unlock(chan);
06905 
06906 #ifndef IMAP_STORAGE
06907    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06908 #else
06909    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06910 #endif
06911    make_file(fn, sizeof(fn), todir, msgnum);
06912    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06913 
06914    if (!ast_strlen_zero(vmu->attachfmt)) {
06915       if (strstr(fmt, vmu->attachfmt))
06916          fmt = vmu->attachfmt;
06917       else
06918          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);
06919    }
06920 
06921    /* Attach only the first format */
06922    fmt = ast_strdupa(fmt);
06923    stringp = fmt;
06924    strsep(&stringp, "|");
06925 
06926    if (!ast_strlen_zero(vmu->serveremail))
06927       myserveremail = vmu->serveremail;
06928 
06929    if (!ast_strlen_zero(vmu->email)) {
06930       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06931 
06932       if (attach_user_voicemail)
06933          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06934 
06935       /* XXX possible imap issue, should category be NULL XXX */
06936       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06937 
06938       if (attach_user_voicemail)
06939          DISPOSE(todir, msgnum);
06940    }
06941 
06942    if (!ast_strlen_zero(vmu->pager)) {
06943       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
06944    }
06945 
06946    if (ast_test_flag(vmu, VM_DELETE))
06947       DELETE(todir, msgnum, fn, vmu);
06948 
06949    /* Leave voicemail for someone */
06950    if (ast_app_has_voicemail(ext_context, NULL)) 
06951       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06952 
06953    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06954 
06955    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);
06956    run_externnotify(vmu->context, vmu->mailbox, flag);
06957 
06958 #ifdef IMAP_STORAGE
06959    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06960    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06961       vm_imap_delete(NULL, vms->curmsg, vmu);
06962       vms->newmessages--;  /* Fix new message count */
06963    }
06964 #endif
06965 
06966    return 0;
06967 }
06968 
06969 /*!
06970  * \brief Sends a voicemail message to a mailbox recipient.
06971  * \param chan
06972  * \param context
06973  * \param vms
06974  * \param sender
06975  * \param fmt
06976  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06977  *             Will be 0 when called to forward an existing message (option 8)
06978  *             Will be 1 when called to leave a message (option 3->5)
06979  * \param record_gain 
06980  * \param urgent
06981  *
06982  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06983  * 
06984  * When in the leave message mode (is_new_message == 1):
06985  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06986  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06987  *
06988  * When in the forward message mode (is_new_message == 0):
06989  *   - retreives the current message to be forwarded
06990  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06991  *   - determines the target mailbox and folders
06992  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06993  *
06994  * \return zero on success, -1 on error.
06995  */
06996 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)
06997 {
06998 #ifdef IMAP_STORAGE
06999    int todircount = 0;
07000    struct vm_state *dstvms;
07001 #endif
07002    char username[70]="";
07003    char fn[PATH_MAX]; /* for playback of name greeting */
07004    char ecodes[16] = "#";
07005    int res = 0, cmd = 0;
07006    struct ast_vm_user *receiver = NULL, *vmtmp;
07007    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07008    char *stringp;
07009    const char *s;
07010    int saved_messages = 0;
07011    int valid_extensions = 0;
07012    char *dir;
07013    int curmsg;
07014    char urgent_str[7] = "";
07015    char tmptxtfile[PATH_MAX];
07016    int prompt_played = 0;
07017 #ifndef IMAP_STORAGE
07018    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07019 #endif
07020    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07021       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07022    }
07023 
07024    if (vms == NULL) return -1;
07025    dir = vms->curdir;
07026    curmsg = vms->curmsg;
07027 
07028    tmptxtfile[0] = '\0';
07029    while (!res && !valid_extensions) {
07030       int use_directory = 0;
07031       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07032          int done = 0;
07033          int retries = 0;
07034          cmd = 0;
07035          while ((cmd >= 0) && !done ){
07036             if (cmd)
07037                retries = 0;
07038             switch (cmd) {
07039             case '1': 
07040                use_directory = 0;
07041                done = 1;
07042                break;
07043             case '2': 
07044                use_directory = 1;
07045                done = 1;
07046                break;
07047             case '*': 
07048                cmd = 't';
07049                done = 1;
07050                break;
07051             default: 
07052                /* Press 1 to enter an extension press 2 to use the directory */
07053                cmd = ast_play_and_wait(chan, "vm-forward");
07054                if (!cmd)
07055                   cmd = ast_waitfordigit(chan, 3000);
07056                if (!cmd)
07057                   retries++;
07058                if (retries > 3) {
07059                   cmd = 't';
07060                   done = 1;
07061                }
07062                
07063             }
07064          }
07065          if (cmd < 0 || cmd == 't')
07066             break;
07067       }
07068       
07069       if (use_directory) {
07070          /* use app_directory */
07071          
07072          char old_context[sizeof(chan->context)];
07073          char old_exten[sizeof(chan->exten)];
07074          int old_priority;
07075          struct ast_app* directory_app;
07076 
07077          directory_app = pbx_findapp("Directory");
07078          if (directory_app) {
07079             char vmcontext[256];
07080             /* make backup copies */
07081             memcpy(old_context, chan->context, sizeof(chan->context));
07082             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07083             old_priority = chan->priority;
07084             
07085             /* call the the Directory, changes the channel */
07086             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07087             res = pbx_exec(chan, directory_app, vmcontext);
07088             
07089             ast_copy_string(username, chan->exten, sizeof(username));
07090             
07091             /* restore the old context, exten, and priority */
07092             memcpy(chan->context, old_context, sizeof(chan->context));
07093             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07094             chan->priority = old_priority;
07095          } else {
07096             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07097             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07098          }
07099       } else {
07100          /* Ask for an extension */
07101          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07102          prompt_played++;
07103          if (res || prompt_played > 4)
07104             break;
07105          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07106             break;
07107       }
07108       
07109       /* start all over if no username */
07110       if (ast_strlen_zero(username))
07111          continue;
07112       stringp = username;
07113       s = strsep(&stringp, "*");
07114       /* start optimistic */
07115       valid_extensions = 1;
07116       while (s) {
07117          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07118             int oldmsgs;
07119             int newmsgs;
07120             int capacity;
07121             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07122                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07123                /* Shouldn't happen, but allow trying another extension if it does */
07124                res = ast_play_and_wait(chan, "pbx-invalid");
07125                valid_extensions = 0;
07126                break;
07127             }
07128             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07129             if ((newmsgs + oldmsgs) >= capacity) {
07130                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07131                res = ast_play_and_wait(chan, "vm-mailboxfull");
07132                valid_extensions = 0;
07133                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07134                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07135                   free_user(vmtmp);
07136                }
07137                inprocess_count(receiver->mailbox, receiver->context, -1);
07138                break;
07139             }
07140             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07141          } else {
07142             /* XXX Optimization for the future.  When we encounter a single bad extension,
07143              * bailing out on all of the extensions may not be the way to go.  We should
07144              * probably just bail on that single extension, then allow the user to enter
07145              * several more. XXX
07146              */
07147             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07148                free_user(receiver);
07149             }
07150             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07151             /* "I am sorry, that's not a valid extension.  Please try again." */
07152             res = ast_play_and_wait(chan, "pbx-invalid");
07153             valid_extensions = 0;
07154             break;
07155          }
07156 
07157          /* play name if available, else play extension number */
07158          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07159          RETRIEVE(fn, -1, s, receiver->context);
07160          if (ast_fileexists(fn, NULL, NULL) > 0) {
07161             res = ast_stream_and_wait(chan, fn, ecodes);
07162             if (res) {
07163                DISPOSE(fn, -1);
07164                return res;
07165             }
07166          } else {
07167             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07168          }
07169          DISPOSE(fn, -1);
07170 
07171          s = strsep(&stringp, "*");
07172       }
07173       /* break from the loop of reading the extensions */
07174       if (valid_extensions)
07175          break;
07176    }
07177    /* check if we're clear to proceed */
07178    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07179       return res;
07180    if (is_new_message == 1) {
07181       struct leave_vm_options leave_options;
07182       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07183       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07184 
07185       /* Send VoiceMail */
07186       memset(&leave_options, 0, sizeof(leave_options));
07187       leave_options.record_gain = record_gain;
07188       cmd = leave_voicemail(chan, mailbox, &leave_options);
07189    } else {
07190       /* Forward VoiceMail */
07191       long duration = 0;
07192       struct vm_state vmstmp;
07193       int copy_msg_result = 0;
07194       memcpy(&vmstmp, vms, sizeof(vmstmp));
07195 
07196       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07197 
07198       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07199       if (!cmd) {
07200          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07201 #ifdef IMAP_STORAGE
07202             int attach_user_voicemail;
07203             char *myserveremail = serveremail;
07204             
07205             /* get destination mailbox */
07206             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07207             if (!dstvms) {
07208                dstvms = create_vm_state_from_user(vmtmp);
07209             }
07210             if (dstvms) {
07211                init_mailstream(dstvms, 0);
07212                if (!dstvms->mailstream) {
07213                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07214                } else {
07215                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07216                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07217                }
07218             } else {
07219                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07220             }
07221             if (!ast_strlen_zero(vmtmp->serveremail))
07222                myserveremail = vmtmp->serveremail;
07223             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07224             /* NULL category for IMAP storage */
07225             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07226                dstvms->curbox,
07227                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07228                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07229                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07230                NULL, urgent_str);
07231 #else
07232             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07233 #endif
07234             saved_messages++;
07235             AST_LIST_REMOVE_CURRENT(list);
07236             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07237             free_user(vmtmp);
07238             if (res)
07239                break;
07240          }
07241          AST_LIST_TRAVERSE_SAFE_END;
07242          if (saved_messages > 0 && !copy_msg_result) {
07243             /* give confirmation that the message was saved */
07244             /* commented out since we can't forward batches yet
07245             if (saved_messages == 1)
07246                res = ast_play_and_wait(chan, "vm-message");
07247             else
07248                res = ast_play_and_wait(chan, "vm-messages");
07249             if (!res)
07250                res = ast_play_and_wait(chan, "vm-saved"); */
07251 #ifdef IMAP_STORAGE
07252             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07253             if (ast_strlen_zero(vmstmp.introfn))
07254 #endif
07255             res = ast_play_and_wait(chan, "vm-msgsaved");
07256          }
07257 #ifndef IMAP_STORAGE
07258          else {
07259             /* with IMAP, mailbox full warning played by imap_check_limits */
07260             res = ast_play_and_wait(chan, "vm-mailboxfull");
07261          }
07262          /* Restore original message without prepended message if backup exists */
07263          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07264          strcpy(textfile, msgfile);
07265          strcpy(backup, msgfile);
07266          strcpy(backup_textfile, msgfile);
07267          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07268          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07269          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07270          if (ast_fileexists(backup, NULL, NULL) > 0) {
07271             ast_filerename(backup, msgfile, NULL);
07272             rename(backup_textfile, textfile);
07273          }
07274 #endif
07275       }
07276       DISPOSE(dir, curmsg);
07277 #ifndef IMAP_STORAGE
07278       if (cmd) { /* assuming hangup, cleanup backup file */
07279          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07280          strcpy(textfile, msgfile);
07281          strcpy(backup_textfile, msgfile);
07282          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07283          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07284          rename(backup_textfile, textfile);
07285       }
07286 #endif
07287    }
07288 
07289    /* If anything failed above, we still have this list to free */
07290    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07291       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07292       free_user(vmtmp);
07293    }
07294    return res ? res : cmd;
07295 }
07296 
07297 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07298 {
07299    int res;
07300    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07301       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07302    return res;
07303 }
07304 
07305 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07306 {
07307    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);
07308 }
07309 
07310 static int play_message_category(struct ast_channel *chan, const char *category)
07311 {
07312    int res = 0;
07313 
07314    if (!ast_strlen_zero(category))
07315       res = ast_play_and_wait(chan, category);
07316 
07317    if (res) {
07318       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07319       res = 0;
07320    }
07321 
07322    return res;
07323 }
07324 
07325 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07326 {
07327    int res = 0;
07328    struct vm_zone *the_zone = NULL;
07329    time_t t;
07330 
07331    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07332       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07333       return 0;
07334    }
07335 
07336    /* Does this user have a timezone specified? */
07337    if (!ast_strlen_zero(vmu->zonetag)) {
07338       /* Find the zone in the list */
07339       struct vm_zone *z;
07340       AST_LIST_LOCK(&zones);
07341       AST_LIST_TRAVERSE(&zones, z, list) {
07342          if (!strcmp(z->name, vmu->zonetag)) {
07343             the_zone = z;
07344             break;
07345          }
07346       }
07347       AST_LIST_UNLOCK(&zones);
07348    }
07349 
07350 /* No internal variable parsing for now, so we'll comment it out for the time being */
07351 #if 0
07352    /* Set the DIFF_* variables */
07353    ast_localtime(&t, &time_now, NULL);
07354    tv_now = ast_tvnow();
07355    ast_localtime(&tv_now, &time_then, NULL);
07356 
07357    /* Day difference */
07358    if (time_now.tm_year == time_then.tm_year)
07359       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07360    else
07361       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07362    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07363 
07364    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07365 #endif
07366    if (the_zone) {
07367       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07368    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07369       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07370    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07371       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07372    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07373       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);
07374    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07375       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07376    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07377       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07378    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07379       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07380    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07381       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);
07382    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07383       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07384    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07385       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07386    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07387       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);
07388    } else {
07389       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07390    }
07391 #if 0
07392    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07393 #endif
07394    return res;
07395 }
07396 
07397 
07398 
07399 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07400 {
07401    int res = 0;
07402    int i;
07403    char *callerid, *name;
07404    char prefile[PATH_MAX] = "";
07405    
07406 
07407    /* If voicemail cid is not enabled, or we didn't get cid or context from
07408     * the attribute file, leave now.
07409     *
07410     * TODO Still need to change this so that if this function is called by the
07411     * message envelope (and someone is explicitly requesting to hear the CID),
07412     * it does not check to see if CID is enabled in the config file.
07413     */
07414    if ((cid == NULL)||(context == NULL))
07415       return res;
07416 
07417    /* Strip off caller ID number from name */
07418    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07419    ast_callerid_parse(cid, &name, &callerid);
07420    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07421       /* Check for internal contexts and only */
07422       /* say extension when the call didn't come from an internal context in the list */
07423       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07424          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07425          if ((strcmp(cidinternalcontexts[i], context) == 0))
07426             break;
07427       }
07428       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07429          if (!res) {
07430             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07431             if (!ast_strlen_zero(prefile)) {
07432             /* See if we can find a recorded name for this person instead of their extension number */
07433                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07434                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07435                   if (!callback)
07436                      res = wait_file2(chan, vms, "vm-from");
07437                   res = ast_stream_and_wait(chan, prefile, "");
07438                } else {
07439                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07440                   /* Say "from extension" as one saying to sound smoother */
07441                   if (!callback)
07442                      res = wait_file2(chan, vms, "vm-from-extension");
07443                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07444                }
07445             }
07446          }
07447       } else if (!res) {
07448          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07449          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07450          if (!callback)
07451             res = wait_file2(chan, vms, "vm-from-phonenumber");
07452          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07453       }
07454    } else {
07455       /* Number unknown */
07456       ast_debug(1, "VM-CID: From an unknown number\n");
07457       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07458       res = wait_file2(chan, vms, "vm-unknown-caller");
07459    }
07460    return res;
07461 }
07462 
07463 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07464 {
07465    int res = 0;
07466    int durationm;
07467    int durations;
07468    /* Verify that we have a duration for the message */
07469    if (duration == NULL)
07470       return res;
07471 
07472    /* Convert from seconds to minutes */
07473    durations = atoi(duration);
07474    durationm = (durations / 60);
07475 
07476    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07477 
07478    if ((!res) && (durationm >= minduration)) {
07479       res = wait_file2(chan, vms, "vm-duration");
07480 
07481       /* POLISH syntax */
07482       if (!strncasecmp(chan->language, "pl", 2)) {
07483          div_t num = div(durationm, 10);
07484 
07485          if (durationm == 1) {
07486             res = ast_play_and_wait(chan, "digits/1z");
07487             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07488          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07489             if (num.rem == 2) {
07490                if (!num.quot) {
07491                   res = ast_play_and_wait(chan, "digits/2-ie");
07492                } else {
07493                   res = say_and_wait(chan, durationm - 2 , chan->language);
07494                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07495                }
07496             } else {
07497                res = say_and_wait(chan, durationm, chan->language);
07498             }
07499             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07500          } else {
07501             res = say_and_wait(chan, durationm, chan->language);
07502             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07503          }
07504       /* DEFAULT syntax */
07505       } else {
07506          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07507          res = wait_file2(chan, vms, "vm-minutes");
07508       }
07509    }
07510    return res;
07511 }
07512 
07513 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07514 {
07515    int res = 0;
07516    char filename[256], *cid;
07517    const char *origtime, *context, *category, *duration, *flag;
07518    struct ast_config *msg_cfg;
07519    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07520 
07521    vms->starting = 0;
07522    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07523    adsi_message(chan, vms);
07524    if (!vms->curmsg)
07525       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07526    else if (vms->curmsg == vms->lastmsg)
07527       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07528 
07529    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07530    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07531    msg_cfg = ast_config_load(filename, config_flags);
07532    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07533       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07534       return 0;
07535    }
07536    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07537 
07538    /* Play the word urgent if we are listening to urgent messages */
07539    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07540       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07541    }
07542 
07543    if (!res) {
07544       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07545       /* POLISH syntax */
07546       if (!strncasecmp(chan->language, "pl", 2)) {
07547          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07548             int ten, one;
07549             char nextmsg[256];
07550             ten = (vms->curmsg + 1) / 10;
07551             one = (vms->curmsg + 1) % 10;
07552 
07553             if (vms->curmsg < 20) {
07554                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07555                res = wait_file2(chan, vms, nextmsg);
07556             } else {
07557                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07558                res = wait_file2(chan, vms, nextmsg);
07559                if (one > 0) {
07560                   if (!res) {
07561                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07562                      res = wait_file2(chan, vms, nextmsg);
07563                   }
07564                }
07565             }
07566          }
07567          if (!res)
07568             res = wait_file2(chan, vms, "vm-message");
07569       /* HEBREW syntax */
07570       } else if (!strncasecmp(chan->language, "he", 2)) {
07571          if (!vms->curmsg) {
07572             res = wait_file2(chan, vms, "vm-message");
07573             res = wait_file2(chan, vms, "vm-first");
07574          } else if (vms->curmsg == vms->lastmsg) {
07575             res = wait_file2(chan, vms, "vm-message");
07576             res = wait_file2(chan, vms, "vm-last");
07577          } else {
07578             res = wait_file2(chan, vms, "vm-message");
07579             res = wait_file2(chan, vms, "vm-number");
07580             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07581          }
07582       /* VIETNAMESE syntax */
07583       } else if (!strncasecmp(chan->language, "vi", 2)) {
07584          if (!vms->curmsg) {
07585             res = wait_file2(chan, vms, "vm-message");
07586             res = wait_file2(chan, vms, "vm-first");
07587          } else if (vms->curmsg == vms->lastmsg) {
07588             res = wait_file2(chan, vms, "vm-message");
07589             res = wait_file2(chan, vms, "vm-last");
07590          } else {
07591             res = wait_file2(chan, vms, "vm-message");
07592             res = wait_file2(chan, vms, "vm-number");
07593             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07594          }
07595       } else {
07596          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07597             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07598          } else { /* DEFAULT syntax */
07599             res = wait_file2(chan, vms, "vm-message");
07600          }
07601          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07602             if (!res) {
07603                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07604             }
07605          }
07606       }
07607    }
07608 
07609    if (!msg_cfg) {
07610       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07611       return 0;
07612    }
07613 
07614    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07615       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07616       DISPOSE(vms->curdir, vms->curmsg);
07617       ast_config_destroy(msg_cfg);
07618       return 0;
07619    }
07620 
07621    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07622    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07623    category = ast_variable_retrieve(msg_cfg, "message", "category");
07624 
07625    context = ast_variable_retrieve(msg_cfg, "message", "context");
07626    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07627       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07628    if (!res) {
07629       res = play_message_category(chan, category);
07630    }
07631    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07632       res = play_message_datetime(chan, vmu, origtime, filename);
07633    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07634       res = play_message_callerid(chan, vms, cid, context, 0);
07635    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07636       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07637    /* Allow pressing '1' to skip envelope / callerid */
07638    if (res == '1')
07639       res = 0;
07640    ast_config_destroy(msg_cfg);
07641 
07642    if (!res) {
07643       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07644       vms->heard[vms->curmsg] = 1;
07645 #ifdef IMAP_STORAGE
07646       /*IMAP storage stores any prepended message from a forward
07647        * as a separate file from the rest of the message
07648        */
07649       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07650          wait_file(chan, vms, vms->introfn);
07651       }
07652 #endif
07653       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07654          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07655          res = 0;
07656       }
07657    }
07658    DISPOSE(vms->curdir, vms->curmsg);
07659    return res;
07660 }
07661 
07662 #ifdef IMAP_STORAGE
07663 static int imap_remove_file(char *dir, int msgnum)
07664 {
07665    char fn[PATH_MAX];
07666    char full_fn[PATH_MAX];
07667    char intro[PATH_MAX] = {0,};
07668    
07669    if (msgnum > -1) {
07670       make_file(fn, sizeof(fn), dir, msgnum);
07671       snprintf(intro, sizeof(intro), "%sintro", fn);
07672    } else
07673       ast_copy_string(fn, dir, sizeof(fn));
07674    
07675    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07676       ast_filedelete(fn, NULL);
07677       if (!ast_strlen_zero(intro)) {
07678          ast_filedelete(intro, NULL);
07679       }
07680       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07681       unlink(full_fn);
07682    }
07683    return 0;
07684 }
07685 
07686 
07687 
07688 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07689 {
07690    char *file, *filename;
07691    char *attachment;
07692    char arg[10];
07693    int i;
07694    BODY* body;
07695 
07696    file = strrchr(ast_strdupa(dir), '/');
07697    if (file) {
07698       *file++ = '\0';
07699    } else {
07700       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07701       return -1;
07702    }
07703 
07704    ast_mutex_lock(&vms->lock);
07705    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07706       mail_fetchstructure(vms->mailstream, i + 1, &body);
07707       /* We have the body, now we extract the file name of the first attachment. */
07708       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07709          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07710       } else {
07711          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07712          ast_mutex_unlock(&vms->lock);
07713          return -1;
07714       }
07715       filename = strsep(&attachment, ".");
07716       if (!strcmp(filename, file)) {
07717          sprintf(arg, "%d", i + 1);
07718          mail_setflag(vms->mailstream, arg, "\\DELETED");
07719       }
07720    }
07721    mail_expunge(vms->mailstream);
07722    ast_mutex_unlock(&vms->lock);
07723    return 0;
07724 }
07725 
07726 #elif !defined(IMAP_STORAGE)
07727 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07728 {
07729    int count_msg, last_msg;
07730 
07731    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07732 
07733    /* Rename the member vmbox HERE so that we don't try to return before
07734     * we know what's going on.
07735     */
07736    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07737 
07738    /* Faster to make the directory than to check if it exists. */
07739    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07740 
07741    /* traverses directory using readdir (or select query for ODBC) */
07742    count_msg = count_messages(vmu, vms->curdir);
07743    if (count_msg < 0) {
07744       return count_msg;
07745    } else {
07746       vms->lastmsg = count_msg - 1;
07747    }
07748 
07749    if (vm_allocate_dh(vms, vmu, count_msg)) {
07750       return -1;
07751    }
07752 
07753    /*
07754    The following test is needed in case sequencing gets messed up.
07755    There appears to be more than one way to mess up sequence, so
07756    we will not try to find all of the root causes--just fix it when
07757    detected.
07758    */
07759 
07760    if (vm_lock_path(vms->curdir)) {
07761       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07762       return ERROR_LOCK_PATH;
07763    }
07764 
07765    /* for local storage, checks directory for messages up to maxmsg limit */
07766    last_msg = last_message_index(vmu, vms->curdir);
07767    ast_unlock_path(vms->curdir);
07768 
07769    if (last_msg < -1) {
07770       return last_msg;
07771 #ifndef ODBC_STORAGE
07772    } else if (vms->lastmsg != last_msg) {
07773       ast_log(LOG_NOTICE, "Resequencing mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07774         resequence_mailbox(vmu, vms->curdir, count_msg);
07775 #endif
07776    }
07777 
07778    return 0;
07779 }
07780 #endif
07781 
07782 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07783 {
07784    int x = 0;
07785 #ifndef IMAP_STORAGE
07786    int res = 0, nummsg;
07787    char fn2[PATH_MAX];
07788 #endif
07789 
07790    if (vms->lastmsg <= -1) {
07791       goto done;
07792    }
07793 
07794    vms->curmsg = -1;
07795 #ifndef IMAP_STORAGE
07796    /* Get the deleted messages fixed */
07797    if (vm_lock_path(vms->curdir)) {
07798       return ERROR_LOCK_PATH;
07799    }
07800 
07801    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07802    for (x = 0; x < vms->lastmsg + 1; x++) {
07803       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07804          /* Save this message.  It's not in INBOX or hasn't been heard */
07805          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07806          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07807             break;
07808          }
07809          vms->curmsg++;
07810          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07811          if (strcmp(vms->fn, fn2)) {
07812             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07813          }
07814       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07815          /* Move to old folder before deleting */
07816          res = save_to_folder(vmu, vms, x, 1);
07817          if (res == ERROR_LOCK_PATH) {
07818             /* If save failed do not delete the message */
07819             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07820             vms->deleted[x] = 0;
07821             vms->heard[x] = 0;
07822             --x;
07823          }
07824       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07825          /* Move to deleted folder */
07826          res = save_to_folder(vmu, vms, x, 10);
07827          if (res == ERROR_LOCK_PATH) {
07828             /* If save failed do not delete the message */
07829             vms->deleted[x] = 0;
07830             vms->heard[x] = 0;
07831             --x;
07832          }
07833       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07834          /* If realtime storage enabled - we should explicitly delete this message,
07835          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07836          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07837          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07838             DELETE(vms->curdir, x, vms->fn, vmu);
07839          }
07840       }
07841    }
07842 
07843    /* Delete ALL remaining messages */
07844    nummsg = x - 1;
07845    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07846       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07847       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07848          DELETE(vms->curdir, x, vms->fn, vmu);
07849       }
07850    }
07851    ast_unlock_path(vms->curdir);
07852 #else /* defined(IMAP_STORAGE) */
07853    if (vms->deleted) {
07854       /* Since we now expunge after each delete, deleting in reverse order
07855        * ensures that no reordering occurs between each step. */
07856       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07857          if (vms->deleted[x]) {
07858             ast_debug(3, "IMAP delete of %d\n", x);
07859             DELETE(vms->curdir, x, vms->fn, vmu);
07860          }
07861       }
07862    }
07863 #endif
07864 
07865 done:
07866    if (vms->deleted && vmu->maxmsg) {
07867       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07868    }
07869    if (vms->heard && vmu->maxmsg) {
07870       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07871    }
07872 
07873    return 0;
07874 }
07875 
07876 /* In Greek even though we CAN use a syntax like "friends messages"
07877  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07878  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07879  * syntax for the above three categories which is more elegant.
07880  */
07881 
07882 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07883 {
07884    int cmd;
07885    char *buf;
07886 
07887    buf = alloca(strlen(box) + 2);
07888    strcpy(buf, box);
07889    strcat(buf, "s");
07890 
07891    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07892       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07893       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07894    } else {
07895       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07896       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07897    }
07898 }
07899 
07900 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07901 {
07902    int cmd;
07903 
07904    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07905       if (!strcasecmp(box, "vm-INBOX"))
07906          cmd = ast_play_and_wait(chan, "vm-new-e");
07907       else
07908          cmd = ast_play_and_wait(chan, "vm-old-e");
07909       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07910    } else {
07911       cmd = ast_play_and_wait(chan, "vm-messages");
07912       return cmd ? cmd : ast_play_and_wait(chan, box);
07913    }
07914 }
07915 
07916 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07917 {
07918    int cmd;
07919 
07920    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07921       cmd = ast_play_and_wait(chan, "vm-messages");
07922       return cmd ? cmd : ast_play_and_wait(chan, box);
07923    } else {
07924       cmd = ast_play_and_wait(chan, box);
07925       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07926    }
07927 }
07928 
07929 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07930 {
07931    int cmd;
07932 
07933    if (  !strncasecmp(chan->language, "it", 2) ||
07934         !strncasecmp(chan->language, "es", 2) ||
07935         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07936       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07937       return cmd ? cmd : ast_play_and_wait(chan, box);
07938    } else if (!strncasecmp(chan->language, "gr", 2)) {
07939       return vm_play_folder_name_gr(chan, box);
07940    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07941       return ast_play_and_wait(chan, box);
07942    } else if (!strncasecmp(chan->language, "pl", 2)) {
07943       return vm_play_folder_name_pl(chan, box);
07944    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07945       return vm_play_folder_name_ua(chan, box);
07946    } else if (!strncasecmp(chan->language, "vi", 2)) {
07947       return ast_play_and_wait(chan, box);
07948    } else {  /* Default English */
07949       cmd = ast_play_and_wait(chan, box);
07950       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07951    }
07952 }
07953 
07954 /* GREEK SYNTAX
07955    In greek the plural for old/new is
07956    different so we need the following files
07957    We also need vm-denExeteMynhmata because
07958    this syntax is different.
07959 
07960    -> vm-Olds.wav : "Palia"
07961    -> vm-INBOXs.wav : "Nea"
07962    -> vm-denExeteMynhmata : "den exete mynhmata"
07963 */
07964 
07965 
07966 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07967 {
07968    int res = 0;
07969 
07970    if (vms->newmessages) {
07971       res = ast_play_and_wait(chan, "vm-youhave");
07972       if (!res) 
07973          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07974       if (!res) {
07975          if ((vms->newmessages == 1)) {
07976             res = ast_play_and_wait(chan, "vm-INBOX");
07977             if (!res)
07978                res = ast_play_and_wait(chan, "vm-message");
07979          } else {
07980             res = ast_play_and_wait(chan, "vm-INBOXs");
07981             if (!res)
07982                res = ast_play_and_wait(chan, "vm-messages");
07983          }
07984       }
07985    } else if (vms->oldmessages){
07986       res = ast_play_and_wait(chan, "vm-youhave");
07987       if (!res)
07988          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07989       if ((vms->oldmessages == 1)){
07990          res = ast_play_and_wait(chan, "vm-Old");
07991          if (!res)
07992             res = ast_play_and_wait(chan, "vm-message");
07993       } else {
07994          res = ast_play_and_wait(chan, "vm-Olds");
07995          if (!res)
07996             res = ast_play_and_wait(chan, "vm-messages");
07997       }
07998    } else if (!vms->oldmessages && !vms->newmessages) 
07999       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08000    return res;
08001 }
08002 
08003 /* Version of vm_intro() designed to work for many languages.
08004  *
08005  * It is hoped that this function can prevent the proliferation of 
08006  * language-specific vm_intro() functions and in time replace the language-
08007  * specific functions which already exist.  An examination of the language-
08008  * specific functions revealed that they all corrected the same deficiencies
08009  * in vm_intro_en() (which was the default function). Namely:
08010  *
08011  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08012  *     wording of the voicemail greeting hides this problem.  For example,
08013  *     vm-INBOX contains only the word "new".  This means that both of these
08014  *     sequences produce valid utterances:
08015  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08016  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08017  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08018  *     in many languages) the first utterance becomes "you have 1 the new message".
08019  *  2) The function contains hardcoded rules for pluralizing the word "message".
08020  *     These rules are correct for English, but not for many other languages.
08021  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08022  *     required in many languages.
08023  *  4) The gender of the word for "message" is not specified. This is a problem
08024  *     because in many languages the gender of the number in phrases such
08025  *     as "you have one new message" must match the gender of the word
08026  *     meaning "message".
08027  *
08028  * Fixing these problems for each new language has meant duplication of effort.
08029  * This new function solves the problems in the following general ways:
08030  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08031  *     and vm-Old respectively for those languages where it makes sense.
08032  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08033  *     on vm-message.
08034  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08035  *     prefix on vm-new and vm-old (none for English).
08036  *  4) Pass the gender of the language's word for "message" as an agument to
08037  *     this function which is can in turn pass on to the functions which 
08038  *     say numbers and put endings on nounds and adjectives.
08039  *
08040  * All languages require these messages:
08041  *  vm-youhave    "You have..."
08042  *  vm-and     "and"
08043  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08044  *
08045  * To use it for English, you will need these additional sound files:
08046  *  vm-new     "new"
08047  *  vm-message    "message", singular
08048  *  vm-messages      "messages", plural
08049  *
08050  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08051  *
08052  *  vm-newn    "novoye" (singular, neuter)
08053  *  vm-newx    "novikh" (counting plural form, genative plural)
08054  *  vm-message    "sobsheniye" (singular form)
08055  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08056  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08057  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08058  *  digits/2n     "dva" (neuter singular)
08059  */
08060 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08061 {
08062    int res;
08063    int lastnum = 0;
08064 
08065    res = ast_play_and_wait(chan, "vm-youhave");
08066 
08067    if (!res && vms->newmessages) {
08068       lastnum = vms->newmessages;
08069 
08070       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08071          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08072       }
08073 
08074       if (!res && vms->oldmessages) {
08075          res = ast_play_and_wait(chan, "vm-and");
08076       }
08077    }
08078 
08079    if (!res && vms->oldmessages) {
08080       lastnum = vms->oldmessages;
08081 
08082       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08083          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08084       }
08085    }
08086 
08087    if (!res) {
08088       if (lastnum == 0) {
08089          res = ast_play_and_wait(chan, "vm-no");
08090       }
08091       if (!res) {
08092          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08093       }
08094    }
08095 
08096    return res;
08097 }
08098 
08099 /* Default Hebrew syntax */
08100 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08101 {
08102    int res = 0;
08103 
08104    /* Introduce messages they have */
08105    if (!res) {
08106       if ((vms->newmessages) || (vms->oldmessages)) {
08107          res = ast_play_and_wait(chan, "vm-youhave");
08108       }
08109       /*
08110        * The word "shtei" refers to the number 2 in hebrew when performing a count
08111        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08112        * an element, this is one of them.
08113        */
08114       if (vms->newmessages) {
08115          if (!res) {
08116             if (vms->newmessages == 1) {
08117                res = ast_play_and_wait(chan, "vm-INBOX1");
08118             } else {
08119                if (vms->newmessages == 2) {
08120                   res = ast_play_and_wait(chan, "vm-shtei");
08121                } else {
08122                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08123                }
08124                res = ast_play_and_wait(chan, "vm-INBOX");
08125             }
08126          }
08127          if (vms->oldmessages && !res) {
08128             res = ast_play_and_wait(chan, "vm-and");
08129             if (vms->oldmessages == 1) {
08130                res = ast_play_and_wait(chan, "vm-Old1");
08131             } else {
08132                if (vms->oldmessages == 2) {
08133                   res = ast_play_and_wait(chan, "vm-shtei");
08134                } else {
08135                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08136                }
08137                res = ast_play_and_wait(chan, "vm-Old");
08138             }
08139          }
08140       }
08141       if (!res && vms->oldmessages && !vms->newmessages) {
08142          if (!res) {
08143             if (vms->oldmessages == 1) {
08144                res = ast_play_and_wait(chan, "vm-Old1");
08145             } else {
08146                if (vms->oldmessages == 2) {
08147                   res = ast_play_and_wait(chan, "vm-shtei");
08148                } else {
08149                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08150                }
08151                res = ast_play_and_wait(chan, "vm-Old");
08152             }
08153          }
08154       }
08155       if (!res) {
08156          if (!vms->oldmessages && !vms->newmessages) {
08157             if (!res) {
08158                res = ast_play_and_wait(chan, "vm-nomessages");
08159             }
08160          }
08161       }
08162    }
08163    return res;
08164 }
08165    
08166 /* Default English syntax */
08167 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08168 {
08169    int res;
08170 
08171    /* Introduce messages they have */
08172    res = ast_play_and_wait(chan, "vm-youhave");
08173    if (!res) {
08174       if (vms->urgentmessages) {
08175          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08176          if (!res)
08177             res = ast_play_and_wait(chan, "vm-Urgent");
08178          if ((vms->oldmessages || vms->newmessages) && !res) {
08179             res = ast_play_and_wait(chan, "vm-and");
08180          } else if (!res) {
08181             if ((vms->urgentmessages == 1))
08182                res = ast_play_and_wait(chan, "vm-message");
08183             else
08184                res = ast_play_and_wait(chan, "vm-messages");
08185          }
08186       }
08187       if (vms->newmessages) {
08188          res = say_and_wait(chan, vms->newmessages, chan->language);
08189          if (!res)
08190             res = ast_play_and_wait(chan, "vm-INBOX");
08191          if (vms->oldmessages && !res)
08192             res = ast_play_and_wait(chan, "vm-and");
08193          else if (!res) {
08194             if ((vms->newmessages == 1))
08195                res = ast_play_and_wait(chan, "vm-message");
08196             else
08197                res = ast_play_and_wait(chan, "vm-messages");
08198          }
08199             
08200       }
08201       if (!res && vms->oldmessages) {
08202          res = say_and_wait(chan, vms->oldmessages, chan->language);
08203          if (!res)
08204             res = ast_play_and_wait(chan, "vm-Old");
08205          if (!res) {
08206             if (vms->oldmessages == 1)
08207                res = ast_play_and_wait(chan, "vm-message");
08208             else
08209                res = ast_play_and_wait(chan, "vm-messages");
08210          }
08211       }
08212       if (!res) {
08213          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08214             res = ast_play_and_wait(chan, "vm-no");
08215             if (!res)
08216                res = ast_play_and_wait(chan, "vm-messages");
08217          }
08218       }
08219    }
08220    return res;
08221 }
08222 
08223 /* ITALIAN syntax */
08224 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08225 {
08226    /* Introduce messages they have */
08227    int res;
08228    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08229       res = ast_play_and_wait(chan, "vm-no") ||
08230          ast_play_and_wait(chan, "vm-message");
08231    else
08232       res = ast_play_and_wait(chan, "vm-youhave");
08233    if (!res && vms->newmessages) {
08234       res = (vms->newmessages == 1) ?
08235          ast_play_and_wait(chan, "digits/un") ||
08236          ast_play_and_wait(chan, "vm-nuovo") ||
08237          ast_play_and_wait(chan, "vm-message") :
08238          /* 2 or more new messages */
08239          say_and_wait(chan, vms->newmessages, chan->language) ||
08240          ast_play_and_wait(chan, "vm-nuovi") ||
08241          ast_play_and_wait(chan, "vm-messages");
08242       if (!res && vms->oldmessages)
08243          res = ast_play_and_wait(chan, "vm-and");
08244    }
08245    if (!res && vms->oldmessages) {
08246       res = (vms->oldmessages == 1) ?
08247          ast_play_and_wait(chan, "digits/un") ||
08248          ast_play_and_wait(chan, "vm-vecchio") ||
08249          ast_play_and_wait(chan, "vm-message") :
08250          /* 2 or more old messages */
08251          say_and_wait(chan, vms->oldmessages, chan->language) ||
08252          ast_play_and_wait(chan, "vm-vecchi") ||
08253          ast_play_and_wait(chan, "vm-messages");
08254    }
08255    return res;
08256 }
08257 
08258 /* POLISH syntax */
08259 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08260 {
08261    /* Introduce messages they have */
08262    int res;
08263    div_t num;
08264 
08265    if (!vms->oldmessages && !vms->newmessages) {
08266       res = ast_play_and_wait(chan, "vm-no");
08267       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08268       return res;
08269    } else {
08270       res = ast_play_and_wait(chan, "vm-youhave");
08271    }
08272 
08273    if (vms->newmessages) {
08274       num = div(vms->newmessages, 10);
08275       if (vms->newmessages == 1) {
08276          res = ast_play_and_wait(chan, "digits/1-a");
08277          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08278          res = res ? res : ast_play_and_wait(chan, "vm-message");
08279       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08280          if (num.rem == 2) {
08281             if (!num.quot) {
08282                res = ast_play_and_wait(chan, "digits/2-ie");
08283             } else {
08284                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08285                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08286             }
08287          } else {
08288             res = say_and_wait(chan, vms->newmessages, chan->language);
08289          }
08290          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08291          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08292       } else {
08293          res = say_and_wait(chan, vms->newmessages, chan->language);
08294          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08295          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08296       }
08297       if (!res && vms->oldmessages)
08298          res = ast_play_and_wait(chan, "vm-and");
08299    }
08300    if (!res && vms->oldmessages) {
08301       num = div(vms->oldmessages, 10);
08302       if (vms->oldmessages == 1) {
08303          res = ast_play_and_wait(chan, "digits/1-a");
08304          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08305          res = res ? res : ast_play_and_wait(chan, "vm-message");
08306       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08307          if (num.rem == 2) {
08308             if (!num.quot) {
08309                res = ast_play_and_wait(chan, "digits/2-ie");
08310             } else {
08311                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08312                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08313             }
08314          } else {
08315             res = say_and_wait(chan, vms->oldmessages, chan->language);
08316          }
08317          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08318          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08319       } else {
08320          res = say_and_wait(chan, vms->oldmessages, chan->language);
08321          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08322          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08323       }
08324    }
08325 
08326    return res;
08327 }
08328 
08329 /* SWEDISH syntax */
08330 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08331 {
08332    /* Introduce messages they have */
08333    int res;
08334 
08335    res = ast_play_and_wait(chan, "vm-youhave");
08336    if (res)
08337       return res;
08338 
08339    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08340       res = ast_play_and_wait(chan, "vm-no");
08341       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08342       return res;
08343    }
08344 
08345    if (vms->newmessages) {
08346       if ((vms->newmessages == 1)) {
08347          res = ast_play_and_wait(chan, "digits/ett");
08348          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08349          res = res ? res : ast_play_and_wait(chan, "vm-message");
08350       } else {
08351          res = say_and_wait(chan, vms->newmessages, chan->language);
08352          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08353          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08354       }
08355       if (!res && vms->oldmessages)
08356          res = ast_play_and_wait(chan, "vm-and");
08357    }
08358    if (!res && vms->oldmessages) {
08359       if (vms->oldmessages == 1) {
08360          res = ast_play_and_wait(chan, "digits/ett");
08361          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08362          res = res ? res : ast_play_and_wait(chan, "vm-message");
08363       } else {
08364          res = say_and_wait(chan, vms->oldmessages, chan->language);
08365          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08366          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08367       }
08368    }
08369 
08370    return res;
08371 }
08372 
08373 /* NORWEGIAN syntax */
08374 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08375 {
08376    /* Introduce messages they have */
08377    int res;
08378 
08379    res = ast_play_and_wait(chan, "vm-youhave");
08380    if (res)
08381       return res;
08382 
08383    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08384       res = ast_play_and_wait(chan, "vm-no");
08385       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08386       return res;
08387    }
08388 
08389    if (vms->newmessages) {
08390       if ((vms->newmessages == 1)) {
08391          res = ast_play_and_wait(chan, "digits/1");
08392          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08393          res = res ? res : ast_play_and_wait(chan, "vm-message");
08394       } else {
08395          res = say_and_wait(chan, vms->newmessages, chan->language);
08396          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08397          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08398       }
08399       if (!res && vms->oldmessages)
08400          res = ast_play_and_wait(chan, "vm-and");
08401    }
08402    if (!res && vms->oldmessages) {
08403       if (vms->oldmessages == 1) {
08404          res = ast_play_and_wait(chan, "digits/1");
08405          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08406          res = res ? res : ast_play_and_wait(chan, "vm-message");
08407       } else {
08408          res = say_and_wait(chan, vms->oldmessages, chan->language);
08409          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08410          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08411       }
08412    }
08413 
08414    return res;
08415 }
08416 
08417 /* GERMAN syntax */
08418 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08419 {
08420    /* Introduce messages they have */
08421    int res;
08422    res = ast_play_and_wait(chan, "vm-youhave");
08423    if (!res) {
08424       if (vms->newmessages) {
08425          if ((vms->newmessages == 1))
08426             res = ast_play_and_wait(chan, "digits/1F");
08427          else
08428             res = say_and_wait(chan, vms->newmessages, chan->language);
08429          if (!res)
08430             res = ast_play_and_wait(chan, "vm-INBOX");
08431          if (vms->oldmessages && !res)
08432             res = ast_play_and_wait(chan, "vm-and");
08433          else if (!res) {
08434             if ((vms->newmessages == 1))
08435                res = ast_play_and_wait(chan, "vm-message");
08436             else
08437                res = ast_play_and_wait(chan, "vm-messages");
08438          }
08439             
08440       }
08441       if (!res && vms->oldmessages) {
08442          if (vms->oldmessages == 1)
08443             res = ast_play_and_wait(chan, "digits/1F");
08444          else
08445             res = say_and_wait(chan, vms->oldmessages, chan->language);
08446          if (!res)
08447             res = ast_play_and_wait(chan, "vm-Old");
08448          if (!res) {
08449             if (vms->oldmessages == 1)
08450                res = ast_play_and_wait(chan, "vm-message");
08451             else
08452                res = ast_play_and_wait(chan, "vm-messages");
08453          }
08454       }
08455       if (!res) {
08456          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08457             res = ast_play_and_wait(chan, "vm-no");
08458             if (!res)
08459                res = ast_play_and_wait(chan, "vm-messages");
08460          }
08461       }
08462    }
08463    return res;
08464 }
08465 
08466 /* SPANISH syntax */
08467 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08468 {
08469    /* Introduce messages they have */
08470    int res;
08471    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08472       res = ast_play_and_wait(chan, "vm-youhaveno");
08473       if (!res)
08474          res = ast_play_and_wait(chan, "vm-messages");
08475    } else {
08476       res = ast_play_and_wait(chan, "vm-youhave");
08477    }
08478    if (!res) {
08479       if (vms->newmessages) {
08480          if (!res) {
08481             if ((vms->newmessages == 1)) {
08482                res = ast_play_and_wait(chan, "digits/1M");
08483                if (!res)
08484                   res = ast_play_and_wait(chan, "vm-message");
08485                if (!res)
08486                   res = ast_play_and_wait(chan, "vm-INBOXs");
08487             } else {
08488                res = say_and_wait(chan, vms->newmessages, chan->language);
08489                if (!res)
08490                   res = ast_play_and_wait(chan, "vm-messages");
08491                if (!res)
08492                   res = ast_play_and_wait(chan, "vm-INBOX");
08493             }
08494          }
08495          if (vms->oldmessages && !res)
08496             res = ast_play_and_wait(chan, "vm-and");
08497       }
08498       if (vms->oldmessages) {
08499          if (!res) {
08500             if (vms->oldmessages == 1) {
08501                res = ast_play_and_wait(chan, "digits/1M");
08502                if (!res)
08503                   res = ast_play_and_wait(chan, "vm-message");
08504                if (!res)
08505                   res = ast_play_and_wait(chan, "vm-Olds");
08506             } else {
08507                res = say_and_wait(chan, vms->oldmessages, chan->language);
08508                if (!res)
08509                   res = ast_play_and_wait(chan, "vm-messages");
08510                if (!res)
08511                   res = ast_play_and_wait(chan, "vm-Old");
08512             }
08513          }
08514       }
08515    }
08516 return res;
08517 }
08518 
08519 /* BRAZILIAN PORTUGUESE syntax */
08520 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08521    /* Introduce messages they have */
08522    int res;
08523    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08524       res = ast_play_and_wait(chan, "vm-nomessages");
08525       return res;
08526    } else {
08527       res = ast_play_and_wait(chan, "vm-youhave");
08528    }
08529    if (vms->newmessages) {
08530       if (!res)
08531          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08532       if ((vms->newmessages == 1)) {
08533          if (!res)
08534             res = ast_play_and_wait(chan, "vm-message");
08535          if (!res)
08536             res = ast_play_and_wait(chan, "vm-INBOXs");
08537       } else {
08538          if (!res)
08539             res = ast_play_and_wait(chan, "vm-messages");
08540          if (!res)
08541             res = ast_play_and_wait(chan, "vm-INBOX");
08542       }
08543       if (vms->oldmessages && !res)
08544          res = ast_play_and_wait(chan, "vm-and");
08545    }
08546    if (vms->oldmessages) {
08547       if (!res)
08548          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08549       if (vms->oldmessages == 1) {
08550          if (!res)
08551             res = ast_play_and_wait(chan, "vm-message");
08552          if (!res)
08553             res = ast_play_and_wait(chan, "vm-Olds");
08554       } else {
08555          if (!res)
08556             res = ast_play_and_wait(chan, "vm-messages");
08557          if (!res)
08558             res = ast_play_and_wait(chan, "vm-Old");
08559       }
08560    }
08561    return res;
08562 }
08563 
08564 /* FRENCH syntax */
08565 static int vm_intro_fr(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             res = ast_play_and_wait(chan, "vm-INBOX");
08575          if (vms->oldmessages && !res)
08576             res = ast_play_and_wait(chan, "vm-and");
08577          else if (!res) {
08578             if ((vms->newmessages == 1))
08579                res = ast_play_and_wait(chan, "vm-message");
08580             else
08581                res = ast_play_and_wait(chan, "vm-messages");
08582          }
08583             
08584       }
08585       if (!res && vms->oldmessages) {
08586          res = say_and_wait(chan, vms->oldmessages, chan->language);
08587          if (!res)
08588             res = ast_play_and_wait(chan, "vm-Old");
08589          if (!res) {
08590             if (vms->oldmessages == 1)
08591                res = ast_play_and_wait(chan, "vm-message");
08592             else
08593                res = ast_play_and_wait(chan, "vm-messages");
08594          }
08595       }
08596       if (!res) {
08597          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08598             res = ast_play_and_wait(chan, "vm-no");
08599             if (!res)
08600                res = ast_play_and_wait(chan, "vm-messages");
08601          }
08602       }
08603    }
08604    return res;
08605 }
08606 
08607 /* DUTCH syntax */
08608 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08609 {
08610    /* Introduce messages they have */
08611    int res;
08612    res = ast_play_and_wait(chan, "vm-youhave");
08613    if (!res) {
08614       if (vms->newmessages) {
08615          res = say_and_wait(chan, vms->newmessages, chan->language);
08616          if (!res) {
08617             if (vms->newmessages == 1)
08618                res = ast_play_and_wait(chan, "vm-INBOXs");
08619             else
08620                res = ast_play_and_wait(chan, "vm-INBOX");
08621          }
08622          if (vms->oldmessages && !res)
08623             res = ast_play_and_wait(chan, "vm-and");
08624          else if (!res) {
08625             if ((vms->newmessages == 1))
08626                res = ast_play_and_wait(chan, "vm-message");
08627             else
08628                res = ast_play_and_wait(chan, "vm-messages");
08629          }
08630             
08631       }
08632       if (!res && vms->oldmessages) {
08633          res = say_and_wait(chan, vms->oldmessages, chan->language);
08634          if (!res) {
08635             if (vms->oldmessages == 1)
08636                res = ast_play_and_wait(chan, "vm-Olds");
08637             else
08638                res = ast_play_and_wait(chan, "vm-Old");
08639          }
08640          if (!res) {
08641             if (vms->oldmessages == 1)
08642                res = ast_play_and_wait(chan, "vm-message");
08643             else
08644                res = ast_play_and_wait(chan, "vm-messages");
08645          }
08646       }
08647       if (!res) {
08648          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08649             res = ast_play_and_wait(chan, "vm-no");
08650             if (!res)
08651                res = ast_play_and_wait(chan, "vm-messages");
08652          }
08653       }
08654    }
08655    return res;
08656 }
08657 
08658 /* PORTUGUESE syntax */
08659 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08660 {
08661    /* Introduce messages they have */
08662    int res;
08663    res = ast_play_and_wait(chan, "vm-youhave");
08664    if (!res) {
08665       if (vms->newmessages) {
08666          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08667          if (!res) {
08668             if ((vms->newmessages == 1)) {
08669                res = ast_play_and_wait(chan, "vm-message");
08670                if (!res)
08671                   res = ast_play_and_wait(chan, "vm-INBOXs");
08672             } else {
08673                res = ast_play_and_wait(chan, "vm-messages");
08674                if (!res)
08675                   res = ast_play_and_wait(chan, "vm-INBOX");
08676             }
08677          }
08678          if (vms->oldmessages && !res)
08679             res = ast_play_and_wait(chan, "vm-and");
08680       }
08681       if (!res && vms->oldmessages) {
08682          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08683          if (!res) {
08684             if (vms->oldmessages == 1) {
08685                res = ast_play_and_wait(chan, "vm-message");
08686                if (!res)
08687                   res = ast_play_and_wait(chan, "vm-Olds");
08688             } else {
08689                res = ast_play_and_wait(chan, "vm-messages");
08690                if (!res)
08691                   res = ast_play_and_wait(chan, "vm-Old");
08692             }
08693          }
08694       }
08695       if (!res) {
08696          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08697             res = ast_play_and_wait(chan, "vm-no");
08698             if (!res)
08699                res = ast_play_and_wait(chan, "vm-messages");
08700          }
08701       }
08702    }
08703    return res;
08704 }
08705 
08706 
08707 /* CZECH syntax */
08708 /* in czech there must be declension of word new and message
08709  * czech        : english        : czech      : english
08710  * --------------------------------------------------------
08711  * vm-youhave   : you have 
08712  * vm-novou     : one new        : vm-zpravu  : message
08713  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08714  * vm-novych    : 5-infinite new : vm-zprav   : messages
08715  * vm-starou   : one old
08716  * vm-stare     : 2-4 old 
08717  * vm-starych   : 5-infinite old
08718  * jednu        : one   - falling 4. 
08719  * vm-no        : no  ( no messages )
08720  */
08721 
08722 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08723 {
08724    int res;
08725    res = ast_play_and_wait(chan, "vm-youhave");
08726    if (!res) {
08727       if (vms->newmessages) {
08728          if (vms->newmessages == 1) {
08729             res = ast_play_and_wait(chan, "digits/jednu");
08730          } else {
08731             res = say_and_wait(chan, vms->newmessages, chan->language);
08732          }
08733          if (!res) {
08734             if ((vms->newmessages == 1))
08735                res = ast_play_and_wait(chan, "vm-novou");
08736             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08737                res = ast_play_and_wait(chan, "vm-nove");
08738             if (vms->newmessages > 4)
08739                res = ast_play_and_wait(chan, "vm-novych");
08740          }
08741          if (vms->oldmessages && !res)
08742             res = ast_play_and_wait(chan, "vm-and");
08743          else if (!res) {
08744             if ((vms->newmessages == 1))
08745                res = ast_play_and_wait(chan, "vm-zpravu");
08746             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08747                res = ast_play_and_wait(chan, "vm-zpravy");
08748             if (vms->newmessages > 4)
08749                res = ast_play_and_wait(chan, "vm-zprav");
08750          }
08751       }
08752       if (!res && vms->oldmessages) {
08753          res = say_and_wait(chan, vms->oldmessages, chan->language);
08754          if (!res) {
08755             if ((vms->oldmessages == 1))
08756                res = ast_play_and_wait(chan, "vm-starou");
08757             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08758                res = ast_play_and_wait(chan, "vm-stare");
08759             if (vms->oldmessages > 4)
08760                res = ast_play_and_wait(chan, "vm-starych");
08761          }
08762          if (!res) {
08763             if ((vms->oldmessages == 1))
08764                res = ast_play_and_wait(chan, "vm-zpravu");
08765             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08766                res = ast_play_and_wait(chan, "vm-zpravy");
08767             if (vms->oldmessages > 4)
08768                res = ast_play_and_wait(chan, "vm-zprav");
08769          }
08770       }
08771       if (!res) {
08772          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08773             res = ast_play_and_wait(chan, "vm-no");
08774             if (!res)
08775                res = ast_play_and_wait(chan, "vm-zpravy");
08776          }
08777       }
08778    }
08779    return res;
08780 }
08781 
08782 /* CHINESE (Taiwan) syntax */
08783 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08784 {
08785    int res;
08786    /* Introduce messages they have */
08787    res = ast_play_and_wait(chan, "vm-you");
08788 
08789    if (!res && vms->newmessages) {
08790       res = ast_play_and_wait(chan, "vm-have");
08791       if (!res)
08792          res = say_and_wait(chan, vms->newmessages, chan->language);
08793       if (!res)
08794          res = ast_play_and_wait(chan, "vm-tong");
08795       if (!res)
08796          res = ast_play_and_wait(chan, "vm-INBOX");
08797       if (vms->oldmessages && !res)
08798          res = ast_play_and_wait(chan, "vm-and");
08799       else if (!res) 
08800          res = ast_play_and_wait(chan, "vm-messages");
08801    }
08802    if (!res && vms->oldmessages) {
08803       res = ast_play_and_wait(chan, "vm-have");
08804       if (!res)
08805          res = say_and_wait(chan, vms->oldmessages, chan->language);
08806       if (!res)
08807          res = ast_play_and_wait(chan, "vm-tong");
08808       if (!res)
08809          res = ast_play_and_wait(chan, "vm-Old");
08810       if (!res)
08811          res = ast_play_and_wait(chan, "vm-messages");
08812    }
08813    if (!res && !vms->oldmessages && !vms->newmessages) {
08814       res = ast_play_and_wait(chan, "vm-haveno");
08815       if (!res)
08816          res = ast_play_and_wait(chan, "vm-messages");
08817    }
08818    return res;
08819 }
08820 
08821 /* Vietnamese syntax */
08822 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08823 {
08824    int res;
08825 
08826    /* Introduce messages they have */
08827    res = ast_play_and_wait(chan, "vm-youhave");
08828    if (!res) {
08829       if (vms->newmessages) {
08830          res = say_and_wait(chan, vms->newmessages, chan->language);
08831          if (!res)
08832             res = ast_play_and_wait(chan, "vm-INBOX");
08833          if (vms->oldmessages && !res)
08834             res = ast_play_and_wait(chan, "vm-and");
08835       }
08836       if (!res && vms->oldmessages) {
08837          res = say_and_wait(chan, vms->oldmessages, chan->language);
08838          if (!res)
08839             res = ast_play_and_wait(chan, "vm-Old");        
08840       }
08841       if (!res) {
08842          if (!vms->oldmessages && !vms->newmessages) {
08843             res = ast_play_and_wait(chan, "vm-no");
08844             if (!res)
08845                res = ast_play_and_wait(chan, "vm-message");
08846          }
08847       }
08848    }
08849    return res;
08850 }
08851 
08852 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08853 {
08854    char prefile[256];
08855    
08856    /* Notify the user that the temp greeting is set and give them the option to remove it */
08857    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08858    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08859       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08860       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08861          ast_play_and_wait(chan, "vm-tempgreetactive");
08862       }
08863       DISPOSE(prefile, -1);
08864    }
08865 
08866    /* Play voicemail intro - syntax is different for different languages */
08867    if (0) {
08868       return 0;
08869    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08870       return vm_intro_cs(chan, vms);
08871    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08872       static int deprecation_warning = 0;
08873       if (deprecation_warning++ % 10 == 0) {
08874          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08875       }
08876       return vm_intro_cs(chan, vms);
08877    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08878       return vm_intro_de(chan, vms);
08879    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08880       return vm_intro_es(chan, vms);
08881    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08882       return vm_intro_fr(chan, vms);
08883    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08884       return vm_intro_gr(chan, vms);
08885    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08886       return vm_intro_he(chan, vms);
08887    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08888       return vm_intro_it(chan, vms);
08889    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08890       return vm_intro_nl(chan, vms);
08891    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08892       return vm_intro_no(chan, vms);
08893    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08894       return vm_intro_pl(chan, vms);
08895    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08896       return vm_intro_pt_BR(chan, vms);
08897    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08898       return vm_intro_pt(chan, vms);
08899    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08900       return vm_intro_multilang(chan, vms, "n");
08901    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08902       return vm_intro_se(chan, vms);
08903    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08904       return vm_intro_multilang(chan, vms, "n");
08905    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
08906       return vm_intro_vi(chan, vms);
08907    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08908       return vm_intro_zh(chan, vms);
08909    } else {                                             /* Default to ENGLISH */
08910       return vm_intro_en(chan, vms);
08911    }
08912 }
08913 
08914 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08915 {
08916    int res = 0;
08917    /* Play instructions and wait for new command */
08918    while (!res) {
08919       if (vms->starting) {
08920          if (vms->lastmsg > -1) {
08921             if (skipadvanced)
08922                res = ast_play_and_wait(chan, "vm-onefor-full");
08923             else
08924                res = ast_play_and_wait(chan, "vm-onefor");
08925             if (!res)
08926                res = vm_play_folder_name(chan, vms->vmbox);
08927          }
08928          if (!res) {
08929             if (skipadvanced)
08930                res = ast_play_and_wait(chan, "vm-opts-full");
08931             else
08932                res = ast_play_and_wait(chan, "vm-opts");
08933          }
08934       } else {
08935          /* Added for additional help */
08936          if (skipadvanced) {
08937             res = ast_play_and_wait(chan, "vm-onefor-full");
08938             if (!res)
08939                res = vm_play_folder_name(chan, vms->vmbox);
08940             res = ast_play_and_wait(chan, "vm-opts-full");
08941          }
08942          /* Logic:
08943           * If the current message is not the first OR
08944           * if we're listening to the first new message and there are
08945           * also urgent messages, then prompt for navigation to the
08946           * previous message
08947           */
08948          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08949             res = ast_play_and_wait(chan, "vm-prev");
08950          }
08951          if (!res && !skipadvanced)
08952             res = ast_play_and_wait(chan, "vm-advopts");
08953          if (!res)
08954             res = ast_play_and_wait(chan, "vm-repeat");
08955          /* Logic:
08956           * If we're not listening to the last message OR
08957           * we're listening to the last urgent message and there are
08958           * also new non-urgent messages, then prompt for navigation
08959           * to the next message
08960           */
08961          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08962             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08963             res = ast_play_and_wait(chan, "vm-next");
08964          }
08965          if (!res) {
08966             if (!vms->deleted[vms->curmsg])
08967                res = ast_play_and_wait(chan, "vm-delete");
08968             else
08969                res = ast_play_and_wait(chan, "vm-undelete");
08970             if (!res)
08971                res = ast_play_and_wait(chan, "vm-toforward");
08972             if (!res)
08973                res = ast_play_and_wait(chan, "vm-savemessage");
08974          }
08975       }
08976       if (!res) {
08977          res = ast_play_and_wait(chan, "vm-helpexit");
08978       }
08979       if (!res)
08980          res = ast_waitfordigit(chan, 6000);
08981       if (!res) {
08982          vms->repeats++;
08983          if (vms->repeats > 2) {
08984             res = 't';
08985          }
08986       }
08987    }
08988    return res;
08989 }
08990 
08991 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08992 {
08993    int res = 0;
08994    /* Play instructions and wait for new command */
08995    while (!res) {
08996       if (vms->lastmsg > -1) {
08997          res = ast_play_and_wait(chan, "vm-listen");
08998          if (!res)
08999             res = vm_play_folder_name(chan, vms->vmbox);
09000          if (!res)
09001             res = ast_play_and_wait(chan, "press");
09002          if (!res)
09003             res = ast_play_and_wait(chan, "digits/1");
09004       }
09005       if (!res)
09006          res = ast_play_and_wait(chan, "vm-opts");
09007       if (!res) {
09008          vms->starting = 0;
09009          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09010       }
09011    }
09012    return res;
09013 }
09014 
09015 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09016 {
09017    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09018       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09019    } else {             /* Default to ENGLISH */
09020       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09021    }
09022 }
09023 
09024 
09025 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09026 {
09027    int cmd = 0;
09028    int duration = 0;
09029    int tries = 0;
09030    char newpassword[80] = "";
09031    char newpassword2[80] = "";
09032    char prefile[PATH_MAX] = "";
09033    unsigned char buf[256];
09034    int bytes = 0;
09035 
09036    if (ast_adsi_available(chan)) {
09037       bytes += adsi_logo(buf + bytes);
09038       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09039       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09040       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09041       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09042       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09043    }
09044 
09045    /* First, have the user change their password 
09046       so they won't get here again */
09047    for (;;) {
09048       newpassword[1] = '\0';
09049       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09050       if (cmd == '#')
09051          newpassword[0] = '\0';
09052       if (cmd < 0 || cmd == 't' || cmd == '#')
09053          return cmd;
09054       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09055       if (cmd < 0 || cmd == 't' || cmd == '#')
09056          return cmd;
09057       cmd = check_password(vmu, newpassword); /* perform password validation */
09058       if (cmd != 0) {
09059          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09060          cmd = ast_play_and_wait(chan, vm_invalid_password);
09061       } else {
09062          newpassword2[1] = '\0';
09063          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09064          if (cmd == '#')
09065             newpassword2[0] = '\0';
09066          if (cmd < 0 || cmd == 't' || cmd == '#')
09067             return cmd;
09068          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09069          if (cmd < 0 || cmd == 't' || cmd == '#')
09070             return cmd;
09071          if (!strcmp(newpassword, newpassword2))
09072             break;
09073          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09074          cmd = ast_play_and_wait(chan, vm_mismatch);
09075       }
09076       if (++tries == 3)
09077          return -1;
09078       if (cmd != 0) {
09079          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09080       }
09081    }
09082    if (pwdchange & PWDCHANGE_INTERNAL)
09083       vm_change_password(vmu, newpassword);
09084    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09085       vm_change_password_shell(vmu, newpassword);
09086 
09087    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09088    cmd = ast_play_and_wait(chan, vm_passchanged);
09089 
09090    /* If forcename is set, have the user record their name */  
09091    if (ast_test_flag(vmu, VM_FORCENAME)) {
09092       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09093       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09094          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09095          if (cmd < 0 || cmd == 't' || cmd == '#')
09096             return cmd;
09097       }
09098    }
09099 
09100    /* If forcegreetings is set, have the user record their greetings */
09101    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09102       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09103       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09104          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09105          if (cmd < 0 || cmd == 't' || cmd == '#')
09106             return cmd;
09107       }
09108 
09109       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09110       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09111          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09112          if (cmd < 0 || cmd == 't' || cmd == '#')
09113             return cmd;
09114       }
09115    }
09116 
09117    return cmd;
09118 }
09119 
09120 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09121 {
09122    int cmd = 0;
09123    int retries = 0;
09124    int duration = 0;
09125    char newpassword[80] = "";
09126    char newpassword2[80] = "";
09127    char prefile[PATH_MAX] = "";
09128    unsigned char buf[256];
09129    int bytes = 0;
09130 
09131    if (ast_adsi_available(chan)) {
09132       bytes += adsi_logo(buf + bytes);
09133       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09134       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09135       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09136       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09137       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09138    }
09139    while ((cmd >= 0) && (cmd != 't')) {
09140       if (cmd)
09141          retries = 0;
09142       switch (cmd) {
09143       case '1': /* Record your unavailable message */
09144          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09145          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09146          break;
09147       case '2':  /* Record your busy message */
09148          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09149          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09150          break;
09151       case '3': /* Record greeting */
09152          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09153          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09154          break;
09155       case '4':  /* manage the temporary greeting */
09156          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09157          break;
09158       case '5': /* change password */
09159          if (vmu->password[0] == '-') {
09160             cmd = ast_play_and_wait(chan, "vm-no");
09161             break;
09162          }
09163          newpassword[1] = '\0';
09164          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09165          if (cmd == '#')
09166             newpassword[0] = '\0';
09167          else {
09168             if (cmd < 0)
09169                break;
09170             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09171                break;
09172             }
09173          }
09174          cmd = check_password(vmu, newpassword); /* perform password validation */
09175          if (cmd != 0) {
09176             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09177             cmd = ast_play_and_wait(chan, vm_invalid_password);
09178             if (!cmd) {
09179                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09180             }
09181             break;
09182          }
09183          newpassword2[1] = '\0';
09184          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09185          if (cmd == '#')
09186             newpassword2[0] = '\0';
09187          else {
09188             if (cmd < 0)
09189                break;
09190 
09191             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09192                break;
09193             }
09194          }
09195          if (strcmp(newpassword, newpassword2)) {
09196             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09197             cmd = ast_play_and_wait(chan, vm_mismatch);
09198             if (!cmd) {
09199                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09200             }
09201             break;
09202          }
09203          if (pwdchange & PWDCHANGE_INTERNAL)
09204             vm_change_password(vmu, newpassword);
09205          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09206             vm_change_password_shell(vmu, newpassword);
09207 
09208          ast_debug(1, "User %s set password to %s of length %d\n",
09209             vms->username, newpassword, (int) strlen(newpassword));
09210          cmd = ast_play_and_wait(chan, vm_passchanged);
09211          break;
09212       case '*': 
09213          cmd = 't';
09214          break;
09215       default: 
09216          cmd = 0;
09217          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09218          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09219          if (ast_fileexists(prefile, NULL, NULL)) {
09220             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09221          }
09222          DISPOSE(prefile, -1);
09223          if (!cmd) {
09224             cmd = ast_play_and_wait(chan, "vm-options");
09225          }
09226          if (!cmd) {
09227             cmd = ast_waitfordigit(chan, 6000);
09228          }
09229          if (!cmd) {
09230             retries++;
09231          }
09232          if (retries > 3) {
09233             cmd = 't';
09234          }
09235       }
09236    }
09237    if (cmd == 't')
09238       cmd = 0;
09239    return cmd;
09240 }
09241 
09242 /*!
09243  * \brief The handler for 'record a temporary greeting'. 
09244  * \param chan
09245  * \param vmu
09246  * \param vms
09247  * \param fmtc
09248  * \param record_gain
09249  *
09250  * This is option 4 from the mailbox options menu.
09251  * This function manages the following promptings:
09252  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09253  * 2: remove (delete) the temporary greeting.
09254  * *: return to the main menu.
09255  *
09256  * \return zero on success, -1 on error.
09257  */
09258 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09259 {
09260    int cmd = 0;
09261    int retries = 0;
09262    int duration = 0;
09263    char prefile[PATH_MAX] = "";
09264    unsigned char buf[256];
09265    int bytes = 0;
09266 
09267    if (ast_adsi_available(chan)) {
09268       bytes += adsi_logo(buf + bytes);
09269       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09270       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09271       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09272       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09273       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09274    }
09275 
09276    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09277    while ((cmd >= 0) && (cmd != 't')) {
09278       if (cmd)
09279          retries = 0;
09280       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09281       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09282          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09283          cmd = 't';  
09284       } else {
09285          switch (cmd) {
09286          case '1':
09287             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09288             break;
09289          case '2':
09290             DELETE(prefile, -1, prefile, vmu);
09291             ast_play_and_wait(chan, "vm-tempremoved");
09292             cmd = 't';  
09293             break;
09294          case '*': 
09295             cmd = 't';
09296             break;
09297          default:
09298             cmd = ast_play_and_wait(chan,
09299                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09300                   "vm-tempgreeting2" : "vm-tempgreeting");
09301             if (!cmd)
09302                cmd = ast_waitfordigit(chan, 6000);
09303             if (!cmd)
09304                retries++;
09305             if (retries > 3)
09306                cmd = 't';
09307          }
09308       }
09309       DISPOSE(prefile, -1);
09310    }
09311    if (cmd == 't')
09312       cmd = 0;
09313    return cmd;
09314 }
09315 
09316 /*!
09317  * \brief Greek syntax for 'You have N messages' greeting.
09318  * \param chan
09319  * \param vms
09320  * \param vmu
09321  *
09322  * \return zero on success, -1 on error.
09323  */   
09324 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09325 {
09326    int cmd = 0;
09327 
09328    if (vms->lastmsg > -1) {
09329       cmd = play_message(chan, vmu, vms);
09330    } else {
09331       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09332       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09333          if (!cmd) {
09334             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09335             cmd = ast_play_and_wait(chan, vms->fn);
09336          }
09337          if (!cmd)
09338             cmd = ast_play_and_wait(chan, "vm-messages");
09339       } else {
09340          if (!cmd)
09341             cmd = ast_play_and_wait(chan, "vm-messages");
09342          if (!cmd) {
09343             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09344             cmd = ast_play_and_wait(chan, vms->fn);
09345          }
09346       }
09347    } 
09348    return cmd;
09349 }
09350 
09351 /* Hebrew Syntax */
09352 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09353 {
09354    int cmd = 0;
09355 
09356    if (vms->lastmsg > -1) {
09357       cmd = play_message(chan, vmu, vms);
09358    } else {
09359       if (!strcasecmp(vms->fn, "INBOX")) {
09360          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09361       } else {
09362          cmd = ast_play_and_wait(chan, "vm-nomessages");
09363       }
09364    }
09365    return cmd;
09366 }
09367 
09368 /*! 
09369  * \brief Default English syntax for 'You have N messages' greeting.
09370  * \param chan
09371  * \param vms
09372  * \param vmu
09373  *
09374  * \return zero on success, -1 on error.
09375  */
09376 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09377 {
09378    int cmd = 0;
09379 
09380    if (vms->lastmsg > -1) {
09381       cmd = play_message(chan, vmu, vms);
09382    } else {
09383       cmd = ast_play_and_wait(chan, "vm-youhave");
09384       if (!cmd) 
09385          cmd = ast_play_and_wait(chan, "vm-no");
09386       if (!cmd) {
09387          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09388          cmd = ast_play_and_wait(chan, vms->fn);
09389       }
09390       if (!cmd)
09391          cmd = ast_play_and_wait(chan, "vm-messages");
09392    }
09393    return cmd;
09394 }
09395 
09396 /*! 
09397  *\brief Italian syntax for 'You have N messages' greeting.
09398  * \param chan
09399  * \param vms
09400  * \param vmu
09401  *
09402  * \return zero on success, -1 on error.
09403  */
09404 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09405 {
09406    int cmd;
09407 
09408    if (vms->lastmsg > -1) {
09409       cmd = play_message(chan, vmu, vms);
09410    } else {
09411       cmd = ast_play_and_wait(chan, "vm-no");
09412       if (!cmd)
09413          cmd = ast_play_and_wait(chan, "vm-message");
09414       if (!cmd) {
09415          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09416          cmd = ast_play_and_wait(chan, vms->fn);
09417       }
09418    }
09419    return cmd;
09420 }
09421 
09422 /*! 
09423  * \brief Spanish syntax for 'You have N messages' greeting.
09424  * \param chan
09425  * \param vms
09426  * \param vmu
09427  *
09428  * \return zero on success, -1 on error.
09429  */
09430 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09431 {
09432    int cmd;
09433 
09434    if (vms->lastmsg > -1) {
09435       cmd = play_message(chan, vmu, vms);
09436    } else {
09437       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09438       if (!cmd)
09439          cmd = ast_play_and_wait(chan, "vm-messages");
09440       if (!cmd) {
09441          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09442          cmd = ast_play_and_wait(chan, vms->fn);
09443       }
09444    }
09445    return cmd;
09446 }
09447 
09448 /*! 
09449  * \brief Portuguese syntax for 'You have N messages' greeting.
09450  * \param chan
09451  * \param vms
09452  * \param vmu
09453  *
09454  * \return zero on success, -1 on error.
09455  */
09456 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09457 {
09458    int cmd;
09459 
09460    if (vms->lastmsg > -1) {
09461       cmd = play_message(chan, vmu, vms);
09462    } else {
09463       cmd = ast_play_and_wait(chan, "vm-no");
09464       if (!cmd) {
09465          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09466          cmd = ast_play_and_wait(chan, vms->fn);
09467       }
09468       if (!cmd)
09469          cmd = ast_play_and_wait(chan, "vm-messages");
09470    }
09471    return cmd;
09472 }
09473 
09474 /*! 
09475  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09476  * \param chan
09477  * \param vms
09478  * \param vmu
09479  *
09480  * \return zero on success, -1 on error.
09481  */
09482 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09483 {
09484    int cmd;
09485 
09486    if (vms->lastmsg > -1) {
09487       cmd = play_message(chan, vmu, vms);
09488    } else {
09489       cmd = ast_play_and_wait(chan, "vm-you");
09490       if (!cmd) 
09491          cmd = ast_play_and_wait(chan, "vm-haveno");
09492       if (!cmd)
09493          cmd = ast_play_and_wait(chan, "vm-messages");
09494       if (!cmd) {
09495          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09496          cmd = ast_play_and_wait(chan, vms->fn);
09497       }
09498    }
09499    return cmd;
09500 }
09501 
09502 /*! 
09503  * \brief Vietnamese syntax for 'You have N messages' greeting.
09504  * \param chan
09505  * \param vms
09506  * \param vmu
09507  *
09508  * \return zero on success, -1 on error.
09509  */
09510 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09511 {
09512    int cmd = 0;
09513 
09514    if (vms->lastmsg > -1) {
09515       cmd = play_message(chan, vmu, vms);
09516    } else {
09517       cmd = ast_play_and_wait(chan, "vm-no");
09518       if (!cmd) {
09519          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09520          cmd = ast_play_and_wait(chan, vms->fn);
09521       }
09522    }
09523    return cmd;
09524 }
09525 
09526 /*!
09527  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09528  * \param chan The channel for the current user. We read the language property from this.
09529  * \param vms passed into the language-specific vm_browse_messages function.
09530  * \param vmu passed into the language-specific vm_browse_messages function.
09531  * 
09532  * The method to be invoked is determined by the value of language code property in the user's channel.
09533  * The default (when unable to match) is to use english.
09534  *
09535  * \return zero on success, -1 on error.
09536  */
09537 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09538 {
09539    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09540       return vm_browse_messages_es(chan, vms, vmu);
09541    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09542       return vm_browse_messages_gr(chan, vms, vmu);
09543    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09544       return vm_browse_messages_he(chan, vms, vmu);
09545    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09546       return vm_browse_messages_it(chan, vms, vmu);
09547    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09548       return vm_browse_messages_pt(chan, vms, vmu);
09549    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09550       return vm_browse_messages_vi(chan, vms, vmu);
09551    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09552       return vm_browse_messages_zh(chan, vms, vmu);
09553    } else {                                             /* Default to English syntax */
09554       return vm_browse_messages_en(chan, vms, vmu);
09555    }
09556 }
09557 
09558 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09559          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09560          int skipuser, int max_logins, int silent)
09561 {
09562    int useadsi = 0, valid = 0, logretries = 0;
09563    char password[AST_MAX_EXTENSION]="", *passptr;
09564    struct ast_vm_user vmus, *vmu = NULL;
09565 
09566    /* If ADSI is supported, setup login screen */
09567    adsi_begin(chan, &useadsi);
09568    if (!skipuser && useadsi)
09569       adsi_login(chan);
09570    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09571       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09572       return -1;
09573    }
09574    
09575    /* Authenticate them and get their mailbox/password */
09576    
09577    while (!valid && (logretries < max_logins)) {
09578       /* Prompt for, and read in the username */
09579       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09580          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09581          return -1;
09582       }
09583       if (ast_strlen_zero(mailbox)) {
09584          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09585             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09586          } else {
09587             ast_verb(3, "Username not entered\n"); 
09588             return -1;
09589          }
09590       } else if (mailbox[0] == '*') {
09591          /* user entered '*' */
09592          if (ast_exists_extension(chan, chan->context, "a", 1,
09593             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09594             return -1;
09595          }
09596          mailbox[0] = '\0';
09597       }
09598 
09599       if (useadsi)
09600          adsi_password(chan);
09601 
09602       if (!ast_strlen_zero(prefix)) {
09603          char fullusername[80] = "";
09604          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09605          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09606          ast_copy_string(mailbox, fullusername, mailbox_size);
09607       }
09608 
09609       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09610       vmu = find_user(&vmus, context, mailbox);
09611       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09612          /* saved password is blank, so don't bother asking */
09613          password[0] = '\0';
09614       } else {
09615          if (ast_streamfile(chan, vm_password, chan->language)) {
09616             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09617             return -1;
09618          }
09619          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09620             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09621             return -1;
09622          } else if (password[0] == '*') {
09623             /* user entered '*' */
09624             if (ast_exists_extension(chan, chan->context, "a", 1,
09625                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09626                mailbox[0] = '*';
09627                return -1;
09628             }
09629             mailbox[0] = '\0';
09630          }
09631       }
09632 
09633       if (vmu) {
09634          passptr = vmu->password;
09635          if (passptr[0] == '-') passptr++;
09636       }
09637       if (vmu && !strcmp(passptr, password))
09638          valid++;
09639       else {
09640          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09641          if (!ast_strlen_zero(prefix))
09642             mailbox[0] = '\0';
09643       }
09644       logretries++;
09645       if (!valid) {
09646          if (skipuser || logretries >= max_logins) {
09647             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09648                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09649                return -1;
09650             }
09651          } else {
09652             if (useadsi)
09653                adsi_login(chan);
09654             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09655                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09656                return -1;
09657             }
09658          }
09659          if (ast_waitstream(chan, "")) /* Channel is hung up */
09660             return -1;
09661       }
09662    }
09663    if (!valid && (logretries >= max_logins)) {
09664       ast_stopstream(chan);
09665       ast_play_and_wait(chan, "vm-goodbye");
09666       return -1;
09667    }
09668    if (vmu && !skipuser) {
09669       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09670    }
09671    return 0;
09672 }
09673 
09674 static int vm_execmain(struct ast_channel *chan, const char *data)
09675 {
09676    /* XXX This is, admittedly, some pretty horrendous code.  For some
09677       reason it just seemed a lot easier to do with GOTO's.  I feel
09678       like I'm back in my GWBASIC days. XXX */
09679    int res = -1;
09680    int cmd = 0;
09681    int valid = 0;
09682    char prefixstr[80] ="";
09683    char ext_context[256]="";
09684    int box;
09685    int useadsi = 0;
09686    int skipuser = 0;
09687    struct vm_state vms;
09688    struct ast_vm_user *vmu = NULL, vmus;
09689    char *context = NULL;
09690    int silentexit = 0;
09691    struct ast_flags flags = { 0 };
09692    signed char record_gain = 0;
09693    int play_auto = 0;
09694    int play_folder = 0;
09695    int in_urgent = 0;
09696 #ifdef IMAP_STORAGE
09697    int deleted = 0;
09698 #endif
09699 
09700    /* Add the vm_state to the active list and keep it active */
09701    memset(&vms, 0, sizeof(vms));
09702 
09703    vms.lastmsg = -1;
09704 
09705    memset(&vmus, 0, sizeof(vmus));
09706 
09707    if (chan->_state != AST_STATE_UP) {
09708       ast_debug(1, "Before ast_answer\n");
09709       ast_answer(chan);
09710    }
09711 
09712    if (!ast_strlen_zero(data)) {
09713       char *opts[OPT_ARG_ARRAY_SIZE];
09714       char *parse;
09715       AST_DECLARE_APP_ARGS(args,
09716          AST_APP_ARG(argv0);
09717          AST_APP_ARG(argv1);
09718       );
09719 
09720       parse = ast_strdupa(data);
09721 
09722       AST_STANDARD_APP_ARGS(args, parse);
09723 
09724       if (args.argc == 2) {
09725          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09726             return -1;
09727          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09728             int gain;
09729             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09730                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09731                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09732                   return -1;
09733                } else {
09734                   record_gain = (signed char) gain;
09735                }
09736             } else {
09737                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09738             }
09739          }
09740          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09741             play_auto = 1;
09742             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09743                /* See if it is a folder name first */
09744                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09745                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09746                      play_folder = -1;
09747                   }
09748                } else {
09749                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09750                }
09751             } else {
09752                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09753             }
09754             if (play_folder > 9 || play_folder < 0) {
09755                ast_log(AST_LOG_WARNING,
09756                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09757                   opts[OPT_ARG_PLAYFOLDER]);
09758                play_folder = 0;
09759             }
09760          }
09761       } else {
09762          /* old style options parsing */
09763          while (*(args.argv0)) {
09764             if (*(args.argv0) == 's')
09765                ast_set_flag(&flags, OPT_SILENT);
09766             else if (*(args.argv0) == 'p')
09767                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09768             else 
09769                break;
09770             (args.argv0)++;
09771          }
09772 
09773       }
09774 
09775       valid = ast_test_flag(&flags, OPT_SILENT);
09776 
09777       if ((context = strchr(args.argv0, '@')))
09778          *context++ = '\0';
09779 
09780       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09781          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09782       else
09783          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09784 
09785       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09786          skipuser++;
09787       else
09788          valid = 0;
09789    }
09790 
09791    if (!valid)
09792       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09793 
09794    ast_debug(1, "After vm_authenticate\n");
09795 
09796    if (vms.username[0] == '*') {
09797       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09798 
09799       /* user entered '*' */
09800       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09801          res = 0; /* prevent hangup */
09802          goto out;
09803       }
09804    }
09805 
09806    if (!res) {
09807       valid = 1;
09808       if (!skipuser)
09809          vmu = &vmus;
09810    } else {
09811       res = 0;
09812    }
09813 
09814    /* If ADSI is supported, setup login screen */
09815    adsi_begin(chan, &useadsi);
09816 
09817    if (!valid) {
09818       goto out;
09819    }
09820 
09821 #ifdef IMAP_STORAGE
09822    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09823    pthread_setspecific(ts_vmstate.key, &vms);
09824 
09825    vms.interactive = 1;
09826    vms.updated = 1;
09827    if (vmu)
09828       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09829    vmstate_insert(&vms);
09830    init_vm_state(&vms);
09831 #endif
09832    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09833    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09834       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09835       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09836       return -1;
09837    }
09838    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09839       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09840       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09841       return -1;
09842    }
09843    
09844    /* Set language from config to override channel language */
09845    if (!ast_strlen_zero(vmu->language))
09846       ast_string_field_set(chan, language, vmu->language);
09847 
09848    /* Retrieve urgent, old and new message counts */
09849    ast_debug(1, "Before open_mailbox\n");
09850    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09851    if (res < 0)
09852       goto out;
09853    vms.oldmessages = vms.lastmsg + 1;
09854    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
09855    /* check INBOX */
09856    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09857    if (res < 0)
09858       goto out;
09859    vms.newmessages = vms.lastmsg + 1;
09860    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
09861    /* Start in Urgent */
09862    in_urgent = 1;
09863    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09864    if (res < 0)
09865       goto out;
09866    vms.urgentmessages = vms.lastmsg + 1;
09867    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
09868 
09869    /* Select proper mailbox FIRST!! */
09870    if (play_auto) {
09871       if (vms.urgentmessages) {
09872          in_urgent = 1;
09873          res = open_mailbox(&vms, vmu, 11);
09874       } else {
09875          in_urgent = 0;
09876          res = open_mailbox(&vms, vmu, play_folder);
09877       }
09878       if (res < 0)
09879          goto out;
09880 
09881       /* If there are no new messages, inform the user and hangup */
09882       if (vms.lastmsg == -1) {
09883          in_urgent = 0;
09884          cmd = vm_browse_messages(chan, &vms, vmu);
09885          res = 0;
09886          goto out;
09887       }
09888    } else {
09889       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09890          /* If we only have old messages start here */
09891          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09892          in_urgent = 0;
09893          play_folder = 1;
09894          if (res < 0)
09895             goto out;
09896       } else if (!vms.urgentmessages && vms.newmessages) {
09897          /* If we have new messages but none are urgent */
09898          in_urgent = 0;
09899          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09900          if (res < 0)
09901             goto out;
09902       }
09903    }
09904 
09905    if (useadsi)
09906       adsi_status(chan, &vms);
09907    res = 0;
09908 
09909    /* Check to see if this is a new user */
09910    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09911       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09912       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09913          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09914       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09915       if ((cmd == 't') || (cmd == '#')) {
09916          /* Timeout */
09917          res = 0;
09918          goto out;
09919       } else if (cmd < 0) {
09920          /* Hangup */
09921          res = -1;
09922          goto out;
09923       }
09924    }
09925 #ifdef IMAP_STORAGE
09926       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
09927       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09928          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09929          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09930       }
09931       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09932       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09933          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09934          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09935       }
09936 #endif
09937    if (play_auto) {
09938       cmd = '1';
09939    } else {
09940       cmd = vm_intro(chan, vmu, &vms);
09941    }
09942 
09943    vms.repeats = 0;
09944    vms.starting = 1;
09945    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09946       /* Run main menu */
09947       switch (cmd) {
09948       case '1': /* First message */
09949          vms.curmsg = 0;
09950          /* Fall through */
09951       case '5': /* Play current message */
09952          cmd = vm_browse_messages(chan, &vms, vmu);
09953          break;
09954       case '2': /* Change folders */
09955          if (useadsi)
09956             adsi_folders(chan, 0, "Change to folder...");
09957          cmd = get_folder2(chan, "vm-changeto", 0);
09958          if (cmd == '#') {
09959             cmd = 0;
09960          } else if (cmd > 0) {
09961             cmd = cmd - '0';
09962             res = close_mailbox(&vms, vmu);
09963             if (res == ERROR_LOCK_PATH)
09964                goto out;
09965             /* If folder is not urgent, set in_urgent to zero! */
09966             if (cmd != 11) in_urgent = 0;
09967             res = open_mailbox(&vms, vmu, cmd);
09968             if (res < 0)
09969                goto out;
09970             play_folder = cmd;
09971             cmd = 0;
09972          }
09973          if (useadsi)
09974             adsi_status2(chan, &vms);
09975             
09976          if (!cmd)
09977             cmd = vm_play_folder_name(chan, vms.vmbox);
09978 
09979          vms.starting = 1;
09980          break;
09981       case '3': /* Advanced options */
09982          cmd = 0;
09983          vms.repeats = 0;
09984          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09985             switch (cmd) {
09986             case '1': /* Reply */
09987                if (vms.lastmsg > -1 && !vms.starting) {
09988                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09989                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09990                      res = cmd;
09991                      goto out;
09992                   }
09993                } else
09994                   cmd = ast_play_and_wait(chan, "vm-sorry");
09995                cmd = 't';
09996                break;
09997             case '2': /* Callback */
09998                if (!vms.starting)
09999                   ast_verb(3, "Callback Requested\n");
10000                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10001                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10002                   if (cmd == 9) {
10003                      silentexit = 1;
10004                      goto out;
10005                   } else if (cmd == ERROR_LOCK_PATH) {
10006                      res = cmd;
10007                      goto out;
10008                   }
10009                } else 
10010                   cmd = ast_play_and_wait(chan, "vm-sorry");
10011                cmd = 't';
10012                break;
10013             case '3': /* Envelope */
10014                if (vms.lastmsg > -1 && !vms.starting) {
10015                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10016                   if (cmd == ERROR_LOCK_PATH) {
10017                      res = cmd;
10018                      goto out;
10019                   }
10020                } else
10021                   cmd = ast_play_and_wait(chan, "vm-sorry");
10022                cmd = 't';
10023                break;
10024             case '4': /* Dialout */
10025                if (!ast_strlen_zero(vmu->dialout)) {
10026                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10027                   if (cmd == 9) {
10028                      silentexit = 1;
10029                      goto out;
10030                   }
10031                } else 
10032                   cmd = ast_play_and_wait(chan, "vm-sorry");
10033                cmd = 't';
10034                break;
10035 
10036             case '5': /* Leave VoiceMail */
10037                if (ast_test_flag(vmu, VM_SVMAIL)) {
10038                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10039                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10040                      res = cmd;
10041                      goto out;
10042                   }
10043                } else
10044                   cmd = ast_play_and_wait(chan, "vm-sorry");
10045                cmd = 't';
10046                break;
10047                
10048             case '*': /* Return to main menu */
10049                cmd = 't';
10050                break;
10051 
10052             default:
10053                cmd = 0;
10054                if (!vms.starting) {
10055                   cmd = ast_play_and_wait(chan, "vm-toreply");
10056                }
10057                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10058                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10059                }
10060                if (!cmd && !vms.starting) {
10061                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10062                }
10063                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10064                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10065                }
10066                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
10067                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10068                if (!cmd)
10069                   cmd = ast_play_and_wait(chan, "vm-starmain");
10070                if (!cmd)
10071                   cmd = ast_waitfordigit(chan, 6000);
10072                if (!cmd)
10073                   vms.repeats++;
10074                if (vms.repeats > 3)
10075                   cmd = 't';
10076             }
10077          }
10078          if (cmd == 't') {
10079             cmd = 0;
10080             vms.repeats = 0;
10081          }
10082          break;
10083       case '4': /* Go to the previous message */
10084          if (vms.curmsg > 0) {
10085             vms.curmsg--;
10086             cmd = play_message(chan, vmu, &vms);
10087          } else {
10088             /* Check if we were listening to new
10089                messages.  If so, go to Urgent messages
10090                instead of saying "no more messages"
10091             */
10092             if (in_urgent == 0 && vms.urgentmessages > 0) {
10093                /* Check for Urgent messages */
10094                in_urgent = 1;
10095                res = close_mailbox(&vms, vmu);
10096                if (res == ERROR_LOCK_PATH)
10097                   goto out;
10098                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10099                if (res < 0)
10100                   goto out;
10101                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10102                vms.curmsg = vms.lastmsg;
10103                if (vms.lastmsg < 0)
10104                   cmd = ast_play_and_wait(chan, "vm-nomore");
10105             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10106                vms.curmsg = vms.lastmsg;
10107                cmd = play_message(chan, vmu, &vms);
10108             } else {
10109                cmd = ast_play_and_wait(chan, "vm-nomore");
10110             }
10111          }
10112          break;
10113       case '6': /* Go to the next message */
10114          if (vms.curmsg < vms.lastmsg) {
10115             vms.curmsg++;
10116             cmd = play_message(chan, vmu, &vms);
10117          } else {
10118             if (in_urgent && vms.newmessages > 0) {
10119                /* Check if we were listening to urgent
10120                 * messages.  If so, go to regular new messages
10121                 * instead of saying "no more messages"
10122                 */
10123                in_urgent = 0;
10124                res = close_mailbox(&vms, vmu);
10125                if (res == ERROR_LOCK_PATH)
10126                   goto out;
10127                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10128                if (res < 0)
10129                   goto out;
10130                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10131                vms.curmsg = -1;
10132                if (vms.lastmsg < 0) {
10133                   cmd = ast_play_and_wait(chan, "vm-nomore");
10134                }
10135             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10136                vms.curmsg = 0;
10137                cmd = play_message(chan, vmu, &vms);
10138             } else {
10139                cmd = ast_play_and_wait(chan, "vm-nomore");
10140             }
10141          }
10142          break;
10143       case '7': /* Delete the current message */
10144          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10145             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10146             if (useadsi)
10147                adsi_delete(chan, &vms);
10148             if (vms.deleted[vms.curmsg]) {
10149                if (play_folder == 0) {
10150                   if (in_urgent) {
10151                      vms.urgentmessages--;
10152                   } else {
10153                      vms.newmessages--;
10154                   }
10155                }
10156                else if (play_folder == 1)
10157                   vms.oldmessages--;
10158                cmd = ast_play_and_wait(chan, "vm-deleted");
10159             } else {
10160                if (play_folder == 0) {
10161                   if (in_urgent) {
10162                      vms.urgentmessages++;
10163                   } else {
10164                      vms.newmessages++;
10165                   }
10166                }
10167                else if (play_folder == 1)
10168                   vms.oldmessages++;
10169                cmd = ast_play_and_wait(chan, "vm-undeleted");
10170             }
10171             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10172                if (vms.curmsg < vms.lastmsg) {
10173                   vms.curmsg++;
10174                   cmd = play_message(chan, vmu, &vms);
10175                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10176                   vms.curmsg = 0;
10177                   cmd = play_message(chan, vmu, &vms);
10178                } else {
10179                   /* Check if we were listening to urgent
10180                      messages.  If so, go to regular new messages
10181                      instead of saying "no more messages"
10182                   */
10183                   if (in_urgent == 1) {
10184                      /* Check for new messages */
10185                      in_urgent = 0;
10186                      res = close_mailbox(&vms, vmu);
10187                      if (res == ERROR_LOCK_PATH)
10188                         goto out;
10189                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10190                      if (res < 0)
10191                         goto out;
10192                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10193                      vms.curmsg = -1;
10194                      if (vms.lastmsg < 0)
10195                         cmd = ast_play_and_wait(chan, "vm-nomore");
10196                   } else {
10197                      cmd = ast_play_and_wait(chan, "vm-nomore");
10198                   }
10199                }
10200             }
10201          } else /* Delete not valid if we haven't selected a message */
10202             cmd = 0;
10203 #ifdef IMAP_STORAGE
10204          deleted = 1;
10205 #endif
10206          break;
10207    
10208       case '8': /* Forward the current messgae */
10209          if (vms.lastmsg > -1) {
10210             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10211             if (cmd == ERROR_LOCK_PATH) {
10212                res = cmd;
10213                goto out;
10214             }
10215          } else {
10216             /* Check if we were listening to urgent
10217                messages.  If so, go to regular new messages
10218                instead of saying "no more messages"
10219             */
10220             if (in_urgent == 1 && vms.newmessages > 0) {
10221                /* Check for new messages */
10222                in_urgent = 0;
10223                res = close_mailbox(&vms, vmu);
10224                if (res == ERROR_LOCK_PATH)
10225                   goto out;
10226                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10227                if (res < 0)
10228                   goto out;
10229                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10230                vms.curmsg = -1;
10231                if (vms.lastmsg < 0)
10232                   cmd = ast_play_and_wait(chan, "vm-nomore");
10233             } else {
10234                cmd = ast_play_and_wait(chan, "vm-nomore");
10235             }
10236          }
10237          break;
10238       case '9': /* Save message to folder */
10239          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10240             /* No message selected */
10241             cmd = 0;
10242             break;
10243          }
10244          if (useadsi)
10245             adsi_folders(chan, 1, "Save to folder...");
10246          cmd = get_folder2(chan, "vm-savefolder", 1);
10247          box = 0; /* Shut up compiler */
10248          if (cmd == '#') {
10249             cmd = 0;
10250             break;
10251          } else if (cmd > 0) {
10252             box = cmd = cmd - '0';
10253             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10254             if (cmd == ERROR_LOCK_PATH) {
10255                res = cmd;
10256                goto out;
10257 #ifndef IMAP_STORAGE
10258             } else if (!cmd) {
10259                vms.deleted[vms.curmsg] = 1;
10260 #endif
10261             } else {
10262                vms.deleted[vms.curmsg] = 0;
10263                vms.heard[vms.curmsg] = 0;
10264             }
10265          }
10266          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10267          if (useadsi)
10268             adsi_message(chan, &vms);
10269          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10270          if (!cmd) {
10271             cmd = ast_play_and_wait(chan, "vm-message");
10272             if (!cmd) 
10273                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10274             if (!cmd)
10275                cmd = ast_play_and_wait(chan, "vm-savedto");
10276             if (!cmd)
10277                cmd = vm_play_folder_name(chan, vms.fn);
10278          } else {
10279             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10280          }
10281          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10282             if (vms.curmsg < vms.lastmsg) {
10283                vms.curmsg++;
10284                cmd = play_message(chan, vmu, &vms);
10285             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10286                vms.curmsg = 0;
10287                cmd = play_message(chan, vmu, &vms);
10288             } else {
10289                /* Check if we were listening to urgent
10290                   messages.  If so, go to regular new messages
10291                   instead of saying "no more messages"
10292                */
10293                if (in_urgent == 1 && vms.newmessages > 0) {
10294                   /* Check for new messages */
10295                   in_urgent = 0;
10296                   res = close_mailbox(&vms, vmu);
10297                   if (res == ERROR_LOCK_PATH)
10298                      goto out;
10299                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10300                   if (res < 0)
10301                      goto out;
10302                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10303                   vms.curmsg = -1;
10304                   if (vms.lastmsg < 0)
10305                      cmd = ast_play_and_wait(chan, "vm-nomore");
10306                } else {
10307                   cmd = ast_play_and_wait(chan, "vm-nomore");
10308                }
10309             }
10310          }
10311          break;
10312       case '*': /* Help */
10313          if (!vms.starting) {
10314             cmd = ast_play_and_wait(chan, "vm-onefor");
10315             if (!strncasecmp(chan->language, "he", 2)) {
10316                cmd = ast_play_and_wait(chan, "vm-for");
10317             }
10318             if (!cmd)
10319                cmd = vm_play_folder_name(chan, vms.vmbox);
10320             if (!cmd)
10321                cmd = ast_play_and_wait(chan, "vm-opts");
10322             if (!cmd)
10323                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10324          } else
10325             cmd = 0;
10326          break;
10327       case '0': /* Mailbox options */
10328          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10329          if (useadsi)
10330             adsi_status(chan, &vms);
10331          break;
10332       default: /* Nothing */
10333          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10334          break;
10335       }
10336    }
10337    if ((cmd == 't') || (cmd == '#')) {
10338       /* Timeout */
10339       res = 0;
10340    } else {
10341       /* Hangup */
10342       res = -1;
10343    }
10344 
10345 out:
10346    if (res > -1) {
10347       ast_stopstream(chan);
10348       adsi_goodbye(chan);
10349       if (valid && res != OPERATOR_EXIT) {
10350          if (silentexit)
10351             res = ast_play_and_wait(chan, "vm-dialout");
10352          else 
10353             res = ast_play_and_wait(chan, "vm-goodbye");
10354       }
10355       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10356          res = 0;
10357       }
10358       if (useadsi)
10359          ast_adsi_unload_session(chan);
10360    }
10361    if (vmu)
10362       close_mailbox(&vms, vmu);
10363    if (valid) {
10364       int new = 0, old = 0, urgent = 0;
10365       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10366       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10367       /* Urgent flag not passwd to externnotify here */
10368       run_externnotify(vmu->context, vmu->mailbox, NULL);
10369       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10370       queue_mwi_event(ext_context, urgent, new, old);
10371    }
10372 #ifdef IMAP_STORAGE
10373    /* expunge message - use UID Expunge if supported on IMAP server*/
10374    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10375    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10376       ast_mutex_lock(&vms.lock);
10377 #ifdef HAVE_IMAP_TK2006
10378       if (LEVELUIDPLUS (vms.mailstream)) {
10379          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10380       } else 
10381 #endif
10382          mail_expunge(vms.mailstream);
10383       ast_mutex_unlock(&vms.lock);
10384    }
10385    /*  before we delete the state, we should copy pertinent info
10386     *  back to the persistent model */
10387    if (vmu) {
10388       vmstate_delete(&vms);
10389    }
10390 #endif
10391    if (vmu)
10392       free_user(vmu);
10393    if (vms.deleted)
10394       ast_free(vms.deleted);
10395    if (vms.heard)
10396       ast_free(vms.heard);
10397 
10398 #ifdef IMAP_STORAGE
10399    pthread_setspecific(ts_vmstate.key, NULL);
10400 #endif
10401    return res;
10402 }
10403 
10404 static int vm_exec(struct ast_channel *chan, const char *data)
10405 {
10406    int res = 0;
10407    char *tmp;
10408    struct leave_vm_options leave_options;
10409    struct ast_flags flags = { 0 };
10410    char *opts[OPT_ARG_ARRAY_SIZE];
10411    AST_DECLARE_APP_ARGS(args,
10412       AST_APP_ARG(argv0);
10413       AST_APP_ARG(argv1);
10414    );
10415    
10416    memset(&leave_options, 0, sizeof(leave_options));
10417 
10418    if (chan->_state != AST_STATE_UP)
10419       ast_answer(chan);
10420 
10421    if (!ast_strlen_zero(data)) {
10422       tmp = ast_strdupa(data);
10423       AST_STANDARD_APP_ARGS(args, tmp);
10424       if (args.argc == 2) {
10425          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10426             return -1;
10427          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10428          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10429             int gain;
10430 
10431             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10432                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10433                return -1;
10434             } else {
10435                leave_options.record_gain = (signed char) gain;
10436             }
10437          }
10438          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10439             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10440                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10441          }
10442       }
10443    } else {
10444       char temp[256];
10445       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10446       if (res < 0)
10447          return res;
10448       if (ast_strlen_zero(temp))
10449          return 0;
10450       args.argv0 = ast_strdupa(temp);
10451    }
10452 
10453    res = leave_voicemail(chan, args.argv0, &leave_options);
10454    if (res == 't') {
10455       ast_play_and_wait(chan, "vm-goodbye");
10456       res = 0;
10457    }
10458 
10459    if (res == OPERATOR_EXIT) {
10460       res = 0;
10461    }
10462 
10463    if (res == ERROR_LOCK_PATH) {
10464       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10465       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10466       res = 0;
10467    }
10468 
10469    return res;
10470 }
10471 
10472 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10473 {
10474    struct ast_vm_user *vmu;
10475 
10476    AST_LIST_TRAVERSE(&users, vmu, list) {
10477       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10478          if (strcasecmp(vmu->context, context)) {
10479             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10480                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10481                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10482                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10483          }
10484          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10485          return NULL;
10486       }
10487       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10488          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10489          return NULL;
10490       }
10491    }
10492    
10493    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10494       return NULL;
10495    
10496    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10497    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10498 
10499    AST_LIST_INSERT_TAIL(&users, vmu, list);
10500    
10501    return vmu;
10502 }
10503 
10504 static int append_mailbox(const char *context, const char *box, const char *data)
10505 {
10506    /* Assumes lock is already held */
10507    char *tmp;
10508    char *stringp;
10509    char *s;
10510    struct ast_vm_user *vmu;
10511    char *mailbox_full;
10512    int new = 0, old = 0, urgent = 0;
10513    char secretfn[PATH_MAX] = "";
10514 
10515    tmp = ast_strdupa(data);
10516 
10517    if (!(vmu = find_or_create(context, box)))
10518       return -1;
10519 
10520    populate_defaults(vmu);
10521 
10522    stringp = tmp;
10523    if ((s = strsep(&stringp, ","))) {
10524       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10525    }
10526    if (stringp && (s = strsep(&stringp, ","))) {
10527       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10528    }
10529    if (stringp && (s = strsep(&stringp, ","))) {
10530       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10531    }
10532    if (stringp && (s = strsep(&stringp, ","))) {
10533       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10534    }
10535    if (stringp && (s = strsep(&stringp, ","))) {
10536       apply_options(vmu, s);
10537    }
10538 
10539    switch (vmu->passwordlocation) {
10540    case OPT_PWLOC_SPOOLDIR:
10541       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10542       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10543    }
10544 
10545    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10546    strcpy(mailbox_full, box);
10547    strcat(mailbox_full, "@");
10548    strcat(mailbox_full, context);
10549 
10550    inboxcount2(mailbox_full, &urgent, &new, &old);
10551    queue_mwi_event(mailbox_full, urgent, new, old);
10552 
10553    return 0;
10554 }
10555 
10556 AST_TEST_DEFINE(test_voicemail_vmuser)
10557 {
10558    int res = 0;
10559    struct ast_vm_user *vmu;
10560    /* language parameter seems to only be used for display in manager action */
10561    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10562       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10563       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10564       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10565       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10566       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10567       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir";
10568 #ifdef IMAP_STORAGE
10569    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10570       "imapfolder=INBOX|imapvmshareid=6000";
10571 #endif
10572 
10573    switch (cmd) {
10574    case TEST_INIT:
10575       info->name = "vmuser";
10576       info->category = "/apps/app_voicemail/";
10577       info->summary = "Vmuser unit test";
10578       info->description =
10579          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10580       return AST_TEST_NOT_RUN;
10581    case TEST_EXECUTE:
10582       break;
10583    }
10584 
10585    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10586       return AST_TEST_NOT_RUN;
10587    }
10588    ast_set_flag(vmu, VM_ALLOCED);
10589 
10590    apply_options(vmu, options_string);
10591 
10592    if (!ast_test_flag(vmu, VM_ATTACH)) {
10593       ast_test_status_update(test, "Parse failure for attach option\n");
10594       res = 1;
10595    }
10596    if (strcasecmp(vmu->attachfmt, "wav49")) {
10597       ast_test_status_update(test, "Parse failure for attachftm option\n");
10598       res = 1;
10599    }
10600    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10601       ast_test_status_update(test, "Parse failure for serveremail option\n");
10602       res = 1;
10603    }
10604    if (strcasecmp(vmu->zonetag, "central")) {
10605       ast_test_status_update(test, "Parse failure for tz option\n");
10606       res = 1;
10607    }
10608    if (!ast_test_flag(vmu, VM_DELETE)) {
10609       ast_test_status_update(test, "Parse failure for delete option\n");
10610       res = 1;
10611    }
10612    if (!ast_test_flag(vmu, VM_SAYCID)) {
10613       ast_test_status_update(test, "Parse failure for saycid option\n");
10614       res = 1;
10615    }
10616    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10617       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10618       res = 1;
10619    }
10620    if (!ast_test_flag(vmu, VM_REVIEW)) {
10621       ast_test_status_update(test, "Parse failure for review option\n");
10622       res = 1;
10623    }
10624    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10625       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10626       res = 1;
10627    }
10628    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10629       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10630       res = 1;
10631    }
10632    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10633       ast_test_status_update(test, "Parse failure for operator option\n");
10634       res = 1;
10635    }
10636    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10637       ast_test_status_update(test, "Parse failure for envelope option\n");
10638       res = 1;
10639    }
10640    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10641       ast_test_status_update(test, "Parse failure for moveheard option\n");
10642       res = 1;
10643    }
10644    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10645       ast_test_status_update(test, "Parse failure for sayduration option\n");
10646       res = 1;
10647    }
10648    if (vmu->saydurationm != 5) {
10649       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10650       res = 1;
10651    }
10652    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10653       ast_test_status_update(test, "Parse failure for forcename option\n");
10654       res = 1;
10655    }
10656    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10657       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10658       res = 1;
10659    }
10660    if (strcasecmp(vmu->callback, "somecontext")) {
10661       ast_test_status_update(test, "Parse failure for callbacks option\n");
10662       res = 1;
10663    }
10664    if (strcasecmp(vmu->dialout, "somecontext2")) {
10665       ast_test_status_update(test, "Parse failure for dialout option\n");
10666       res = 1;
10667    }
10668    if (strcasecmp(vmu->exit, "somecontext3")) {
10669       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10670       res = 1;
10671    }
10672    if (vmu->minsecs != 10) {
10673       ast_test_status_update(test, "Parse failure for minsecs option\n");
10674       res = 1;
10675    }
10676    if (vmu->maxsecs != 100) {
10677       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10678       res = 1;
10679    }
10680    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10681       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10682       res = 1;
10683    }
10684    if (vmu->maxdeletedmsg != 50) {
10685       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10686       res = 1;
10687    }
10688    if (vmu->volgain != 1.3) {
10689       ast_test_status_update(test, "Parse failure for volgain option\n");
10690       res = 1;
10691    }
10692    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10693       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10694       res = 1;
10695    }
10696 #ifdef IMAP_STORAGE
10697    apply_options(vmu, option_string2);
10698 
10699    if (strcasecmp(vmu->imapuser, "imapuser")) {
10700       ast_test_status_update(test, "Parse failure for imapuser option\n");
10701       res = 1;
10702    }
10703    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10704       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10705       res = 1;
10706    }
10707    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10708       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10709       res = 1;
10710    }
10711    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10712       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10713       res = 1;
10714    }
10715 #endif
10716 
10717    free_user(vmu);
10718    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10719 }
10720 
10721 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10722 {
10723    struct ast_vm_user svm;
10724    char *context, *box;
10725    AST_DECLARE_APP_ARGS(args,
10726       AST_APP_ARG(mbox);
10727       AST_APP_ARG(options);
10728    );
10729    static int dep_warning = 0;
10730 
10731    if (ast_strlen_zero(data)) {
10732       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10733       return -1;
10734    }
10735 
10736    if (!dep_warning) {
10737       dep_warning = 1;
10738       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10739    }
10740 
10741    box = ast_strdupa(data);
10742 
10743    AST_STANDARD_APP_ARGS(args, box);
10744 
10745    if (args.options) {
10746    }
10747 
10748    if ((context = strchr(args.mbox, '@'))) {
10749       *context = '\0';
10750       context++;
10751    }
10752 
10753    if (find_user(&svm, context, args.mbox)) {
10754       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10755    } else
10756       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10757 
10758    return 0;
10759 }
10760 
10761 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10762 {
10763    struct ast_vm_user svm;
10764    AST_DECLARE_APP_ARGS(arg,
10765       AST_APP_ARG(mbox);
10766       AST_APP_ARG(context);
10767    );
10768 
10769    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10770 
10771    if (ast_strlen_zero(arg.mbox)) {
10772       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10773       return -1;
10774    }
10775 
10776    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10777    return 0;
10778 }
10779 
10780 static struct ast_custom_function mailbox_exists_acf = {
10781    .name = "MAILBOX_EXISTS",
10782    .read = acf_mailbox_exists,
10783 };
10784 
10785 static int vmauthenticate(struct ast_channel *chan, const char *data)
10786 {
10787    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
10788    struct ast_vm_user vmus;
10789    char *options = NULL;
10790    int silent = 0, skipuser = 0;
10791    int res = -1;
10792    
10793    if (data) {
10794       s = ast_strdupa(data);
10795       user = strsep(&s, ",");
10796       options = strsep(&s, ",");
10797       if (user) {
10798          s = user;
10799          user = strsep(&s, "@");
10800          context = strsep(&s, "");
10801          if (!ast_strlen_zero(user))
10802             skipuser++;
10803          ast_copy_string(mailbox, user, sizeof(mailbox));
10804       }
10805    }
10806 
10807    if (options) {
10808       silent = (strchr(options, 's')) != NULL;
10809    }
10810 
10811    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10812       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10813       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10814       ast_play_and_wait(chan, "auth-thankyou");
10815       res = 0;
10816    } else if (mailbox[0] == '*') {
10817       /* user entered '*' */
10818       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10819          res = 0; /* prevent hangup */
10820       }
10821    }
10822 
10823    return res;
10824 }
10825 
10826 static char *show_users_realtime(int fd, const char *context)
10827 {
10828    struct ast_config *cfg;
10829    const char *cat = NULL;
10830 
10831    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10832       "context", context, SENTINEL))) {
10833       return CLI_FAILURE;
10834    }
10835 
10836    ast_cli(fd,
10837       "\n"
10838       "=============================================================\n"
10839       "=== Configured Voicemail Users ==============================\n"
10840       "=============================================================\n"
10841       "===\n");
10842 
10843    while ((cat = ast_category_browse(cfg, cat))) {
10844       struct ast_variable *var = NULL;
10845       ast_cli(fd,
10846          "=== Mailbox ...\n"
10847          "===\n");
10848       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10849          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10850       ast_cli(fd,
10851          "===\n"
10852          "=== ---------------------------------------------------------\n"
10853          "===\n");
10854    }
10855 
10856    ast_cli(fd,
10857       "=============================================================\n"
10858       "\n");
10859 
10860    ast_config_destroy(cfg);
10861 
10862    return CLI_SUCCESS;
10863 }
10864 
10865 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10866 {
10867    int which = 0;
10868    int wordlen;
10869    struct ast_vm_user *vmu;
10870    const char *context = "";
10871 
10872    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10873    if (pos > 4)
10874       return NULL;
10875    if (pos == 3)
10876       return (state == 0) ? ast_strdup("for") : NULL;
10877    wordlen = strlen(word);
10878    AST_LIST_TRAVERSE(&users, vmu, list) {
10879       if (!strncasecmp(word, vmu->context, wordlen)) {
10880          if (context && strcmp(context, vmu->context) && ++which > state)
10881             return ast_strdup(vmu->context);
10882          /* ignore repeated contexts ? */
10883          context = vmu->context;
10884       }
10885    }
10886    return NULL;
10887 }
10888 
10889 /*! \brief Show a list of voicemail users in the CLI */
10890 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10891 {
10892    struct ast_vm_user *vmu;
10893 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10894    const char *context = NULL;
10895    int users_counter = 0;
10896 
10897    switch (cmd) {
10898    case CLI_INIT:
10899       e->command = "voicemail show users";
10900       e->usage =
10901          "Usage: voicemail show users [for <context>]\n"
10902          "       Lists all mailboxes currently set up\n";
10903       return NULL;
10904    case CLI_GENERATE:
10905       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10906    }  
10907 
10908    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10909       return CLI_SHOWUSAGE;
10910    if (a->argc == 5) {
10911       if (strcmp(a->argv[3],"for"))
10912          return CLI_SHOWUSAGE;
10913       context = a->argv[4];
10914    }
10915 
10916    if (ast_check_realtime("voicemail")) {
10917       if (!context) {
10918          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10919          return CLI_SHOWUSAGE;
10920       }
10921       return show_users_realtime(a->fd, context);
10922    }
10923 
10924    AST_LIST_LOCK(&users);
10925    if (AST_LIST_EMPTY(&users)) {
10926       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10927       AST_LIST_UNLOCK(&users);
10928       return CLI_FAILURE;
10929    }
10930    if (a->argc == 3)
10931       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10932    else {
10933       int count = 0;
10934       AST_LIST_TRAVERSE(&users, vmu, list) {
10935          if (!strcmp(context, vmu->context))
10936             count++;
10937       }
10938       if (count) {
10939          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10940       } else {
10941          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10942          AST_LIST_UNLOCK(&users);
10943          return CLI_FAILURE;
10944       }
10945    }
10946    AST_LIST_TRAVERSE(&users, vmu, list) {
10947       int newmsgs = 0, oldmsgs = 0;
10948       char count[12], tmp[256] = "";
10949 
10950       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10951          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10952          inboxcount(tmp, &newmsgs, &oldmsgs);
10953          snprintf(count, sizeof(count), "%d", newmsgs);
10954          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10955          users_counter++;
10956       }
10957    }
10958    AST_LIST_UNLOCK(&users);
10959    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10960    return CLI_SUCCESS;
10961 }
10962 
10963 /*! \brief Show a list of voicemail zones in the CLI */
10964 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10965 {
10966    struct vm_zone *zone;
10967 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10968    char *res = CLI_SUCCESS;
10969 
10970    switch (cmd) {
10971    case CLI_INIT:
10972       e->command = "voicemail show zones";
10973       e->usage =
10974          "Usage: voicemail show zones\n"
10975          "       Lists zone message formats\n";
10976       return NULL;
10977    case CLI_GENERATE:
10978       return NULL;
10979    }
10980 
10981    if (a->argc != 3)
10982       return CLI_SHOWUSAGE;
10983 
10984    AST_LIST_LOCK(&zones);
10985    if (!AST_LIST_EMPTY(&zones)) {
10986       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10987       AST_LIST_TRAVERSE(&zones, zone, list) {
10988          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10989       }
10990    } else {
10991       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10992       res = CLI_FAILURE;
10993    }
10994    AST_LIST_UNLOCK(&zones);
10995 
10996    return res;
10997 }
10998 
10999 /*! \brief Reload voicemail configuration from the CLI */
11000 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11001 {
11002    switch (cmd) {
11003    case CLI_INIT:
11004       e->command = "voicemail reload";
11005       e->usage =
11006          "Usage: voicemail reload\n"
11007          "       Reload voicemail configuration\n";
11008       return NULL;
11009    case CLI_GENERATE:
11010       return NULL;
11011    }
11012 
11013    if (a->argc != 2)
11014       return CLI_SHOWUSAGE;
11015 
11016    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11017    load_config(1);
11018    
11019    return CLI_SUCCESS;
11020 }
11021 
11022 static struct ast_cli_entry cli_voicemail[] = {
11023    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11024    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11025    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11026 };
11027 
11028 #ifdef IMAP_STORAGE
11029    #define DATA_EXPORT_VM_USERS(USER)              \
11030       USER(ast_vm_user, context, AST_DATA_STRING)        \
11031       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11032       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11033       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11034       USER(ast_vm_user, email, AST_DATA_STRING)       \
11035       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11036       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11037       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11038       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11039       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11040       USER(ast_vm_user, language, AST_DATA_STRING)       \
11041       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11042       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11043       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11044       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11045       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11046       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11047       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11048       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11049       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11050       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11051       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11052       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11053       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11054       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11055       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11056 #else
11057    #define DATA_EXPORT_VM_USERS(USER)              \
11058       USER(ast_vm_user, context, AST_DATA_STRING)        \
11059       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11060       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11061       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11062       USER(ast_vm_user, email, AST_DATA_STRING)       \
11063       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11064       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11065       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11066       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11067       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11068       USER(ast_vm_user, language, AST_DATA_STRING)       \
11069       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11070       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11071       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11072       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11073       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11074       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11075       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11076       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11077       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11078       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11079       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11080       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11081 #endif
11082 
11083 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11084 
11085 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11086    ZONE(vm_zone, name, AST_DATA_STRING)      \
11087    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11088    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11089 
11090 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11091 
11092 /*!
11093  * \internal
11094  * \brief Add voicemail user to the data_root.
11095  * \param[in] search The search tree.
11096  * \param[in] data_root The main result node.
11097  * \param[in] user The voicemail user.
11098  */
11099 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11100     struct ast_data *data_root, struct ast_vm_user *user)
11101 {
11102    struct ast_data *data_user, *data_zone;
11103    struct ast_data *data_state;
11104    struct vm_zone *zone = NULL;
11105    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11106    char ext_context[256] = "";
11107 
11108    data_user = ast_data_add_node(data_root, "user");
11109    if (!data_user) {
11110       return -1;
11111    }
11112 
11113    ast_data_add_structure(ast_vm_user, data_user, user);
11114 
11115    AST_LIST_LOCK(&zones);
11116    AST_LIST_TRAVERSE(&zones, zone, list) {
11117       if (!strcmp(zone->name, user->zonetag)) {
11118          break;
11119       }
11120    }
11121    AST_LIST_UNLOCK(&zones);
11122 
11123    /* state */
11124    data_state = ast_data_add_node(data_user, "state");
11125    if (!data_state) {
11126       return -1;
11127    }
11128    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11129    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11130    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11131    ast_data_add_int(data_state, "newmsg", newmsg);
11132    ast_data_add_int(data_state, "oldmsg", oldmsg);
11133 
11134    if (zone) {
11135       data_zone = ast_data_add_node(data_user, "zone");
11136       ast_data_add_structure(vm_zone, data_zone, zone);
11137    }
11138 
11139    if (!ast_data_search_match(search, data_user)) {
11140       ast_data_remove_node(data_root, data_user);
11141    }
11142 
11143    return 0;
11144 }
11145 
11146 static int vm_users_data_provider_get(const struct ast_data_search *search,
11147    struct ast_data *data_root)
11148 {
11149    struct ast_vm_user *user;
11150 
11151    AST_LIST_LOCK(&users);
11152    AST_LIST_TRAVERSE(&users, user, list) {
11153       vm_users_data_provider_get_helper(search, data_root, user);
11154    }
11155    AST_LIST_UNLOCK(&users);
11156 
11157    return 0;
11158 }
11159 
11160 static const struct ast_data_handler vm_users_data_provider = {
11161    .version = AST_DATA_HANDLER_VERSION,
11162    .get = vm_users_data_provider_get
11163 };
11164 
11165 static const struct ast_data_entry vm_data_providers[] = {
11166    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11167 };
11168 
11169 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11170 {
11171    int new = 0, old = 0, urgent = 0;
11172 
11173    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11174 
11175    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11176       mwi_sub->old_urgent = urgent;
11177       mwi_sub->old_new = new;
11178       mwi_sub->old_old = old;
11179       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11180       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11181    }
11182 }
11183 
11184 static void poll_subscribed_mailboxes(void)
11185 {
11186    struct mwi_sub *mwi_sub;
11187 
11188    AST_RWLIST_RDLOCK(&mwi_subs);
11189    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11190       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11191          poll_subscribed_mailbox(mwi_sub);
11192       }
11193    }
11194    AST_RWLIST_UNLOCK(&mwi_subs);
11195 }
11196 
11197 static void *mb_poll_thread(void *data)
11198 {
11199    while (poll_thread_run) {
11200       struct timespec ts = { 0, };
11201       struct timeval wait;
11202 
11203       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11204       ts.tv_sec = wait.tv_sec;
11205       ts.tv_nsec = wait.tv_usec * 1000;
11206 
11207       ast_mutex_lock(&poll_lock);
11208       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11209       ast_mutex_unlock(&poll_lock);
11210 
11211       if (!poll_thread_run)
11212          break;
11213 
11214       poll_subscribed_mailboxes();
11215    }
11216 
11217    return NULL;
11218 }
11219 
11220 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11221 {
11222    ast_free(mwi_sub);
11223 }
11224 
11225 static int handle_unsubscribe(void *datap)
11226 {
11227    struct mwi_sub *mwi_sub;
11228    uint32_t *uniqueid = datap;
11229    
11230    AST_RWLIST_WRLOCK(&mwi_subs);
11231    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11232       if (mwi_sub->uniqueid == *uniqueid) {
11233          AST_LIST_REMOVE_CURRENT(entry);
11234          break;
11235       }
11236    }
11237    AST_RWLIST_TRAVERSE_SAFE_END
11238    AST_RWLIST_UNLOCK(&mwi_subs);
11239 
11240    if (mwi_sub)
11241       mwi_sub_destroy(mwi_sub);
11242 
11243    ast_free(uniqueid);  
11244    return 0;
11245 }
11246 
11247 static int handle_subscribe(void *datap)
11248 {
11249    unsigned int len;
11250    struct mwi_sub *mwi_sub;
11251    struct mwi_sub_task *p = datap;
11252 
11253    len = sizeof(*mwi_sub);
11254    if (!ast_strlen_zero(p->mailbox))
11255       len += strlen(p->mailbox);
11256 
11257    if (!ast_strlen_zero(p->context))
11258       len += strlen(p->context) + 1; /* Allow for seperator */
11259 
11260    if (!(mwi_sub = ast_calloc(1, len)))
11261       return -1;
11262 
11263    mwi_sub->uniqueid = p->uniqueid;
11264    if (!ast_strlen_zero(p->mailbox))
11265       strcpy(mwi_sub->mailbox, p->mailbox);
11266 
11267    if (!ast_strlen_zero(p->context)) {
11268       strcat(mwi_sub->mailbox, "@");
11269       strcat(mwi_sub->mailbox, p->context);
11270    }
11271 
11272    AST_RWLIST_WRLOCK(&mwi_subs);
11273    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11274    AST_RWLIST_UNLOCK(&mwi_subs);
11275    ast_free((void *) p->mailbox);
11276    ast_free((void *) p->context);
11277    ast_free(p);
11278    poll_subscribed_mailbox(mwi_sub);
11279    return 0;
11280 }
11281 
11282 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11283 {
11284    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11285    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11286       return;
11287 
11288    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11289       return;
11290 
11291    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11292    *uniqueid = u;
11293    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11294       ast_free(uniqueid);
11295    }
11296 }
11297 
11298 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11299 {
11300    struct mwi_sub_task *mwist;
11301    
11302    if (ast_event_get_type(event) != AST_EVENT_SUB)
11303       return;
11304 
11305    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11306       return;
11307 
11308    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11309       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11310       return;
11311    }
11312    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11313    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11314    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11315    
11316    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11317       ast_free(mwist);
11318    }
11319 }
11320 
11321 static void start_poll_thread(void)
11322 {
11323    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11324       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11325       AST_EVENT_IE_END);
11326 
11327    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11328       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11329       AST_EVENT_IE_END);
11330 
11331    if (mwi_sub_sub)
11332       ast_event_report_subs(mwi_sub_sub);
11333 
11334    poll_thread_run = 1;
11335 
11336    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11337 }
11338 
11339 static void stop_poll_thread(void)
11340 {
11341    poll_thread_run = 0;
11342 
11343    if (mwi_sub_sub) {
11344       ast_event_unsubscribe(mwi_sub_sub);
11345       mwi_sub_sub = NULL;
11346    }
11347 
11348    if (mwi_unsub_sub) {
11349       ast_event_unsubscribe(mwi_unsub_sub);
11350       mwi_unsub_sub = NULL;
11351    }
11352 
11353    ast_mutex_lock(&poll_lock);
11354    ast_cond_signal(&poll_cond);
11355    ast_mutex_unlock(&poll_lock);
11356 
11357    pthread_join(poll_thread, NULL);
11358 
11359    poll_thread = AST_PTHREADT_NULL;
11360 }
11361 
11362 /*! \brief Manager list voicemail users command */
11363 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11364 {
11365    struct ast_vm_user *vmu = NULL;
11366    const char *id = astman_get_header(m, "ActionID");
11367    char actionid[128] = "";
11368 
11369    if (!ast_strlen_zero(id))
11370       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11371 
11372    AST_LIST_LOCK(&users);
11373 
11374    if (AST_LIST_EMPTY(&users)) {
11375       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11376       AST_LIST_UNLOCK(&users);
11377       return RESULT_SUCCESS;
11378    }
11379    
11380    astman_send_ack(s, m, "Voicemail user list will follow");
11381    
11382    AST_LIST_TRAVERSE(&users, vmu, list) {
11383       char dirname[256];
11384 
11385 #ifdef IMAP_STORAGE
11386       int new, old;
11387       inboxcount(vmu->mailbox, &new, &old);
11388 #endif
11389       
11390       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11391       astman_append(s,
11392          "%s"
11393          "Event: VoicemailUserEntry\r\n"
11394          "VMContext: %s\r\n"
11395          "VoiceMailbox: %s\r\n"
11396          "Fullname: %s\r\n"
11397          "Email: %s\r\n"
11398          "Pager: %s\r\n"
11399          "ServerEmail: %s\r\n"
11400          "MailCommand: %s\r\n"
11401          "Language: %s\r\n"
11402          "TimeZone: %s\r\n"
11403          "Callback: %s\r\n"
11404          "Dialout: %s\r\n"
11405          "UniqueID: %s\r\n"
11406          "ExitContext: %s\r\n"
11407          "SayDurationMinimum: %d\r\n"
11408          "SayEnvelope: %s\r\n"
11409          "SayCID: %s\r\n"
11410          "AttachMessage: %s\r\n"
11411          "AttachmentFormat: %s\r\n"
11412          "DeleteMessage: %s\r\n"
11413          "VolumeGain: %.2f\r\n"
11414          "CanReview: %s\r\n"
11415          "CallOperator: %s\r\n"
11416          "MaxMessageCount: %d\r\n"
11417          "MaxMessageLength: %d\r\n"
11418          "NewMessageCount: %d\r\n"
11419 #ifdef IMAP_STORAGE
11420          "OldMessageCount: %d\r\n"
11421          "IMAPUser: %s\r\n"
11422 #endif
11423          "\r\n",
11424          actionid,
11425          vmu->context,
11426          vmu->mailbox,
11427          vmu->fullname,
11428          vmu->email,
11429          vmu->pager,
11430          vmu->serveremail,
11431          vmu->mailcmd,
11432          vmu->language,
11433          vmu->zonetag,
11434          vmu->callback,
11435          vmu->dialout,
11436          vmu->uniqueid,
11437          vmu->exit,
11438          vmu->saydurationm,
11439          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11440          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11441          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11442          vmu->attachfmt,
11443          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11444          vmu->volgain,
11445          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11446          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11447          vmu->maxmsg,
11448          vmu->maxsecs,
11449 #ifdef IMAP_STORAGE
11450          new, old, vmu->imapuser
11451 #else
11452          count_messages(vmu, dirname)
11453 #endif
11454          );
11455    }     
11456    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11457 
11458    AST_LIST_UNLOCK(&users);
11459 
11460    return RESULT_SUCCESS;
11461 }
11462 
11463 /*! \brief Free the users structure. */
11464 static void free_vm_users(void) 
11465 {
11466    struct ast_vm_user *current;
11467    AST_LIST_LOCK(&users);
11468    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11469       ast_set_flag(current, VM_ALLOCED);
11470       free_user(current);
11471    }
11472    AST_LIST_UNLOCK(&users);
11473 }
11474 
11475 /*! \brief Free the zones structure. */
11476 static void free_vm_zones(void)
11477 {
11478    struct vm_zone *zcur;
11479    AST_LIST_LOCK(&zones);
11480    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11481       free_zone(zcur);
11482    AST_LIST_UNLOCK(&zones);
11483 }
11484 
11485 static const char *substitute_escapes(const char *value)
11486 {
11487    char *current;
11488 
11489    /* Add 16 for fudge factor */
11490    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11491 
11492    ast_str_reset(str);
11493    
11494    /* Substitute strings \r, \n, and \t into the appropriate characters */
11495    for (current = (char *) value; *current; current++) {
11496       if (*current == '\\') {
11497          current++;
11498          if (!*current) {
11499             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11500             break;
11501          }
11502          switch (*current) {
11503          case 'r':
11504             ast_str_append(&str, 0, "\r");
11505             break;
11506          case 'n':
11507 #ifdef IMAP_STORAGE
11508             if (!str->used || str->str[str->used - 1] != '\r') {
11509                ast_str_append(&str, 0, "\r");
11510             }
11511 #endif
11512             ast_str_append(&str, 0, "\n");
11513             break;
11514          case 't':
11515             ast_str_append(&str, 0, "\t");
11516             break;
11517          default:
11518             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11519             break;
11520          }
11521       } else {
11522          ast_str_append(&str, 0, "%c", *current);
11523       }
11524    }
11525 
11526    return ast_str_buffer(str);
11527 }
11528 
11529 static int load_config(int reload)
11530 {
11531    struct ast_vm_user *current;
11532    struct ast_config *cfg, *ucfg;
11533    char *cat;
11534    struct ast_variable *var;
11535    const char *val;
11536    char *q, *stringp, *tmp;
11537    int x;
11538    int tmpadsi[4];
11539    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11540    char secretfn[PATH_MAX] = "";
11541 
11542    ast_unload_realtime("voicemail");
11543    ast_unload_realtime("voicemail_data");
11544 
11545    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11546       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11547          return 0;
11548       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11549          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11550          ucfg = NULL;
11551       }
11552       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11553       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11554          ast_config_destroy(ucfg);
11555          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11556          return 0;
11557       }
11558    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11559       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11560       return 0;
11561    } else {
11562       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11563       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11564          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11565          ucfg = NULL;
11566       }
11567    }
11568 #ifdef IMAP_STORAGE
11569    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11570 #endif
11571    /* set audio control prompts */
11572    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11573    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11574    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11575    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11576    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11577 
11578    /* Free all the users structure */  
11579    free_vm_users();
11580 
11581    /* Free all the zones structure */
11582    free_vm_zones();
11583 
11584    AST_LIST_LOCK(&users);  
11585 
11586    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11587    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11588 
11589    if (cfg) {
11590       /* General settings */
11591 
11592       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11593          val = "default";
11594       ast_copy_string(userscontext, val, sizeof(userscontext));
11595       /* Attach voice message to mail message ? */
11596       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11597          val = "yes";
11598       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11599 
11600       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11601          val = "no";
11602       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11603 
11604       volgain = 0.0;
11605       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11606          sscanf(val, "%30lf", &volgain);
11607 
11608 #ifdef ODBC_STORAGE
11609       strcpy(odbc_database, "asterisk");
11610       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11611          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11612       }
11613       strcpy(odbc_table, "voicemessages");
11614       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11615          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11616       }
11617 #endif      
11618       /* Mail command */
11619       strcpy(mailcmd, SENDMAIL);
11620       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11621          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11622 
11623       maxsilence = 0;
11624       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11625          maxsilence = atoi(val);
11626          if (maxsilence > 0)
11627             maxsilence *= 1000;
11628       }
11629       
11630       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11631          maxmsg = MAXMSG;
11632       } else {
11633          maxmsg = atoi(val);
11634          if (maxmsg < 0) {
11635             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11636             maxmsg = MAXMSG;
11637          } else if (maxmsg > MAXMSGLIMIT) {
11638             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11639             maxmsg = MAXMSGLIMIT;
11640          }
11641       }
11642 
11643       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11644          maxdeletedmsg = 0;
11645       } else {
11646          if (sscanf(val, "%30d", &x) == 1)
11647             maxdeletedmsg = x;
11648          else if (ast_true(val))
11649             maxdeletedmsg = MAXMSG;
11650          else
11651             maxdeletedmsg = 0;
11652 
11653          if (maxdeletedmsg < 0) {
11654             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11655             maxdeletedmsg = MAXMSG;
11656          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11657             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11658             maxdeletedmsg = MAXMSGLIMIT;
11659          }
11660       }
11661 
11662       /* Load date format config for voicemail mail */
11663       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11664          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11665       }
11666 
11667       /* Load date format config for voicemail pager mail */
11668       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11669          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11670       }
11671 
11672       /* External password changing command */
11673       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11674          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11675          pwdchange = PWDCHANGE_EXTERNAL;
11676       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11677          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11678          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11679       }
11680  
11681       /* External password validation command */
11682       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11683          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11684          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11685       }
11686 
11687 #ifdef IMAP_STORAGE
11688       /* IMAP server address */
11689       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11690          ast_copy_string(imapserver, val, sizeof(imapserver));
11691       } else {
11692          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11693       }
11694       /* IMAP server port */
11695       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11696          ast_copy_string(imapport, val, sizeof(imapport));
11697       } else {
11698          ast_copy_string(imapport, "143", sizeof(imapport));
11699       }
11700       /* IMAP server flags */
11701       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11702          ast_copy_string(imapflags, val, sizeof(imapflags));
11703       }
11704       /* IMAP server master username */
11705       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11706          ast_copy_string(authuser, val, sizeof(authuser));
11707       }
11708       /* IMAP server master password */
11709       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11710          ast_copy_string(authpassword, val, sizeof(authpassword));
11711       }
11712       /* Expunge on exit */
11713       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11714          if (ast_false(val))
11715             expungeonhangup = 0;
11716          else
11717             expungeonhangup = 1;
11718       } else {
11719          expungeonhangup = 1;
11720       }
11721       /* IMAP voicemail folder */
11722       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11723          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11724       } else {
11725          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11726       }
11727       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11728          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11729       }
11730       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11731          imapgreetings = ast_true(val);
11732       } else {
11733          imapgreetings = 0;
11734       }
11735       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11736          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11737       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11738          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11739          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11740       } else {
11741          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11742       }
11743 
11744       /* There is some very unorthodox casting done here. This is due
11745        * to the way c-client handles the argument passed in. It expects a 
11746        * void pointer and casts the pointer directly to a long without
11747        * first dereferencing it. */
11748       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11749          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11750       } else {
11751          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11752       }
11753 
11754       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11755          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11756       } else {
11757          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11758       }
11759 
11760       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11761          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11762       } else {
11763          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11764       }
11765 
11766       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11767          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11768       } else {
11769          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11770       }
11771 
11772       /* Increment configuration version */
11773       imapversion++;
11774 #endif
11775       /* External voicemail notify application */
11776       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11777          ast_copy_string(externnotify, val, sizeof(externnotify));
11778          ast_debug(1, "found externnotify: %s\n", externnotify);
11779       } else {
11780          externnotify[0] = '\0';
11781       }
11782 
11783       /* SMDI voicemail notification */
11784       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11785          ast_debug(1, "Enabled SMDI voicemail notification\n");
11786          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11787             smdi_iface = ast_smdi_interface_find(val);
11788          } else {
11789             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11790             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
11791          }
11792          if (!smdi_iface) {
11793             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11794          } 
11795       }
11796 
11797       /* Silence treshold */
11798       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11799       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11800          silencethreshold = atoi(val);
11801       
11802       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11803          val = ASTERISK_USERNAME;
11804       ast_copy_string(serveremail, val, sizeof(serveremail));
11805       
11806       vmmaxsecs = 0;
11807       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11808          if (sscanf(val, "%30d", &x) == 1) {
11809             vmmaxsecs = x;
11810          } else {
11811             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11812          }
11813       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11814          static int maxmessage_deprecate = 0;
11815          if (maxmessage_deprecate == 0) {
11816             maxmessage_deprecate = 1;
11817             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11818          }
11819          if (sscanf(val, "%30d", &x) == 1) {
11820             vmmaxsecs = x;
11821          } else {
11822             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11823          }
11824       }
11825 
11826       vmminsecs = 0;
11827       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11828          if (sscanf(val, "%30d", &x) == 1) {
11829             vmminsecs = x;
11830             if (maxsilence / 1000 >= vmminsecs) {
11831                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11832             }
11833          } else {
11834             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11835          }
11836       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11837          static int maxmessage_deprecate = 0;
11838          if (maxmessage_deprecate == 0) {
11839             maxmessage_deprecate = 1;
11840             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11841          }
11842          if (sscanf(val, "%30d", &x) == 1) {
11843             vmminsecs = x;
11844             if (maxsilence / 1000 >= vmminsecs) {
11845                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11846             }
11847          } else {
11848             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11849          }
11850       }
11851 
11852       val = ast_variable_retrieve(cfg, "general", "format");
11853       if (!val) {
11854          val = "wav";   
11855       } else {
11856          tmp = ast_strdupa(val);
11857          val = ast_format_str_reduce(tmp);
11858          if (!val) {
11859             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11860             val = "wav";
11861          }
11862       }
11863       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11864 
11865       skipms = 3000;
11866       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11867          if (sscanf(val, "%30d", &x) == 1) {
11868             maxgreet = x;
11869          } else {
11870             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11871          }
11872       }
11873 
11874       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11875          if (sscanf(val, "%30d", &x) == 1) {
11876             skipms = x;
11877          } else {
11878             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11879          }
11880       }
11881 
11882       maxlogins = 3;
11883       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11884          if (sscanf(val, "%30d", &x) == 1) {
11885             maxlogins = x;
11886          } else {
11887             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11888          }
11889       }
11890 
11891       minpassword = MINPASSWORD;
11892       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11893          if (sscanf(val, "%30d", &x) == 1) {
11894             minpassword = x;
11895          } else {
11896             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11897          }
11898       }
11899 
11900       /* Force new user to record name ? */
11901       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11902          val = "no";
11903       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11904 
11905       /* Force new user to record greetings ? */
11906       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11907          val = "no";
11908       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11909 
11910       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11911          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11912          stringp = ast_strdupa(val);
11913          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11914             if (!ast_strlen_zero(stringp)) {
11915                q = strsep(&stringp, ",");
11916                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11917                   q++;
11918                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11919                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11920             } else {
11921                cidinternalcontexts[x][0] = '\0';
11922             }
11923          }
11924       }
11925       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11926          ast_debug(1, "VM Review Option disabled globally\n");
11927          val = "no";
11928       }
11929       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11930 
11931       /* Temporary greeting reminder */
11932       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11933          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11934          val = "no";
11935       } else {
11936          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11937       }
11938       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11939       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11940          ast_debug(1, "VM next message wrap disabled globally\n");
11941          val = "no";
11942       }
11943       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11944 
11945       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11946          ast_debug(1, "VM Operator break disabled globally\n");
11947          val = "no";
11948       }
11949       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11950 
11951       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11952          ast_debug(1, "VM CID Info before msg disabled globally\n");
11953          val = "no";
11954       } 
11955       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11956 
11957       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
11958          ast_debug(1, "Send Voicemail msg disabled globally\n");
11959          val = "no";
11960       }
11961       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11962    
11963       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11964          ast_debug(1, "ENVELOPE before msg enabled globally\n");
11965          val = "yes";
11966       }
11967       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11968 
11969       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11970          ast_debug(1, "Move Heard enabled globally\n");
11971          val = "yes";
11972       }
11973       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11974 
11975       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11976          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11977          val = "no";
11978       }
11979       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11980 
11981       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11982          ast_debug(1, "Duration info before msg enabled globally\n");
11983          val = "yes";
11984       }
11985       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11986 
11987       saydurationminfo = 2;
11988       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11989          if (sscanf(val, "%30d", &x) == 1) {
11990             saydurationminfo = x;
11991          } else {
11992             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11993          }
11994       }
11995 
11996       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11997          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
11998          val = "no";
11999       }
12000       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12001 
12002       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12003          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12004          ast_debug(1, "found dialout context: %s\n", dialcontext);
12005       } else {
12006          dialcontext[0] = '\0';  
12007       }
12008       
12009       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12010          ast_copy_string(callcontext, val, sizeof(callcontext));
12011          ast_debug(1, "found callback context: %s\n", callcontext);
12012       } else {
12013          callcontext[0] = '\0';
12014       }
12015 
12016       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12017          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12018          ast_debug(1, "found operator context: %s\n", exitcontext);
12019       } else {
12020          exitcontext[0] = '\0';
12021       }
12022       
12023       /* load password sounds configuration */
12024       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12025          ast_copy_string(vm_password, val, sizeof(vm_password));
12026       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12027          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12028       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12029          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12030       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12031          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12032       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12033          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12034       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12035          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12036       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12037          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12038       }
12039       /* load configurable audio prompts */
12040       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12041          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12042       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12043          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12044       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12045          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12046       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12047          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12048       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12049          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12050 
12051       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12052          val = "no";
12053       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12054 
12055       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12056          val = "voicemail.conf";
12057       }
12058       if (!(strcmp(val, "spooldir"))) {
12059          passwordlocation = OPT_PWLOC_SPOOLDIR;
12060       } else {
12061          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12062       }
12063 
12064       poll_freq = DEFAULT_POLL_FREQ;
12065       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12066          if (sscanf(val, "%30u", &poll_freq) != 1) {
12067             poll_freq = DEFAULT_POLL_FREQ;
12068             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12069          }
12070       }
12071 
12072       poll_mailboxes = 0;
12073       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12074          poll_mailboxes = ast_true(val);
12075 
12076       if (ucfg) { 
12077          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12078             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12079                continue;
12080             if ((current = find_or_create(userscontext, cat))) {
12081                populate_defaults(current);
12082                apply_options_full(current, ast_variable_browse(ucfg, cat));
12083                ast_copy_string(current->context, userscontext, sizeof(current->context));
12084                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12085                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12086                }
12087 
12088                switch (current->passwordlocation) {
12089                case OPT_PWLOC_SPOOLDIR:
12090                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12091                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12092                }
12093             }
12094          }
12095          ast_config_destroy(ucfg);
12096       }
12097       cat = ast_category_browse(cfg, NULL);
12098       while (cat) {
12099          if (strcasecmp(cat, "general")) {
12100             var = ast_variable_browse(cfg, cat);
12101             if (strcasecmp(cat, "zonemessages")) {
12102                /* Process mailboxes in this context */
12103                while (var) {
12104                   append_mailbox(cat, var->name, var->value);
12105                   var = var->next;
12106                }
12107             } else {
12108                /* Timezones in this context */
12109                while (var) {
12110                   struct vm_zone *z;
12111                   if ((z = ast_malloc(sizeof(*z)))) {
12112                      char *msg_format, *tzone;
12113                      msg_format = ast_strdupa(var->value);
12114                      tzone = strsep(&msg_format, "|,");
12115                      if (msg_format) {
12116                         ast_copy_string(z->name, var->name, sizeof(z->name));
12117                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12118                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12119                         AST_LIST_LOCK(&zones);
12120                         AST_LIST_INSERT_HEAD(&zones, z, list);
12121                         AST_LIST_UNLOCK(&zones);
12122                      } else {
12123                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12124                         ast_free(z);
12125                      }
12126                   } else {
12127                      AST_LIST_UNLOCK(&users);
12128                      ast_config_destroy(cfg);
12129                      return -1;
12130                   }
12131                   var = var->next;
12132                }
12133             }
12134          }
12135          cat = ast_category_browse(cfg, cat);
12136       }
12137       memset(fromstring, 0, sizeof(fromstring));
12138       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12139       strcpy(charset, "ISO-8859-1");
12140       if (emailbody) {
12141          ast_free(emailbody);
12142          emailbody = NULL;
12143       }
12144       if (emailsubject) {
12145          ast_free(emailsubject);
12146          emailsubject = NULL;
12147       }
12148       if (pagerbody) {
12149          ast_free(pagerbody);
12150          pagerbody = NULL;
12151       }
12152       if (pagersubject) {
12153          ast_free(pagersubject);
12154          pagersubject = NULL;
12155       }
12156       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12157          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12158       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12159          ast_copy_string(fromstring, val, sizeof(fromstring));
12160       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12161          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12162       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12163          ast_copy_string(charset, val, sizeof(charset));
12164       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12165          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12166          for (x = 0; x < 4; x++) {
12167             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12168          }
12169       }
12170       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12171          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12172          for (x = 0; x < 4; x++) {
12173             memcpy(&adsisec[x], &tmpadsi[x], 1);
12174          }
12175       }
12176       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12177          if (atoi(val)) {
12178             adsiver = atoi(val);
12179          }
12180       }
12181       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12182          ast_copy_string(zonetag, val, sizeof(zonetag));
12183       }
12184       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12185          ast_copy_string(locale, val, sizeof(locale));
12186       }
12187       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12188          emailsubject = ast_strdup(val);
12189       }
12190       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12191          emailbody = ast_strdup(substitute_escapes(val));
12192       }
12193       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12194          pagersubject = ast_strdup(val);
12195       }
12196       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12197          pagerbody = ast_strdup(substitute_escapes(val));
12198       }
12199       AST_LIST_UNLOCK(&users);
12200       ast_config_destroy(cfg);
12201 
12202       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12203          start_poll_thread();
12204       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12205          stop_poll_thread();;
12206 
12207       return 0;
12208    } else {
12209       AST_LIST_UNLOCK(&users);
12210       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12211       if (ucfg)
12212          ast_config_destroy(ucfg);
12213       return 0;
12214    }
12215 }
12216 
12217 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12218 {
12219    int res = -1;
12220    char dir[PATH_MAX];
12221    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12222    ast_debug(2, "About to try retrieving name file %s\n", dir);
12223    RETRIEVE(dir, -1, mailbox, context);
12224    if (ast_fileexists(dir, NULL, NULL)) {
12225       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12226    }
12227    DISPOSE(dir, -1);
12228    return res;
12229 }
12230 
12231 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12232    struct ast_config *pwconf;
12233    struct ast_flags config_flags = { 0 };
12234 
12235    pwconf = ast_config_load(secretfn, config_flags);
12236    if (pwconf) {
12237       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12238       if (val) {
12239          ast_copy_string(password, val, passwordlen);
12240          return;
12241       }
12242    }
12243    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12244 }
12245 
12246 static int write_password_to_file(const char *secretfn, const char *password) {
12247    struct ast_config *conf;
12248    struct ast_category *cat;
12249    struct ast_variable *var;
12250 
12251    if (!(conf=ast_config_new())) {
12252       ast_log(LOG_ERROR, "Error creating new config structure\n");
12253       return -1;
12254    }
12255    if (!(cat=ast_category_new("general","",1))) {
12256       ast_log(LOG_ERROR, "Error creating new category structure\n");
12257       return -1;
12258    }
12259    if (!(var=ast_variable_new("password",password,""))) {
12260       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12261       return -1;
12262    }
12263    ast_category_append(conf,cat);
12264    ast_variable_append(cat,var);
12265    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12266       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12267       return -1;
12268    }
12269    return 0;
12270 }
12271 
12272 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12273 {
12274    char *context;
12275    char *args_copy;
12276    int res;
12277 
12278    if (ast_strlen_zero(data)) {
12279       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12280       return -1;
12281    }
12282 
12283    args_copy = ast_strdupa(data);
12284    if ((context = strchr(args_copy, '@'))) {
12285       *context++ = '\0';
12286    } else {
12287       context = "default";
12288    }
12289 
12290    if ((res = sayname(chan, args_copy, context) < 0)) {
12291       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12292       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12293       if (!res) {
12294          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12295       }
12296    }
12297 
12298    return res;
12299 }
12300 
12301 #ifdef TEST_FRAMEWORK
12302 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12303 {
12304    return 0;
12305 }
12306 
12307 static struct ast_frame *fake_read(struct ast_channel *ast)
12308 {
12309    return &ast_null_frame;
12310 }
12311 
12312 AST_TEST_DEFINE(test_voicemail_vmsayname)
12313 {
12314    char dir[PATH_MAX];
12315    char dir2[PATH_MAX];
12316    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12317    static const char TEST_EXTENSION[] = "1234";
12318 
12319    struct ast_channel *test_channel1 = NULL;
12320    int res = -1;
12321 
12322    static const struct ast_channel_tech fake_tech = {
12323       .write = fake_write,
12324       .read = fake_read,
12325    };
12326 
12327    switch (cmd) {
12328    case TEST_INIT:
12329       info->name = "vmsayname_exec";
12330       info->category = "/apps/app_voicemail/";
12331       info->summary = "Vmsayname unit test";
12332       info->description =
12333          "This tests passing various parameters to vmsayname";
12334       return AST_TEST_NOT_RUN;
12335    case TEST_EXECUTE:
12336       break;
12337    }
12338 
12339    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12340         NULL, NULL, 0, 0, "TestChannel1"))) {
12341       goto exit_vmsayname_test;
12342    }
12343 
12344    /* normally this is done in the channel driver */
12345    test_channel1->nativeformats = AST_FORMAT_GSM;
12346    test_channel1->writeformat = AST_FORMAT_GSM;
12347    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12348    test_channel1->readformat = AST_FORMAT_GSM;
12349    test_channel1->rawreadformat = AST_FORMAT_GSM;
12350    test_channel1->tech = &fake_tech;
12351 
12352    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12353    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12354    if (!(res = vmsayname_exec(test_channel1, dir))) {
12355       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12356       if (ast_fileexists(dir, NULL, NULL)) {
12357          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12358          res = -1;
12359          goto exit_vmsayname_test;
12360       } else {
12361          /* no greeting already exists as expected, let's create one to fully test sayname */
12362          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12363             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12364             goto exit_vmsayname_test;
12365          }
12366          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12367          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12368          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12369          if ((res = symlink(dir, dir2))) {
12370             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12371             goto exit_vmsayname_test;
12372          }
12373          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12374          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12375          res = vmsayname_exec(test_channel1, dir);
12376 
12377          /* TODO: there may be a better way to do this */
12378          unlink(dir2);
12379          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12380          rmdir(dir2);
12381          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12382          rmdir(dir2);
12383       }
12384    }
12385 
12386 exit_vmsayname_test:
12387 
12388    if (test_channel1) {
12389       ast_hangup(test_channel1);
12390    }
12391 
12392    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12393 }
12394 
12395 AST_TEST_DEFINE(test_voicemail_msgcount)
12396 {
12397    int i, j, res = AST_TEST_PASS, syserr;
12398    struct ast_vm_user *vmu;
12399    struct vm_state vms;
12400 #ifdef IMAP_STORAGE
12401    struct ast_channel *chan = NULL;
12402 #endif
12403    struct {
12404       char dir[256];
12405       char file[256];
12406       char txtfile[256];
12407    } tmp[3];
12408    char syscmd[256];
12409    const char origweasels[] = "tt-weasels";
12410    const char testcontext[] = "test";
12411    const char testmailbox[] = "00000000";
12412    const char testspec[] = "00000000@test";
12413    FILE *txt;
12414    int new, old, urgent;
12415    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12416    const int folder2mbox[3] = { 1, 11, 0 };
12417    const int expected_results[3][12] = {
12418       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12419       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12420       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12421       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12422    };
12423 
12424    switch (cmd) {
12425    case TEST_INIT:
12426       info->name = "test_voicemail_msgcount";
12427       info->category = "/apps/app_voicemail/";
12428       info->summary = "Test Voicemail status checks";
12429       info->description =
12430          "Verify that message counts are correct when retrieved through the public API";
12431       return AST_TEST_NOT_RUN;
12432    case TEST_EXECUTE:
12433       break;
12434    }
12435 
12436    /* Make sure the original path was completely empty */
12437    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12438    if ((syserr = ast_safe_system(syscmd))) {
12439       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12440          syserr > 0 ? strerror(syserr) : "unable to fork()");
12441       return AST_TEST_FAIL;
12442    }
12443 
12444 #ifdef IMAP_STORAGE
12445    if (!(chan = ast_dummy_channel_alloc())) {
12446       ast_test_status_update(test, "Unable to create dummy channel\n");
12447       return AST_TEST_FAIL;
12448    }
12449 #endif
12450 
12451    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12452       !(vmu = find_or_create(testcontext, testmailbox))) {
12453       ast_test_status_update(test, "Cannot create vmu structure\n");
12454       ast_unreplace_sigchld();
12455       return AST_TEST_FAIL;
12456    }
12457 
12458    populate_defaults(vmu);
12459    memset(&vms, 0, sizeof(vms));
12460 
12461    /* Create temporary voicemail */
12462    for (i = 0; i < 3; i++) {
12463       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12464       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12465       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12466 
12467       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12468          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12469             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12470          if ((syserr = ast_safe_system(syscmd))) {
12471             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12472                syserr > 0 ? strerror(syserr) : "unable to fork()");
12473             ast_unreplace_sigchld();
12474             return AST_TEST_FAIL;
12475          }
12476       }
12477 
12478       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12479          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12480          fclose(txt);
12481       } else {
12482          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12483          res = AST_TEST_FAIL;
12484          break;
12485       }
12486       open_mailbox(&vms, vmu, folder2mbox[i]);
12487       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12488 
12489       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12490       for (j = 0; j < 3; j++) {
12491          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12492          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12493             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12494                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12495             res = AST_TEST_FAIL;
12496          }
12497       }
12498 
12499       new = old = urgent = 0;
12500       if (ast_app_inboxcount(testspec, &new, &old)) {
12501          ast_test_status_update(test, "inboxcount returned failure\n");
12502          res = AST_TEST_FAIL;
12503       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12504          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12505             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12506          res = AST_TEST_FAIL;
12507       }
12508 
12509       new = old = urgent = 0;
12510       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12511          ast_test_status_update(test, "inboxcount2 returned failure\n");
12512          res = AST_TEST_FAIL;
12513       } else if (old != expected_results[i][6 + 0] ||
12514             urgent != expected_results[i][6 + 1] ||
12515                new != expected_results[i][6 + 2]    ) {
12516          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12517             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12518          res = AST_TEST_FAIL;
12519       }
12520 
12521       new = old = urgent = 0;
12522       for (j = 0; j < 3; j++) {
12523          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12524             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12525                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12526             res = AST_TEST_FAIL;
12527          }
12528       }
12529    }
12530 
12531    for (i = 0; i < 3; i++) {
12532       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12533        * server, in which case, the rm below will not affect the
12534        * voicemails. */
12535       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12536       DISPOSE(tmp[i].dir, 0);
12537    }
12538 
12539    if (vms.deleted) {
12540       ast_free(vms.deleted);
12541    }
12542    if (vms.heard) {
12543       ast_free(vms.heard);
12544    }
12545 
12546 #ifdef IMAP_STORAGE
12547    chan = ast_channel_release(chan);
12548 #endif
12549 
12550    /* And remove test directory */
12551    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12552    if ((syserr = ast_safe_system(syscmd))) {
12553       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12554          syserr > 0 ? strerror(syserr) : "unable to fork()");
12555    }
12556 
12557    return res;
12558 }
12559 
12560 AST_TEST_DEFINE(test_voicemail_notify_endl)
12561 {
12562    int res = AST_TEST_PASS;
12563    char testcontext[] = "test";
12564    char testmailbox[] = "00000000";
12565    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12566    char attach[256], attach2[256];
12567    char buf[256] = ""; /* No line should actually be longer than 80 */
12568    struct ast_channel *chan = NULL;
12569    struct ast_vm_user *vmu, vmus = {
12570       .flags = 0,
12571    };
12572    FILE *file;
12573    struct {
12574       char *name;
12575       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12576       void *location;
12577       union {
12578          int intval;
12579          char *strval;
12580       } u;
12581    } test_items[] = {
12582       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12583       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12584       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12585       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12586       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12587       { "attach2", STRPTR, attach2, .u.strval = "" },
12588       { "attach", STRPTR, attach, .u.strval = "" },
12589    };
12590    int which;
12591 
12592    switch (cmd) {
12593    case TEST_INIT:
12594       info->name = "test_voicemail_notify_endl";
12595       info->category = "/apps/app_voicemail/";
12596       info->summary = "Test Voicemail notification end-of-line";
12597       info->description =
12598          "Verify that notification emails use a consistent end-of-line character";
12599       return AST_TEST_NOT_RUN;
12600    case TEST_EXECUTE:
12601       break;
12602    }
12603 
12604    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12605    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12606 
12607    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12608       !(vmu = find_or_create(testcontext, testmailbox))) {
12609       ast_test_status_update(test, "Cannot create vmu structure\n");
12610       return AST_TEST_NOT_RUN;
12611    }
12612 
12613    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12614       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12615       return AST_TEST_NOT_RUN;
12616    }
12617 
12618    populate_defaults(vmu);
12619    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12620 #ifdef IMAP_STORAGE
12621    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12622 #endif
12623 
12624    file = tmpfile();
12625    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12626       /* Kill previous test, if any */
12627       rewind(file);
12628       if (ftruncate(fileno(file), 0)) {
12629          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12630          res = AST_TEST_FAIL;
12631          break;
12632       }
12633 
12634       /* Make each change, in order, to the test mailbox */
12635       if (test_items[which].type == INT) {
12636          *((int *) test_items[which].location) = test_items[which].u.intval;
12637       } else if (test_items[which].type == FLAGVAL) {
12638          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12639             ast_clear_flag(vmu, test_items[which].u.intval);
12640          } else {
12641             ast_set_flag(vmu, test_items[which].u.intval);
12642          }
12643       } else if (test_items[which].type == STATIC) {
12644          strcpy(test_items[which].location, test_items[which].u.strval);
12645       } else if (test_items[which].type == STRPTR) {
12646          test_items[which].location = test_items[which].u.strval;
12647       }
12648 
12649       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12650       rewind(file);
12651       while (fgets(buf, sizeof(buf), file)) {
12652          if (
12653 #ifdef IMAP_STORAGE
12654          buf[strlen(buf) - 2] != '\r'
12655 #else
12656          buf[strlen(buf) - 2] == '\r'
12657 #endif
12658          || buf[strlen(buf) - 1] != '\n') {
12659             res = AST_TEST_FAIL;
12660          }
12661       }
12662    }
12663    fclose(file);
12664    return res;
12665 }
12666 #endif /* defined(TEST_FRAMEWORK) */
12667 
12668 static int reload(void)
12669 {
12670    return load_config(1);
12671 }
12672 
12673 static int unload_module(void)
12674 {
12675    int res;
12676 
12677    res = ast_unregister_application(app);
12678    res |= ast_unregister_application(app2);
12679    res |= ast_unregister_application(app3);
12680    res |= ast_unregister_application(app4);
12681    res |= ast_unregister_application(sayname_app);
12682    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12683    res |= ast_manager_unregister("VoicemailUsersList");
12684    res |= ast_data_unregister(NULL);
12685 #ifdef TEST_FRAMEWORK
12686    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12687    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12688    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12689    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12690 #endif
12691    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12692    ast_uninstall_vm_functions();
12693    ao2_ref(inprocess_container, -1);
12694 
12695    if (poll_thread != AST_PTHREADT_NULL)
12696       stop_poll_thread();
12697 
12698    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12699    ast_unload_realtime("voicemail");
12700    ast_unload_realtime("voicemail_data");
12701 
12702    free_vm_users();
12703    free_vm_zones();
12704    return res;
12705 }
12706 
12707 static int load_module(void)
12708 {
12709    int res;
12710    my_umask = umask(0);
12711    umask(my_umask);
12712 
12713    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12714       return AST_MODULE_LOAD_DECLINE;
12715    }
12716 
12717    /* compute the location of the voicemail spool directory */
12718    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12719    
12720    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12721       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12722    }
12723 
12724    if ((res = load_config(0)))
12725       return res;
12726 
12727    res = ast_register_application_xml(app, vm_exec);
12728    res |= ast_register_application_xml(app2, vm_execmain);
12729    res |= ast_register_application_xml(app3, vm_box_exists);
12730    res |= ast_register_application_xml(app4, vmauthenticate);
12731    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12732    res |= ast_custom_function_register(&mailbox_exists_acf);
12733    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12734 #ifdef TEST_FRAMEWORK
12735    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12736    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12737    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12738    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12739 #endif
12740 
12741    if (res)
12742       return res;
12743 
12744    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12745    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12746 
12747    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12748    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12749    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12750 
12751    return res;
12752 }
12753 
12754 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
12755 {
12756    int cmd = 0;
12757    char destination[80] = "";
12758    int retries = 0;
12759 
12760    if (!num) {
12761       ast_verb(3, "Destination number will be entered manually\n");
12762       while (retries < 3 && cmd != 't') {
12763          destination[1] = '\0';
12764          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
12765          if (!cmd)
12766             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
12767          if (!cmd)
12768             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
12769          if (!cmd) {
12770             cmd = ast_waitfordigit(chan, 6000);
12771             if (cmd)
12772                destination[0] = cmd;
12773          }
12774          if (!cmd) {
12775             retries++;
12776          } else {
12777 
12778             if (cmd < 0)
12779                return 0;
12780             if (cmd == '*') {
12781                ast_verb(3, "User hit '*' to cancel outgoing call\n");
12782                return 0;
12783             }
12784             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
12785                retries++;
12786             else
12787                cmd = 't';
12788          }
12789       }
12790       if (retries >= 3) {
12791          return 0;
12792       }
12793       
12794    } else {
12795       if (option_verbose > 2)
12796          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
12797       ast_copy_string(destination, num, sizeof(destination));
12798    }
12799 
12800    if (!ast_strlen_zero(destination)) {
12801       if (destination[strlen(destination) -1 ] == '*')
12802          return 0; 
12803       if (option_verbose > 2)
12804          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
12805       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
12806       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
12807       chan->priority = 0;
12808       return 9;
12809    }
12810    return 0;
12811 }
12812 
12813 /*!
12814  * \brief The advanced options within a message.
12815  * \param chan
12816  * \param vmu 
12817  * \param vms
12818  * \param msg
12819  * \param option
12820  * \param record_gain
12821  *
12822  * Provides handling for the play message envelope, call the person back, or reply to message. 
12823  *
12824  * \return zero on success, -1 on error.
12825  */
12826 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)
12827 {
12828    int res = 0;
12829    char filename[PATH_MAX];
12830    struct ast_config *msg_cfg = NULL;
12831    const char *origtime, *context;
12832    char *name, *num;
12833    int retries = 0;
12834    char *cid;
12835    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
12836 
12837    vms->starting = 0; 
12838 
12839    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12840 
12841    /* Retrieve info from VM attribute file */
12842    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
12843    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
12844    msg_cfg = ast_config_load(filename, config_flags);
12845    DISPOSE(vms->curdir, vms->curmsg);
12846    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
12847       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
12848       return 0;
12849    }
12850 
12851    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
12852       ast_config_destroy(msg_cfg);
12853       return 0;
12854    }
12855 
12856    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
12857 
12858    context = ast_variable_retrieve(msg_cfg, "message", "context");
12859    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
12860       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
12861    switch (option) {
12862    case 3: /* Play message envelope */
12863       if (!res)
12864          res = play_message_datetime(chan, vmu, origtime, filename);
12865       if (!res)
12866          res = play_message_callerid(chan, vms, cid, context, 0);
12867 
12868       res = 't';
12869       break;
12870 
12871    case 2:  /* Call back */
12872 
12873       if (ast_strlen_zero(cid))
12874          break;
12875 
12876       ast_callerid_parse(cid, &name, &num);
12877       while ((res > -1) && (res != 't')) {
12878          switch (res) {
12879          case '1':
12880             if (num) {
12881                /* Dial the CID number */
12882                res = dialout(chan, vmu, num, vmu->callback);
12883                if (res) {
12884                   ast_config_destroy(msg_cfg);
12885                   return 9;
12886                }
12887             } else {
12888                res = '2';
12889             }
12890             break;
12891 
12892          case '2':
12893             /* Want to enter a different number, can only do this if there's a dialout context for this user */
12894             if (!ast_strlen_zero(vmu->dialout)) {
12895                res = dialout(chan, vmu, NULL, vmu->dialout);
12896                if (res) {
12897                   ast_config_destroy(msg_cfg);
12898                   return 9;
12899                }
12900             } else {
12901                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
12902                res = ast_play_and_wait(chan, "vm-sorry");
12903             }
12904             ast_config_destroy(msg_cfg);
12905             return res;
12906          case '*':
12907             res = 't';
12908             break;
12909          case '3':
12910          case '4':
12911          case '5':
12912          case '6':
12913          case '7':
12914          case '8':
12915          case '9':
12916          case '0':
12917 
12918             res = ast_play_and_wait(chan, "vm-sorry");
12919             retries++;
12920             break;
12921          default:
12922             if (num) {
12923                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
12924                res = ast_play_and_wait(chan, "vm-num-i-have");
12925                if (!res)
12926                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
12927                if (!res)
12928                   res = ast_play_and_wait(chan, "vm-tocallnum");
12929                /* Only prompt for a caller-specified number if there is a dialout context specified */
12930                if (!ast_strlen_zero(vmu->dialout)) {
12931                   if (!res)
12932                      res = ast_play_and_wait(chan, "vm-calldiffnum");
12933                }
12934             } else {
12935                res = ast_play_and_wait(chan, "vm-nonumber");
12936                if (!ast_strlen_zero(vmu->dialout)) {
12937                   if (!res)
12938                      res = ast_play_and_wait(chan, "vm-toenternumber");
12939                }
12940             }
12941             if (!res)
12942                res = ast_play_and_wait(chan, "vm-star-cancel");
12943             if (!res)
12944                res = ast_waitfordigit(chan, 6000);
12945             if (!res) {
12946                retries++;
12947                if (retries > 3)
12948                   res = 't';
12949             }
12950             break; 
12951             
12952          }
12953          if (res == 't')
12954             res = 0;
12955          else if (res == '*')
12956             res = -1;
12957       }
12958       break;
12959       
12960    case 1:  /* Reply */
12961       /* Send reply directly to sender */
12962       if (ast_strlen_zero(cid))
12963          break;
12964 
12965       ast_callerid_parse(cid, &name, &num);
12966       if (!num) {
12967          ast_verb(3, "No CID number available, no reply sent\n");
12968          if (!res)
12969             res = ast_play_and_wait(chan, "vm-nonumber");
12970          ast_config_destroy(msg_cfg);
12971          return res;
12972       } else {
12973          struct ast_vm_user vmu2;
12974          if (find_user(&vmu2, vmu->context, num)) {
12975             struct leave_vm_options leave_options;
12976             char mailbox[AST_MAX_EXTENSION * 2 + 2];
12977             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
12978 
12979             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
12980             
12981             memset(&leave_options, 0, sizeof(leave_options));
12982             leave_options.record_gain = record_gain;
12983             res = leave_voicemail(chan, mailbox, &leave_options);
12984             if (!res)
12985                res = 't';
12986             ast_config_destroy(msg_cfg);
12987             return res;
12988          } else {
12989             /* Sender has no mailbox, can't reply */
12990             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
12991             ast_play_and_wait(chan, "vm-nobox");
12992             res = 't';
12993             ast_config_destroy(msg_cfg);
12994             return res;
12995          }
12996       } 
12997       res = 0;
12998 
12999       break;
13000    }
13001 
13002 #ifndef IMAP_STORAGE
13003    ast_config_destroy(msg_cfg);
13004 
13005    if (!res) {
13006       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13007       vms->heard[msg] = 1;
13008       res = wait_file(chan, vms, vms->fn);
13009    }
13010 #endif
13011    return res;
13012 }
13013 
13014 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13015          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
13016          signed char record_gain, struct vm_state *vms, char *flag)
13017 {
13018    /* Record message & let caller review or re-record it, or set options if applicable */
13019    int res = 0;
13020    int cmd = 0;
13021    int max_attempts = 3;
13022    int attempts = 0;
13023    int recorded = 0;
13024    int msg_exists = 0;
13025    signed char zero_gain = 0;
13026    char tempfile[PATH_MAX];
13027    char *acceptdtmf = "#";
13028    char *canceldtmf = "";
13029    int canceleddtmf = 0;
13030 
13031    /* Note that urgent and private are for flagging messages as such in the future */
13032 
13033    /* barf if no pointer passed to store duration in */
13034    if (duration == NULL) {
13035       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13036       return -1;
13037    }
13038 
13039    if (!outsidecaller)
13040       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13041    else
13042       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13043 
13044    cmd = '3';  /* Want to start by recording */
13045 
13046    while ((cmd >= 0) && (cmd != 't')) {
13047       switch (cmd) {
13048       case '1':
13049          if (!msg_exists) {
13050             /* In this case, 1 is to record a message */
13051             cmd = '3';
13052             break;
13053          } else {
13054             /* Otherwise 1 is to save the existing message */
13055             ast_verb(3, "Saving message as is\n");
13056             if (!outsidecaller) 
13057                ast_filerename(tempfile, recordfile, NULL);
13058             ast_stream_and_wait(chan, "vm-msgsaved", "");
13059             if (!outsidecaller) {
13060                /* Saves to IMAP server only if imapgreeting=yes */
13061                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13062                DISPOSE(recordfile, -1);
13063             }
13064             cmd = 't';
13065             return res;
13066          }
13067       case '2':
13068          /* Review */
13069          ast_verb(3, "Reviewing the message\n");
13070          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13071          break;
13072       case '3':
13073          msg_exists = 0;
13074          /* Record */
13075          if (recorded == 1) 
13076             ast_verb(3, "Re-recording the message\n");
13077          else  
13078             ast_verb(3, "Recording the message\n");
13079          
13080          if (recorded && outsidecaller) {
13081             cmd = ast_play_and_wait(chan, INTRO);
13082             cmd = ast_play_and_wait(chan, "beep");
13083          }
13084          recorded = 1;
13085          /* 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 */
13086          if (record_gain)
13087             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13088          if (ast_test_flag(vmu, VM_OPERATOR))
13089             canceldtmf = "0";
13090          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13091          if (strchr(canceldtmf, cmd)) {
13092          /* need this flag here to distinguish between pressing '0' during message recording or after */
13093             canceleddtmf = 1;
13094          }
13095          if (record_gain)
13096             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13097          if (cmd == -1) {
13098             /* User has hung up, no options to give */
13099             if (!outsidecaller) {
13100                /* user was recording a greeting and they hung up, so let's delete the recording. */
13101                ast_filedelete(tempfile, NULL);
13102             }     
13103             return cmd;
13104          }
13105          if (cmd == '0') {
13106             break;
13107          } else if (cmd == '*') {
13108             break;
13109 #if 0
13110          } else if (vmu->review && (*duration < 5)) {
13111             /* Message is too short */
13112             ast_verb(3, "Message too short\n");
13113             cmd = ast_play_and_wait(chan, "vm-tooshort");
13114             cmd = ast_filedelete(tempfile, NULL);
13115             break;
13116          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13117             /* Message is all silence */
13118             ast_verb(3, "Nothing recorded\n");
13119             cmd = ast_filedelete(tempfile, NULL);
13120             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13121             if (!cmd)
13122                cmd = ast_play_and_wait(chan, "vm-speakup");
13123             break;
13124 #endif
13125          } else {
13126             /* If all is well, a message exists */
13127             msg_exists = 1;
13128             cmd = 0;
13129          }
13130          break;
13131       case '4':
13132          if (outsidecaller) {  /* only mark vm messages */
13133             /* Mark Urgent */
13134             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13135                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13136                res = ast_play_and_wait(chan, "vm-marked-urgent");
13137                strcpy(flag, "Urgent");
13138             } else if (flag) {
13139                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13140                res = ast_play_and_wait(chan, "vm-urgent-removed");
13141                strcpy(flag, "");
13142             } else {
13143                ast_play_and_wait(chan, "vm-sorry");
13144             }
13145             cmd = 0;
13146          } else {
13147             cmd = ast_play_and_wait(chan, "vm-sorry");
13148          }
13149          break;
13150       case '5':
13151       case '6':
13152       case '7':
13153       case '8':
13154       case '9':
13155       case '*':
13156       case '#':
13157          cmd = ast_play_and_wait(chan, "vm-sorry");
13158          break;
13159 #if 0 
13160 /*  XXX Commented out for the moment because of the dangers of deleting
13161     a message while recording (can put the message numbers out of sync) */
13162       case '*':
13163          /* Cancel recording, delete message, offer to take another message*/
13164          cmd = ast_play_and_wait(chan, "vm-deleted");
13165          cmd = ast_filedelete(tempfile, NULL);
13166          if (outsidecaller) {
13167             res = vm_exec(chan, NULL);
13168             return res;
13169          }
13170          else
13171             return 1;
13172 #endif
13173       case '0':
13174          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13175             cmd = ast_play_and_wait(chan, "vm-sorry");
13176             break;
13177          }
13178          if (msg_exists || recorded) {
13179             cmd = ast_play_and_wait(chan, "vm-saveoper");
13180             if (!cmd)
13181                cmd = ast_waitfordigit(chan, 3000);
13182             if (cmd == '1') {
13183                ast_filerename(tempfile, recordfile, NULL);
13184                ast_play_and_wait(chan, "vm-msgsaved");
13185                cmd = '0';
13186             } else if (cmd == '4') {
13187                if (flag) {
13188                   ast_play_and_wait(chan, "vm-marked-urgent");
13189                   strcpy(flag, "Urgent");
13190                }
13191                ast_play_and_wait(chan, "vm-msgsaved");
13192                cmd = '0';
13193             } else {
13194                ast_play_and_wait(chan, "vm-deleted");
13195                DELETE(tempfile, -1, tempfile, vmu);
13196                cmd = '0';
13197             }
13198          }
13199          return cmd;
13200       default:
13201          /* If the caller is an ouside caller, and the review option is enabled,
13202             allow them to review the message, but let the owner of the box review
13203             their OGM's */
13204          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13205             return cmd;
13206          if (msg_exists) {
13207             cmd = ast_play_and_wait(chan, "vm-review");
13208             if (!cmd && outsidecaller) {
13209                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13210                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13211                } else if (flag) {
13212                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13213                }
13214             }
13215          } else {
13216             cmd = ast_play_and_wait(chan, "vm-torerecord");
13217             if (!cmd)
13218                cmd = ast_waitfordigit(chan, 600);
13219          }
13220          
13221          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13222             cmd = ast_play_and_wait(chan, "vm-reachoper");
13223             if (!cmd)
13224                cmd = ast_waitfordigit(chan, 600);
13225          }
13226 #if 0
13227          if (!cmd)
13228             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13229 #endif
13230          if (!cmd)
13231             cmd = ast_waitfordigit(chan, 6000);
13232          if (!cmd) {
13233             attempts++;
13234          }
13235          if (attempts > max_attempts) {
13236             cmd = 't';
13237          }
13238       }
13239    }
13240    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13241       /* Hang up or timeout, so delete the recording. */
13242       ast_filedelete(tempfile, NULL);
13243    }
13244 
13245    if (cmd != 't' && outsidecaller)
13246       ast_play_and_wait(chan, "vm-goodbye");
13247 
13248    return cmd;
13249 }
13250 
13251 /* This is a workaround so that menuselect displays a proper description
13252  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13253  */
13254 
13255 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13256       .load = load_module,
13257       .unload = unload_module,
13258       .reload = reload,
13259       .nonoptreq = "res_adsi,res_smdi",
13260       );

Generated on Mon Jun 27 16:50:48 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7