Fri Apr 29 2011 07:53:56

Asterisk developer's documentation


app_voicemail.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043  ***/
00044 
00045 /*** MAKEOPTS
00046 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00047    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00048       <conflict>ODBC_STORAGE</conflict>
00049       <conflict>IMAP_STORAGE</conflict>
00050       <defaultenabled>yes</defaultenabled>
00051    </member>
00052    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00053       <depend>generic_odbc</depend>
00054       <depend>ltdl</depend>
00055       <conflict>IMAP_STORAGE</conflict>
00056       <conflict>FILE_STORAGE</conflict>
00057       <defaultenabled>no</defaultenabled>
00058    </member>
00059    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00060       <depend>imap_tk</depend>
00061       <conflict>ODBC_STORAGE</conflict>
00062       <conflict>FILE_STORAGE</conflict>
00063       <use>openssl</use>
00064       <defaultenabled>no</defaultenabled>
00065    </member>
00066 </category>
00067 ***/
00068 
00069 #include "asterisk.h"
00070 
00071 #ifdef IMAP_STORAGE
00072 #include <ctype.h>
00073 #include <signal.h>
00074 #include <pwd.h>
00075 #ifdef USE_SYSTEM_IMAP
00076 #include <imap/c-client.h>
00077 #include <imap/imap4r1.h>
00078 #include <imap/linkage.h>
00079 #elif defined (USE_SYSTEM_CCLIENT)
00080 #include <c-client/c-client.h>
00081 #include <c-client/imap4r1.h>
00082 #include <c-client/linkage.h>
00083 #else
00084 #include "c-client.h"
00085 #include "imap4r1.h"
00086 #include "linkage.h"
00087 #endif
00088 #endif
00089 
00090 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 301047 $")
00091 
00092 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00093 #include <sys/time.h>
00094 #include <sys/stat.h>
00095 #include <sys/mman.h>
00096 #include <time.h>
00097 #include <dirent.h>
00098 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00099 #include <sys/wait.h>
00100 #endif
00101 
00102 #include "asterisk/logger.h"
00103 #include "asterisk/lock.h"
00104 #include "asterisk/file.h"
00105 #include "asterisk/channel.h"
00106 #include "asterisk/pbx.h"
00107 #include "asterisk/config.h"
00108 #include "asterisk/say.h"
00109 #include "asterisk/module.h"
00110 #include "asterisk/adsi.h"
00111 #include "asterisk/app.h"
00112 #include "asterisk/manager.h"
00113 #include "asterisk/dsp.h"
00114 #include "asterisk/localtime.h"
00115 #include "asterisk/cli.h"
00116 #include "asterisk/utils.h"
00117 #include "asterisk/stringfields.h"
00118 #include "asterisk/smdi.h"
00119 #include "asterisk/astobj2.h"
00120 #include "asterisk/event.h"
00121 #include "asterisk/taskprocessor.h"
00122 #include "asterisk/test.h"
00123 
00124 #ifdef ODBC_STORAGE
00125 #include "asterisk/res_odbc.h"
00126 #endif
00127 
00128 #ifdef IMAP_STORAGE
00129 #include "asterisk/threadstorage.h"
00130 #endif
00131 
00132 /*** DOCUMENTATION
00133    <application name="VoiceMail" language="en_US">
00134       <synopsis>
00135          Leave a Voicemail message.
00136       </synopsis>
00137       <syntax>
00138          <parameter name="mailboxs" argsep="&amp;" required="true">
00139             <argument name="mailbox1" argsep="@" required="true">
00140                <argument name="mailbox" required="true" />
00141                <argument name="context" />
00142             </argument>
00143             <argument name="mailbox2" argsep="@" multiple="true">
00144                <argument name="mailbox" required="true" />
00145                <argument name="context" />
00146             </argument>
00147          </parameter>
00148          <parameter name="options">
00149             <optionlist>
00150                <option name="b">
00151                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00152                </option>
00153                <option name="d">
00154                   <argument name="c" />
00155                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00156                   if played during the greeting. Context defaults to the current context.</para>
00157                </option>
00158                <option name="g">
00159                   <argument name="#" required="true" />
00160                   <para>Use the specified amount of gain when recording the voicemail
00161                   message. The units are whole-number decibels (dB). Only works on supported
00162                   technologies, which is DAHDI only.</para>
00163                </option>
00164                <option name="s">
00165                   <para>Skip the playback of instructions for leaving a message to the
00166                   calling party.</para>
00167                </option>
00168                <option name="u">
00169                   <para>Play the <literal>unavailable</literal> greeting.</para>
00170                </option>
00171                <option name="U">
00172                   <para>Mark message as <literal>URGENT</literal>.</para>
00173                </option>
00174                <option name="P">
00175                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00176                </option>
00177             </optionlist>
00178          </parameter>
00179       </syntax>
00180       <description>
00181          <para>This application allows the calling party to leave a message for the specified
00182          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00183          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00184          exist.</para>
00185          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00186          <enumlist>
00187             <enum name="0">
00188                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00189             </enum>
00190             <enum name="*">
00191                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00192             </enum>
00193          </enumlist>
00194          <para>This application will set the following channel variable upon completion:</para>
00195          <variablelist>
00196             <variable name="VMSTATUS">
00197                <para>This indicates the status of the execution of the VoiceMail application.</para>
00198                <value name="SUCCESS" />
00199                <value name="USEREXIT" />
00200                <value name="FAILED" />
00201             </variable>
00202          </variablelist>
00203       </description>
00204    </application>
00205    <application name="VoiceMailMain" language="en_US">
00206       <synopsis>
00207          Check Voicemail messages.
00208       </synopsis>
00209       <syntax>
00210          <parameter name="mailbox" required="true" argsep="@">
00211             <argument name="mailbox" />
00212             <argument name="context" />
00213          </parameter>
00214          <parameter name="options">
00215             <optionlist>
00216                <option name="p">
00217                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00218                   the mailbox that is entered by the caller.</para>
00219                </option>
00220                <option name="g">
00221                   <argument name="#" required="true" />
00222                   <para>Use the specified amount of gain when recording a voicemail message.
00223                   The units are whole-number decibels (dB).</para>
00224                </option>
00225                <option name="s">
00226                   <para>Skip checking the passcode for the mailbox.</para>
00227                </option>
00228                <option name="a">
00229                   <argument name="folder" required="true" />
00230                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00231                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00232                   <enumlist>
00233                      <enum name="0"><para>INBOX</para></enum>
00234                      <enum name="1"><para>Old</para></enum>
00235                      <enum name="2"><para>Work</para></enum>
00236                      <enum name="3"><para>Family</para></enum>
00237                      <enum name="4"><para>Friends</para></enum>
00238                      <enum name="5"><para>Cust1</para></enum>
00239                      <enum name="6"><para>Cust2</para></enum>
00240                      <enum name="7"><para>Cust3</para></enum>
00241                      <enum name="8"><para>Cust4</para></enum>
00242                      <enum name="9"><para>Cust5</para></enum>
00243                   </enumlist>
00244                </option>
00245             </optionlist>
00246          </parameter>
00247       </syntax>
00248       <description>
00249          <para>This application allows the calling party to check voicemail messages. A specific
00250          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00251          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00252          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00253          <literal>default</literal> context will be used.</para>
00254          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00255          or Password, and the extension exists:</para>
00256          <enumlist>
00257             <enum name="*">
00258                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00259             </enum>
00260          </enumlist>
00261       </description>
00262    </application>
00263    <application name="MailboxExists" language="en_US">
00264       <synopsis>
00265          Check to see if Voicemail mailbox exists.
00266       </synopsis>
00267       <syntax>
00268          <parameter name="mailbox" required="true" argsep="@">
00269             <argument name="mailbox" required="true" />
00270             <argument name="context" />
00271          </parameter>
00272          <parameter name="options">
00273             <para>None options.</para>
00274          </parameter>
00275       </syntax>
00276       <description>
00277          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00278          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00279          will be used.</para>
00280          <para>This application will set the following channel variable upon completion:</para>
00281          <variablelist>
00282             <variable name="VMBOXEXISTSSTATUS">
00283                <para>This will contain the status of the execution of the MailboxExists application.
00284                Possible values include:</para>
00285                <value name="SUCCESS" />
00286                <value name="FAILED" />
00287             </variable>
00288          </variablelist>
00289       </description>
00290    </application>
00291    <application name="VMAuthenticate" language="en_US">
00292       <synopsis>
00293          Authenticate with Voicemail passwords.
00294       </synopsis>
00295       <syntax>
00296          <parameter name="mailbox" required="true" argsep="@">
00297             <argument name="mailbox" />
00298             <argument name="context" />
00299          </parameter>
00300          <parameter name="options">
00301             <optionlist>
00302                <option name="s">
00303                   <para>Skip playing the initial prompts.</para>
00304                </option>
00305             </optionlist>
00306          </parameter>
00307       </syntax>
00308       <description>
00309          <para>This application behaves the same way as the Authenticate application, but the passwords
00310          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00311          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00312          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00313          mailbox.</para>
00314          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00315          or Password, and the extension exists:</para>
00316          <enumlist>
00317             <enum name="*">
00318                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00319             </enum>
00320          </enumlist>
00321       </description>
00322    </application>
00323    <application name="VMSayName" language="en_US">
00324       <synopsis>
00325          Play the name of a voicemail user
00326       </synopsis>
00327       <syntax>
00328          <parameter name="mailbox" required="true" argsep="@">
00329             <argument name="mailbox" />
00330             <argument name="context" />
00331          </parameter>
00332       </syntax>
00333       <description>
00334          <para>This application will say the recorded name of the voicemail user specified as the
00335          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00336       </description>
00337    </application>
00338    <function name="MAILBOX_EXISTS" language="en_US">
00339       <synopsis>
00340          Tell if a mailbox is configured.
00341       </synopsis>
00342       <syntax argsep="@">
00343          <parameter name="mailbox" required="true" />
00344          <parameter name="context" />
00345       </syntax>
00346       <description>
00347          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00348          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00349          context.</para>
00350       </description>
00351    </function>
00352    <manager name="VoicemailUsersList" language="en_US">
00353       <synopsis>
00354          List All Voicemail User Information.
00355       </synopsis>
00356       <syntax>
00357          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00358       </syntax>
00359       <description>
00360       </description>
00361    </manager>
00362  ***/
00363 
00364 #ifdef IMAP_STORAGE
00365 static char imapserver[48];
00366 static char imapport[8];
00367 static char imapflags[128];
00368 static char imapfolder[64];
00369 static char imapparentfolder[64] = "\0";
00370 static char greetingfolder[64];
00371 static char authuser[32];
00372 static char authpassword[42];
00373 static int imapversion = 1;
00374 
00375 static int expungeonhangup = 1;
00376 static int imapgreetings = 0;
00377 static char delimiter = '\0';
00378 
00379 struct vm_state;
00380 struct ast_vm_user;
00381 
00382 AST_THREADSTORAGE(ts_vmstate);
00383 
00384 /* Forward declarations for IMAP */
00385 static int init_mailstream(struct vm_state *vms, int box);
00386 static void write_file(char *filename, char *buffer, unsigned long len);
00387 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00388 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00389 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00390 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00391 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00392 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00393 static void vmstate_insert(struct vm_state *vms);
00394 static void vmstate_delete(struct vm_state *vms);
00395 static void set_update(MAILSTREAM * stream);
00396 static void init_vm_state(struct vm_state *vms);
00397 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00398 static void get_mailbox_delimiter(MAILSTREAM *stream);
00399 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00400 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00401 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);
00402 static void update_messages_by_imapuser(const char *user, unsigned long number);
00403 static int vm_delete(char *file);
00404 
00405 static int imap_remove_file (char *dir, int msgnum);
00406 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00407 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00408 static void check_quota(struct vm_state *vms, char *mailbox);
00409 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00410 struct vmstate {
00411    struct vm_state *vms;
00412    AST_LIST_ENTRY(vmstate) list;
00413 };
00414 
00415 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00416 
00417 #endif
00418 
00419 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00420 
00421 #define COMMAND_TIMEOUT 5000
00422 /* Don't modify these here; set your umask at runtime instead */
00423 #define  VOICEMAIL_DIR_MODE   0777
00424 #define  VOICEMAIL_FILE_MODE  0666
00425 #define  CHUNKSIZE   65536
00426 
00427 #define VOICEMAIL_CONFIG "voicemail.conf"
00428 #define ASTERISK_USERNAME "asterisk"
00429 
00430 /* Define fast-forward, pause, restart, and reverse keys
00431    while listening to a voicemail message - these are
00432    strings, not characters */
00433 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00434 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00435 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00436 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00437 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00438 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00439 
00440 /* Default mail command to mail voicemail. Change it with the
00441     mailcmd= command in voicemail.conf */
00442 #define SENDMAIL "/usr/sbin/sendmail -t"
00443 
00444 #define INTRO "vm-intro"
00445 
00446 #define MAXMSG 100
00447 #define MAXMSGLIMIT 9999
00448 
00449 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00450 
00451 #define BASELINELEN 72
00452 #define BASEMAXINLINE 256
00453 #ifdef IMAP_STORAGE
00454 #define ENDL "\r\n"
00455 #else
00456 #define ENDL "\n"
00457 #endif
00458 
00459 #define MAX_DATETIME_FORMAT   512
00460 #define MAX_NUM_CID_CONTEXTS 10
00461 
00462 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00463 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00464 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00465 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00466 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00467 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00468 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00469 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00470 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00471 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00472 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00473 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00474 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00475 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00476 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00477 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00478 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00479 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00480 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00481 #define ERROR_LOCK_PATH  -100
00482 #define OPERATOR_EXIT     300
00483 
00484 
00485 enum vm_box {
00486    NEW_FOLDER,
00487    OLD_FOLDER,
00488    WORK_FOLDER,
00489    FAMILY_FOLDER,
00490    FRIENDS_FOLDER,
00491    GREETINGS_FOLDER
00492 };
00493 
00494 enum vm_option_flags {
00495    OPT_SILENT =           (1 << 0),
00496    OPT_BUSY_GREETING =    (1 << 1),
00497    OPT_UNAVAIL_GREETING = (1 << 2),
00498    OPT_RECORDGAIN =       (1 << 3),
00499    OPT_PREPEND_MAILBOX =  (1 << 4),
00500    OPT_AUTOPLAY =         (1 << 6),
00501    OPT_DTMFEXIT =         (1 << 7),
00502    OPT_MESSAGE_Urgent =   (1 << 8),
00503    OPT_MESSAGE_PRIORITY = (1 << 9)
00504 };
00505 
00506 enum vm_option_args {
00507    OPT_ARG_RECORDGAIN = 0,
00508    OPT_ARG_PLAYFOLDER = 1,
00509    OPT_ARG_DTMFEXIT   = 2,
00510    /* This *must* be the last value in this enum! */
00511    OPT_ARG_ARRAY_SIZE = 3,
00512 };
00513 
00514 enum vm_passwordlocation {
00515    OPT_PWLOC_VOICEMAILCONF = 0,
00516    OPT_PWLOC_SPOOLDIR      = 1,
00517    OPT_PWLOC_USERSCONF     = 2,
00518 };
00519 
00520 AST_APP_OPTIONS(vm_app_options, {
00521    AST_APP_OPTION('s', OPT_SILENT),
00522    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00523    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00524    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00525    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00526    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00527    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00528    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00529    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00530 });
00531 
00532 static int load_config(int reload);
00533 
00534 /*! \page vmlang Voicemail Language Syntaxes Supported
00535 
00536    \par Syntaxes supported, not really language codes.
00537    \arg \b en    - English
00538    \arg \b de    - German
00539    \arg \b es    - Spanish
00540    \arg \b fr    - French
00541    \arg \b it    - Italian
00542    \arg \b nl    - Dutch
00543    \arg \b pt    - Portuguese
00544    \arg \b pt_BR - Portuguese (Brazil)
00545    \arg \b gr    - Greek
00546    \arg \b no    - Norwegian
00547    \arg \b se    - Swedish
00548    \arg \b tw    - Chinese (Taiwan)
00549    \arg \b ua - Ukrainian
00550 
00551 German requires the following additional soundfile:
00552 \arg \b 1F  einE (feminine)
00553 
00554 Spanish requires the following additional soundfile:
00555 \arg \b 1M      un (masculine)
00556 
00557 Dutch, Portuguese & Spanish require the following additional soundfiles:
00558 \arg \b vm-INBOXs singular of 'new'
00559 \arg \b vm-Olds      singular of 'old/heard/read'
00560 
00561 NB these are plural:
00562 \arg \b vm-INBOX  nieuwe (nl)
00563 \arg \b vm-Old    oude (nl)
00564 
00565 Polish uses:
00566 \arg \b vm-new-a  'new', feminine singular accusative
00567 \arg \b vm-new-e  'new', feminine plural accusative
00568 \arg \b vm-new-ych   'new', feminine plural genitive
00569 \arg \b vm-old-a  'old', feminine singular accusative
00570 \arg \b vm-old-e  'old', feminine plural accusative
00571 \arg \b vm-old-ych   'old', feminine plural genitive
00572 \arg \b digits/1-a   'one', not always same as 'digits/1'
00573 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00574 
00575 Swedish uses:
00576 \arg \b vm-nytt      singular of 'new'
00577 \arg \b vm-nya    plural of 'new'
00578 \arg \b vm-gammalt   singular of 'old'
00579 \arg \b vm-gamla  plural of 'old'
00580 \arg \b digits/ett   'one', not always same as 'digits/1'
00581 
00582 Norwegian uses:
00583 \arg \b vm-ny     singular of 'new'
00584 \arg \b vm-nye    plural of 'new'
00585 \arg \b vm-gammel singular of 'old'
00586 \arg \b vm-gamle  plural of 'old'
00587 
00588 Dutch also uses:
00589 \arg \b nl-om     'at'?
00590 
00591 Spanish also uses:
00592 \arg \b vm-youhaveno
00593 
00594 Italian requires the following additional soundfile:
00595 
00596 For vm_intro_it:
00597 \arg \b vm-nuovo  new
00598 \arg \b vm-nuovi  new plural
00599 \arg \b vm-vecchio   old
00600 \arg \b vm-vecchi old plural
00601 
00602 Chinese (Taiwan) requires the following additional soundfile:
00603 \arg \b vm-tong      A class-word for call (tong1)
00604 \arg \b vm-ri     A class-word for day (ri4)
00605 \arg \b vm-you    You (ni3)
00606 \arg \b vm-haveno   Have no (mei2 you3)
00607 \arg \b vm-have     Have (you3)
00608 \arg \b vm-listen   To listen (yao4 ting1)
00609 
00610 
00611 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00612 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00613 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00614 
00615 */
00616 
00617 struct baseio {
00618    int iocp;
00619    int iolen;
00620    int linelength;
00621    int ateof;
00622    unsigned char iobuf[BASEMAXINLINE];
00623 };
00624 
00625 /*! Structure for linked list of users 
00626  * Use ast_vm_user_destroy() to free one of these structures. */
00627 struct ast_vm_user {
00628    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00629    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00630    char password[80];               /*!< Secret pin code, numbers only */
00631    char fullname[80];               /*!< Full name, for directory app */
00632    char email[80];                  /*!< E-mail address */
00633    char *emailsubject;              /*!< E-mail subject */
00634    char *emailbody;                 /*!< E-mail body */
00635    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00636    char serveremail[80];            /*!< From: Mail address */
00637    char mailcmd[160];               /*!< Configurable mail command */
00638    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00639    char zonetag[80];                /*!< Time zone */
00640    char locale[20];                 /*!< The locale (for presentation of date/time) */
00641    char callback[80];
00642    char dialout[80];
00643    char uniqueid[80];               /*!< Unique integer identifier */
00644    char exit[80];
00645    char attachfmt[20];              /*!< Attachment format */
00646    unsigned int flags;              /*!< VM_ flags */ 
00647    int saydurationm;
00648    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00649    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00650    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00651    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00652    int passwordlocation;            /*!< Storage location of the password */
00653 #ifdef IMAP_STORAGE
00654    char imapuser[80];               /*!< IMAP server login */
00655    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00656    char imapfolder[64];             /*!< IMAP voicemail folder */
00657    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00658    int imapversion;                 /*!< If configuration changes, use the new values */
00659 #endif
00660    double volgain;                  /*!< Volume gain for voicemails sent via email */
00661    AST_LIST_ENTRY(ast_vm_user) list;
00662 };
00663 
00664 /*! Voicemail time zones */
00665 struct vm_zone {
00666    AST_LIST_ENTRY(vm_zone) list;
00667    char name[80];
00668    char timezone[80];
00669    char msg_format[512];
00670 };
00671 
00672 #define VMSTATE_MAX_MSG_ARRAY 256
00673 
00674 /*! Voicemail mailbox state */
00675 struct vm_state {
00676    char curbox[80];
00677    char username[80];
00678    char context[80];
00679    char curdir[PATH_MAX];
00680    char vmbox[PATH_MAX];
00681    char fn[PATH_MAX];
00682    char intro[PATH_MAX];
00683    int *deleted;
00684    int *heard;
00685    int dh_arraysize; /* used for deleted / heard allocation */
00686    int curmsg;
00687    int lastmsg;
00688    int newmessages;
00689    int oldmessages;
00690    int urgentmessages;
00691    int starting;
00692    int repeats;
00693 #ifdef IMAP_STORAGE
00694    ast_mutex_t lock;
00695    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00696    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00697    MAILSTREAM *mailstream;
00698    int vmArrayIndex;
00699    char imapuser[80];                   /*!< IMAP server login */
00700    char imapfolder[64];                 /*!< IMAP voicemail folder */
00701    int imapversion;
00702    int interactive;
00703    char introfn[PATH_MAX];              /*!< Name of prepended file */
00704    unsigned int quota_limit;
00705    unsigned int quota_usage;
00706    struct vm_state *persist_vms;
00707 #endif
00708 };
00709 
00710 #ifdef ODBC_STORAGE
00711 static char odbc_database[80];
00712 static char odbc_table[80];
00713 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00714 #define DISPOSE(a,b) remove_file(a,b)
00715 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00716 #define EXISTS(a,b,c,d) (message_exists(a,b))
00717 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00718 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00719 #define DELETE(a,b,c,d) (delete_file(a,b))
00720 #else
00721 #ifdef IMAP_STORAGE
00722 #define DISPOSE(a,b) (imap_remove_file(a,b))
00723 #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))
00724 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00725 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00726 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00727 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00728 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00729 #else
00730 #define RETRIEVE(a,b,c,d)
00731 #define DISPOSE(a,b)
00732 #define STORE(a,b,c,d,e,f,g,h,i,j)
00733 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00734 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00735 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00736 #define DELETE(a,b,c,d) (vm_delete(c))
00737 #endif
00738 #endif
00739 
00740 static char VM_SPOOL_DIR[PATH_MAX];
00741 
00742 static char ext_pass_cmd[128];
00743 static char ext_pass_check_cmd[128];
00744 
00745 static int my_umask;
00746 
00747 #define PWDCHANGE_INTERNAL (1 << 1)
00748 #define PWDCHANGE_EXTERNAL (1 << 2)
00749 static int pwdchange = PWDCHANGE_INTERNAL;
00750 
00751 #ifdef ODBC_STORAGE
00752 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00753 #else
00754 # ifdef IMAP_STORAGE
00755 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00756 # else
00757 # define tdesc "Comedian Mail (Voicemail System)"
00758 # endif
00759 #endif
00760 
00761 static char userscontext[AST_MAX_EXTENSION] = "default";
00762 
00763 static char *addesc = "Comedian Mail";
00764 
00765 /* Leave a message */
00766 static char *app = "VoiceMail";
00767 
00768 /* Check mail, control, etc */
00769 static char *app2 = "VoiceMailMain";
00770 
00771 static char *app3 = "MailboxExists";
00772 static char *app4 = "VMAuthenticate";
00773 
00774 static char *sayname_app = "VMSayName";
00775 
00776 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00777 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00778 static char zonetag[80];
00779 static char locale[20];
00780 static int maxsilence;
00781 static int maxmsg;
00782 static int maxdeletedmsg;
00783 static int silencethreshold = 128;
00784 static char serveremail[80];
00785 static char mailcmd[160];  /* Configurable mail cmd */
00786 static char externnotify[160]; 
00787 static struct ast_smdi_interface *smdi_iface = NULL;
00788 static char vmfmts[80];
00789 static double volgain;
00790 static int vmminsecs;
00791 static int vmmaxsecs;
00792 static int maxgreet;
00793 static int skipms;
00794 static int maxlogins;
00795 static int minpassword;
00796 static int passwordlocation;
00797 
00798 /*! Poll mailboxes for changes since there is something external to
00799  *  app_voicemail that may change them. */
00800 static unsigned int poll_mailboxes;
00801 
00802 /*! Polling frequency */
00803 static unsigned int poll_freq;
00804 /*! By default, poll every 30 seconds */
00805 #define DEFAULT_POLL_FREQ 30
00806 
00807 AST_MUTEX_DEFINE_STATIC(poll_lock);
00808 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00809 static pthread_t poll_thread = AST_PTHREADT_NULL;
00810 static unsigned char poll_thread_run;
00811 
00812 /*! Subscription to ... MWI event subscriptions */
00813 static struct ast_event_sub *mwi_sub_sub;
00814 /*! Subscription to ... MWI event un-subscriptions */
00815 static struct ast_event_sub *mwi_unsub_sub;
00816 
00817 /*!
00818  * \brief An MWI subscription
00819  *
00820  * This is so we can keep track of which mailboxes are subscribed to.
00821  * This way, we know which mailboxes to poll when the pollmailboxes
00822  * option is being used.
00823  */
00824 struct mwi_sub {
00825    AST_RWLIST_ENTRY(mwi_sub) entry;
00826    int old_urgent;
00827    int old_new;
00828    int old_old;
00829    uint32_t uniqueid;
00830    char mailbox[1];
00831 };
00832 
00833 struct mwi_sub_task {
00834    const char *mailbox;
00835    const char *context;
00836    uint32_t uniqueid;
00837 };
00838 
00839 static struct ast_taskprocessor *mwi_subscription_tps;
00840 
00841 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00842 
00843 /* custom audio control prompts for voicemail playback */
00844 static char listen_control_forward_key[12];
00845 static char listen_control_reverse_key[12];
00846 static char listen_control_pause_key[12];
00847 static char listen_control_restart_key[12];
00848 static char listen_control_stop_key[12];
00849 
00850 /* custom password sounds */
00851 static char vm_password[80] = "vm-password";
00852 static char vm_newpassword[80] = "vm-newpassword";
00853 static char vm_passchanged[80] = "vm-passchanged";
00854 static char vm_reenterpassword[80] = "vm-reenterpassword";
00855 static char vm_mismatch[80] = "vm-mismatch";
00856 static char vm_invalid_password[80] = "vm-invalid-password";
00857 static char vm_pls_try_again[80] = "vm-pls-try-again";
00858 
00859 static struct ast_flags globalflags = {0};
00860 
00861 static int saydurationminfo;
00862 
00863 static char dialcontext[AST_MAX_CONTEXT] = "";
00864 static char callcontext[AST_MAX_CONTEXT] = "";
00865 static char exitcontext[AST_MAX_CONTEXT] = "";
00866 
00867 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00868 
00869 
00870 static char *emailbody = NULL;
00871 static char *emailsubject = NULL;
00872 static char *pagerbody = NULL;
00873 static char *pagersubject = NULL;
00874 static char fromstring[100];
00875 static char pagerfromstring[100];
00876 static char charset[32] = "ISO-8859-1";
00877 
00878 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00879 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00880 static int adsiver = 1;
00881 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00882 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00883 
00884 /* Forward declarations - generic */
00885 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00886 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);
00887 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00888 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00889          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00890          signed char record_gain, struct vm_state *vms, char *flag);
00891 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00892 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00893 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);
00894 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);
00895 static void apply_options(struct ast_vm_user *vmu, const char *options);
00896 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);
00897 static int is_valid_dtmf(const char *key);
00898 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00899 static int write_password_to_file(const char *secretfn, const char *password);
00900 
00901 struct ao2_container *inprocess_container;
00902 
00903 struct inprocess {
00904    int count;
00905    char *context;
00906    char mailbox[0];
00907 };
00908 
00909 static int inprocess_hash_fn(const void *obj, const int flags)
00910 {
00911    const struct inprocess *i = obj;
00912    return atoi(i->mailbox);
00913 }
00914 
00915 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00916 {
00917    struct inprocess *i = obj, *j = arg;
00918    if (strcmp(i->mailbox, j->mailbox)) {
00919       return 0;
00920    }
00921    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00922 }
00923 
00924 static int inprocess_count(const char *context, const char *mailbox, int delta)
00925 {
00926    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00927    arg->context = arg->mailbox + strlen(mailbox) + 1;
00928    strcpy(arg->mailbox, mailbox); /* SAFE */
00929    strcpy(arg->context, context); /* SAFE */
00930    ao2_lock(inprocess_container);
00931    if ((i = ao2_find(inprocess_container, arg, 0))) {
00932       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00933       ao2_unlock(inprocess_container);
00934       ao2_ref(i, -1);
00935       return ret;
00936    }
00937    if (delta < 0) {
00938       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00939    }
00940    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00941       ao2_unlock(inprocess_container);
00942       return 0;
00943    }
00944    i->context = i->mailbox + strlen(mailbox) + 1;
00945    strcpy(i->mailbox, mailbox); /* SAFE */
00946    strcpy(i->context, context); /* SAFE */
00947    i->count = delta;
00948    ao2_link(inprocess_container, i);
00949    ao2_unlock(inprocess_container);
00950    ao2_ref(i, -1);
00951    return 0;
00952 }
00953 
00954 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00955 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00956 #endif
00957 
00958 /*!
00959  * \brief Strips control and non 7-bit clean characters from input string.
00960  *
00961  * \note To map control and none 7-bit characters to a 7-bit clean characters
00962  *  please use ast_str_encode_mine().
00963  */
00964 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00965 {
00966    char *bufptr = buf;
00967    for (; *input; input++) {
00968       if (*input < 32) {
00969          continue;
00970       }
00971       *bufptr++ = *input;
00972       if (bufptr == buf + buflen - 1) {
00973          break;
00974       }
00975    }
00976    *bufptr = '\0';
00977    return buf;
00978 }
00979 
00980 
00981 /*!
00982  * \brief Sets default voicemail system options to a voicemail user.
00983  *
00984  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00985  * - all the globalflags
00986  * - the saydurationminfo
00987  * - the callcontext
00988  * - the dialcontext
00989  * - the exitcontext
00990  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00991  * - volume gain.
00992  */
00993 static void populate_defaults(struct ast_vm_user *vmu)
00994 {
00995    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00996    vmu->passwordlocation = passwordlocation;
00997    if (saydurationminfo) {
00998       vmu->saydurationm = saydurationminfo;
00999    }
01000    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01001    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01002    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01003    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01004    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01005    if (vmminsecs) {
01006       vmu->minsecs = vmminsecs;
01007    }
01008    if (vmmaxsecs) {
01009       vmu->maxsecs = vmmaxsecs;
01010    }
01011    if (maxmsg) {
01012       vmu->maxmsg = maxmsg;
01013    }
01014    if (maxdeletedmsg) {
01015       vmu->maxdeletedmsg = maxdeletedmsg;
01016    }
01017    vmu->volgain = volgain;
01018    vmu->emailsubject = NULL;
01019    vmu->emailbody = NULL;
01020 #ifdef IMAP_STORAGE
01021    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01022 #endif
01023 }
01024 
01025 /*!
01026  * \brief Sets a a specific property value.
01027  * \param vmu The voicemail user object to work with.
01028  * \param var The name of the property to be set.
01029  * \param value The value to be set to the property.
01030  * 
01031  * The property name must be one of the understood properties. See the source for details.
01032  */
01033 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01034 {
01035    int x;
01036    if (!strcasecmp(var, "attach")) {
01037       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01038    } else if (!strcasecmp(var, "attachfmt")) {
01039       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01040    } else if (!strcasecmp(var, "serveremail")) {
01041       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01042    } else if (!strcasecmp(var, "language")) {
01043       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01044    } else if (!strcasecmp(var, "tz")) {
01045       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01046    } else if (!strcasecmp(var, "locale")) {
01047       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01048 #ifdef IMAP_STORAGE
01049    } else if (!strcasecmp(var, "imapuser")) {
01050       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01051       vmu->imapversion = imapversion;
01052    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01053       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01054       vmu->imapversion = imapversion;
01055    } else if (!strcasecmp(var, "imapfolder")) {
01056       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01057    } else if (!strcasecmp(var, "imapvmshareid")) {
01058       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01059       vmu->imapversion = imapversion;
01060 #endif
01061    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01062       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01063    } else if (!strcasecmp(var, "saycid")){
01064       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01065    } else if (!strcasecmp(var, "sendvoicemail")){
01066       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01067    } else if (!strcasecmp(var, "review")){
01068       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01069    } else if (!strcasecmp(var, "tempgreetwarn")){
01070       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01071    } else if (!strcasecmp(var, "messagewrap")){
01072       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01073    } else if (!strcasecmp(var, "operator")) {
01074       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01075    } else if (!strcasecmp(var, "envelope")){
01076       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01077    } else if (!strcasecmp(var, "moveheard")){
01078       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01079    } else if (!strcasecmp(var, "sayduration")){
01080       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01081    } else if (!strcasecmp(var, "saydurationm")){
01082       if (sscanf(value, "%30d", &x) == 1) {
01083          vmu->saydurationm = x;
01084       } else {
01085          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01086       }
01087    } else if (!strcasecmp(var, "forcename")){
01088       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01089    } else if (!strcasecmp(var, "forcegreetings")){
01090       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01091    } else if (!strcasecmp(var, "callback")) {
01092       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01093    } else if (!strcasecmp(var, "dialout")) {
01094       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01095    } else if (!strcasecmp(var, "exitcontext")) {
01096       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01097    } else if (!strcasecmp(var, "minsecs")) {
01098       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01099          vmu->minsecs = x;
01100       } else {
01101          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01102          vmu->minsecs = vmminsecs;
01103       }
01104    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01105       vmu->maxsecs = atoi(value);
01106       if (vmu->maxsecs <= 0) {
01107          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01108          vmu->maxsecs = vmmaxsecs;
01109       } else {
01110          vmu->maxsecs = atoi(value);
01111       }
01112       if (!strcasecmp(var, "maxmessage"))
01113          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01114    } else if (!strcasecmp(var, "maxmsg")) {
01115       vmu->maxmsg = atoi(value);
01116       /* Accept maxmsg=0 (Greetings only voicemail) */
01117       if (vmu->maxmsg < 0) {
01118          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01119          vmu->maxmsg = MAXMSG;
01120       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01121          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01122          vmu->maxmsg = MAXMSGLIMIT;
01123       }
01124    } else if (!strcasecmp(var, "nextaftercmd")) {
01125       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01126    } else if (!strcasecmp(var, "backupdeleted")) {
01127       if (sscanf(value, "%30d", &x) == 1)
01128          vmu->maxdeletedmsg = x;
01129       else if (ast_true(value))
01130          vmu->maxdeletedmsg = MAXMSG;
01131       else
01132          vmu->maxdeletedmsg = 0;
01133 
01134       if (vmu->maxdeletedmsg < 0) {
01135          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01136          vmu->maxdeletedmsg = MAXMSG;
01137       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01138          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01139          vmu->maxdeletedmsg = MAXMSGLIMIT;
01140       }
01141    } else if (!strcasecmp(var, "volgain")) {
01142       sscanf(value, "%30lf", &vmu->volgain);
01143    } else if (!strcasecmp(var, "passwordlocation")) {
01144       if (!strcasecmp(value, "spooldir")) {
01145          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01146       } else {
01147          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01148       }
01149    } else if (!strcasecmp(var, "options")) {
01150       apply_options(vmu, value);
01151    }
01152 }
01153 
01154 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01155 {
01156    int fds[2], pid = 0;
01157 
01158    memset(buf, 0, len);
01159 
01160    if (pipe(fds)) {
01161       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01162    } else {
01163       /* good to go*/
01164       pid = ast_safe_fork(0);
01165 
01166       if (pid < 0) {
01167          /* ok maybe not */
01168          close(fds[0]);
01169          close(fds[1]);
01170          snprintf(buf, len, "FAILURE: Fork failed");
01171       } else if (pid) {
01172          /* parent */
01173          close(fds[1]);
01174          if (read(fds[0], buf, len) < 0) {
01175             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01176          }
01177          close(fds[0]);
01178       } else {
01179          /*  child */
01180          AST_DECLARE_APP_ARGS(arg,
01181             AST_APP_ARG(v)[20];
01182          );
01183          char *mycmd = ast_strdupa(command);
01184 
01185          close(fds[0]);
01186          dup2(fds[1], STDOUT_FILENO);
01187          close(fds[1]);
01188          ast_close_fds_above_n(STDOUT_FILENO);
01189 
01190          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01191 
01192          execv(arg.v[0], arg.v); 
01193          printf("FAILURE: %s", strerror(errno));
01194          _exit(0);
01195       }
01196    }
01197    return buf;
01198 }
01199 
01200 /*!
01201  * \brief Check that password meets minimum required length
01202  * \param vmu The voicemail user to change the password for.
01203  * \param password The password string to check
01204  *
01205  * \return zero on ok, 1 on not ok.
01206  */
01207 static int check_password(struct ast_vm_user *vmu, char *password)
01208 {
01209    /* check minimum length */
01210    if (strlen(password) < minpassword)
01211       return 1;
01212    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01213       char cmd[255], buf[255];
01214 
01215       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01216 
01217       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01218       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01219          ast_debug(5, "Result: %s\n", buf);
01220          if (!strncasecmp(buf, "VALID", 5)) {
01221             ast_debug(3, "Passed password check: '%s'\n", buf);
01222             return 0;
01223          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01224             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01225             return 0;
01226          } else {
01227             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01228             return 1;
01229          }
01230       }
01231    }
01232    return 0;
01233 }
01234 
01235 /*! 
01236  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01237  * \param vmu The voicemail user to change the password for.
01238  * \param password The new value to be set to the password for this user.
01239  * 
01240  * This only works if there is a realtime engine configured.
01241  * This is called from the (top level) vm_change_password.
01242  *
01243  * \return zero on success, -1 on error.
01244  */
01245 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01246 {
01247    int res = -1;
01248    if (!strcmp(vmu->password, password)) {
01249       /* No change (but an update would return 0 rows updated, so we opt out here) */
01250       return 0;
01251    }
01252 
01253    if (strlen(password) > 10) {
01254       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01255    }
01256    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01257       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01258       res = 0;
01259    }
01260    return res;
01261 }
01262 
01263 /*!
01264  * \brief Destructively Parse options and apply.
01265  */
01266 static void apply_options(struct ast_vm_user *vmu, const char *options)
01267 {  
01268    char *stringp;
01269    char *s;
01270    char *var, *value;
01271    stringp = ast_strdupa(options);
01272    while ((s = strsep(&stringp, "|"))) {
01273       value = s;
01274       if ((var = strsep(&value, "=")) && value) {
01275          apply_option(vmu, var, value);
01276       }
01277    }  
01278 }
01279 
01280 /*!
01281  * \brief Loads the options specific to a voicemail user.
01282  * 
01283  * This is called when a vm_user structure is being set up, such as from load_options.
01284  */
01285 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01286 {
01287    for (; var; var = var->next) {
01288       if (!strcasecmp(var->name, "vmsecret")) {
01289          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01290       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01291          if (ast_strlen_zero(retval->password))
01292             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01293       } else if (!strcasecmp(var->name, "uniqueid")) {
01294          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01295       } else if (!strcasecmp(var->name, "pager")) {
01296          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01297       } else if (!strcasecmp(var->name, "email")) {
01298          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01299       } else if (!strcasecmp(var->name, "fullname")) {
01300          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01301       } else if (!strcasecmp(var->name, "context")) {
01302          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01303       } else if (!strcasecmp(var->name, "emailsubject")) {
01304          retval->emailsubject = ast_strdup(var->value);
01305       } else if (!strcasecmp(var->name, "emailbody")) {
01306          retval->emailbody = ast_strdup(var->value);
01307 #ifdef IMAP_STORAGE
01308       } else if (!strcasecmp(var->name, "imapuser")) {
01309          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01310          retval->imapversion = imapversion;
01311       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01312          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01313          retval->imapversion = imapversion;
01314       } else if (!strcasecmp(var->name, "imapfolder")) {
01315          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01316       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01317          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01318          retval->imapversion = imapversion;
01319 #endif
01320       } else
01321          apply_option(retval, var->name, var->value);
01322    }
01323 }
01324 
01325 /*!
01326  * \brief Determines if a DTMF key entered is valid.
01327  * \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.
01328  *
01329  * Tests the character entered against the set of valid DTMF characters. 
01330  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01331  */
01332 static int is_valid_dtmf(const char *key)
01333 {
01334    int i;
01335    char *local_key = ast_strdupa(key);
01336 
01337    for (i = 0; i < strlen(key); ++i) {
01338       if (!strchr(VALID_DTMF, *local_key)) {
01339          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01340          return 0;
01341       }
01342       local_key++;
01343    }
01344    return 1;
01345 }
01346 
01347 /*!
01348  * \brief Finds a voicemail user from the realtime engine.
01349  * \param ivm
01350  * \param context
01351  * \param mailbox
01352  *
01353  * 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.
01354  *
01355  * \return The ast_vm_user structure for the user that was found.
01356  */
01357 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01358 {
01359    struct ast_variable *var;
01360    struct ast_vm_user *retval;
01361 
01362    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01363       if (!ivm)
01364          ast_set_flag(retval, VM_ALLOCED);   
01365       else
01366          memset(retval, 0, sizeof(*retval));
01367       if (mailbox) 
01368          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01369       populate_defaults(retval);
01370       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01371          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01372       else
01373          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01374       if (var) {
01375          apply_options_full(retval, var);
01376          ast_variables_destroy(var);
01377       } else { 
01378          if (!ivm) 
01379             ast_free(retval);
01380          retval = NULL;
01381       }  
01382    } 
01383    return retval;
01384 }
01385 
01386 /*!
01387  * \brief Finds a voicemail user from the users file or the realtime engine.
01388  * \param ivm
01389  * \param context
01390  * \param mailbox
01391  * 
01392  * \return The ast_vm_user structure for the user that was found.
01393  */
01394 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01395 {
01396    /* This function could be made to generate one from a database, too */
01397    struct ast_vm_user *vmu = NULL, *cur;
01398    AST_LIST_LOCK(&users);
01399 
01400    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01401       context = "default";
01402 
01403    AST_LIST_TRAVERSE(&users, cur, list) {
01404 #ifdef IMAP_STORAGE
01405       if (cur->imapversion != imapversion) {
01406          continue;
01407       }
01408 #endif
01409       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01410          break;
01411       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01412          break;
01413    }
01414    if (cur) {
01415       /* Make a copy, so that on a reload, we have no race */
01416       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01417          memcpy(vmu, cur, sizeof(*vmu));
01418          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01419          AST_LIST_NEXT(vmu, list) = NULL;
01420       }
01421    } else
01422       vmu = find_user_realtime(ivm, context, mailbox);
01423    AST_LIST_UNLOCK(&users);
01424    return vmu;
01425 }
01426 
01427 /*!
01428  * \brief Resets a user password to a specified password.
01429  * \param context
01430  * \param mailbox
01431  * \param newpass
01432  *
01433  * This does the actual change password work, called by the vm_change_password() function.
01434  *
01435  * \return zero on success, -1 on error.
01436  */
01437 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01438 {
01439    /* This function could be made to generate one from a database, too */
01440    struct ast_vm_user *cur;
01441    int res = -1;
01442    AST_LIST_LOCK(&users);
01443    AST_LIST_TRAVERSE(&users, cur, list) {
01444       if ((!context || !strcasecmp(context, cur->context)) &&
01445          (!strcasecmp(mailbox, cur->mailbox)))
01446             break;
01447    }
01448    if (cur) {
01449       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01450       res = 0;
01451    }
01452    AST_LIST_UNLOCK(&users);
01453    return res;
01454 }
01455 
01456 /*! 
01457  * \brief The handler for the change password option.
01458  * \param vmu The voicemail user to work with.
01459  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01460  * 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.
01461  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01462  */
01463 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01464 {
01465    struct ast_config   *cfg = NULL;
01466    struct ast_variable *var = NULL;
01467    struct ast_category *cat = NULL;
01468    char *category = NULL, *value = NULL, *new = NULL;
01469    const char *tmp = NULL;
01470    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01471    char secretfn[PATH_MAX] = "";
01472    int found = 0;
01473 
01474    if (!change_password_realtime(vmu, newpassword))
01475       return;
01476 
01477    /* check if we should store the secret in the spool directory next to the messages */
01478    switch (vmu->passwordlocation) {
01479    case OPT_PWLOC_SPOOLDIR:
01480       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01481       if (write_password_to_file(secretfn, newpassword) == 0) {
01482          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01483          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01484          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01485          break;
01486       } else {
01487          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01488       }
01489       /* Fall-through */
01490    case OPT_PWLOC_VOICEMAILCONF:
01491       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01492          while ((category = ast_category_browse(cfg, category))) {
01493             if (!strcasecmp(category, vmu->context)) {
01494                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01495                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01496                   break;
01497                }
01498                value = strstr(tmp, ",");
01499                if (!value) {
01500                   new = alloca(strlen(newpassword)+1);
01501                   sprintf(new, "%s", newpassword);
01502                } else {
01503                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01504                   sprintf(new, "%s%s", newpassword, value);
01505                }
01506                if (!(cat = ast_category_get(cfg, category))) {
01507                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01508                   break;
01509                }
01510                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01511                found = 1;
01512             }
01513          }
01514          /* save the results */
01515          if (found) {
01516             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01517             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01518             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01519             break;
01520          }
01521       }
01522       /* Fall-through */
01523    case OPT_PWLOC_USERSCONF:
01524       /* check users.conf and update the password stored for the mailbox */
01525       /* if no vmsecret entry exists create one. */
01526       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01527          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01528          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01529             ast_debug(4, "users.conf: %s\n", category);
01530             if (!strcasecmp(category, vmu->mailbox)) {
01531                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01532                   ast_debug(3, "looks like we need to make vmsecret!\n");
01533                   var = ast_variable_new("vmsecret", newpassword, "");
01534                } else {
01535                   var = NULL;
01536                }
01537                new = alloca(strlen(newpassword) + 1);
01538                sprintf(new, "%s", newpassword);
01539                if (!(cat = ast_category_get(cfg, category))) {
01540                   ast_debug(4, "failed to get category!\n");
01541                   ast_free(var);
01542                   break;
01543                }
01544                if (!var) {
01545                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01546                } else {
01547                   ast_variable_append(cat, var);
01548                }
01549                found = 1;
01550                break;
01551             }
01552          }
01553          /* save the results and clean things up */
01554          if (found) {
01555             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01556             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01557             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01558          }
01559       }
01560    }
01561 }
01562 
01563 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01564 {
01565    char buf[255];
01566    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01567    if (!ast_safe_system(buf)) {
01568       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01569       /* Reset the password in memory, too */
01570       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01571    }
01572 }
01573 
01574 /*! 
01575  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01576  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01577  * \param len The length of the path string that was written out.
01578  * \param context
01579  * \param ext 
01580  * \param folder 
01581  * 
01582  * The path is constructed as 
01583  *    VM_SPOOL_DIRcontext/ext/folder
01584  *
01585  * \return zero on success, -1 on error.
01586  */
01587 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01588 {
01589    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01590 }
01591 
01592 /*! 
01593  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01594  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01595  * \param len The length of the path string that was written out.
01596  * \param dir 
01597  * \param num 
01598  * 
01599  * The path is constructed as 
01600  *    VM_SPOOL_DIRcontext/ext/folder
01601  *
01602  * \return zero on success, -1 on error.
01603  */
01604 static int make_file(char *dest, const int len, const char *dir, const int num)
01605 {
01606    return snprintf(dest, len, "%s/msg%04d", dir, num);
01607 }
01608 
01609 /* same as mkstemp, but return a FILE * */
01610 static FILE *vm_mkftemp(char *template)
01611 {
01612    FILE *p = NULL;
01613    int pfd = mkstemp(template);
01614    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01615    if (pfd > -1) {
01616       p = fdopen(pfd, "w+");
01617       if (!p) {
01618          close(pfd);
01619          pfd = -1;
01620       }
01621    }
01622    return p;
01623 }
01624 
01625 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01626  * \param dest    String. base directory.
01627  * \param len     Length of dest.
01628  * \param context String. Ignored if is null or empty string.
01629  * \param ext     String. Ignored if is null or empty string.
01630  * \param folder  String. Ignored if is null or empty string. 
01631  * \return -1 on failure, 0 on success.
01632  */
01633 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01634 {
01635    mode_t   mode = VOICEMAIL_DIR_MODE;
01636    int res;
01637 
01638    make_dir(dest, len, context, ext, folder);
01639    if ((res = ast_mkdir(dest, mode))) {
01640       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01641       return -1;
01642    }
01643    return 0;
01644 }
01645 
01646 static const char * const mailbox_folders[] = {
01647 #ifdef IMAP_STORAGE
01648    imapfolder,
01649 #else
01650    "INBOX",
01651 #endif
01652    "Old",
01653    "Work",
01654    "Family",
01655    "Friends",
01656    "Cust1",
01657    "Cust2",
01658    "Cust3",
01659    "Cust4",
01660    "Cust5",
01661    "Deleted",
01662    "Urgent",
01663 };
01664 
01665 static const char *mbox(struct ast_vm_user *vmu, int id)
01666 {
01667 #ifdef IMAP_STORAGE
01668    if (vmu && id == 0) {
01669       return vmu->imapfolder;
01670    }
01671 #endif
01672    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01673 }
01674 
01675 static int get_folder_by_name(const char *name)
01676 {
01677    size_t i;
01678 
01679    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01680       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01681          return i;
01682       }
01683    }
01684 
01685    return -1;
01686 }
01687 
01688 static void free_user(struct ast_vm_user *vmu)
01689 {
01690    if (ast_test_flag(vmu, VM_ALLOCED)) {
01691       if (vmu->emailbody != NULL) {
01692          ast_free(vmu->emailbody);
01693          vmu->emailbody = NULL;
01694       }
01695       if (vmu->emailsubject != NULL) {
01696          ast_free(vmu->emailsubject);
01697          vmu->emailsubject = NULL;
01698       }
01699       ast_free(vmu);
01700    }
01701 }
01702 
01703 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01704 
01705    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01706    if (!vms->dh_arraysize) {
01707       /* initial allocation */
01708       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01709          return -1;
01710       }
01711       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01712          return -1;
01713       }
01714       vms->dh_arraysize = arraysize;
01715    } else if (vms->dh_arraysize < arraysize) {
01716       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01717          return -1;
01718       }
01719       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01720          return -1;
01721       }
01722       memset(vms->deleted, 0, arraysize * sizeof(int));
01723       memset(vms->heard, 0, arraysize * sizeof(int));
01724       vms->dh_arraysize = arraysize;
01725    }
01726 
01727    return 0;
01728 }
01729 
01730 /* All IMAP-specific functions should go in this block. This
01731  * keeps them from being spread out all over the code */
01732 #ifdef IMAP_STORAGE
01733 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01734 {
01735    char arg[10];
01736    struct vm_state *vms;
01737    unsigned long messageNum;
01738 
01739    /* If greetings aren't stored in IMAP, just delete the file */
01740    if (msgnum < 0 && !imapgreetings) {
01741       ast_filedelete(file, NULL);
01742       return;
01743    }
01744 
01745    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01746       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);
01747       return;
01748    }
01749 
01750    /* find real message number based on msgnum */
01751    /* this may be an index into vms->msgArray based on the msgnum. */
01752    messageNum = vms->msgArray[msgnum];
01753    if (messageNum == 0) {
01754       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01755       return;
01756    }
01757    if (option_debug > 2)
01758       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01759    /* delete message */
01760    snprintf (arg, sizeof(arg), "%lu", messageNum);
01761    ast_mutex_lock(&vms->lock);
01762    mail_setflag (vms->mailstream, arg, "\\DELETED");
01763    mail_expunge(vms->mailstream);
01764    ast_mutex_unlock(&vms->lock);
01765 }
01766 
01767 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01768 {
01769    struct vm_state *vms_p;
01770    char *file, *filename;
01771    char *attachment;
01772    int ret = 0, i;
01773    BODY *body;
01774 
01775    /* This function is only used for retrieval of IMAP greetings
01776     * regular messages are not retrieved this way, nor are greetings
01777     * if they are stored locally*/
01778    if (msgnum > -1 || !imapgreetings) {
01779       return 0;
01780    } else {
01781       file = strrchr(ast_strdupa(dir), '/');
01782       if (file)
01783          *file++ = '\0';
01784       else {
01785          ast_debug (1, "Failed to procure file name from directory passed.\n");
01786          return -1;
01787       }
01788    }
01789 
01790    /* check if someone is accessing this box right now... */
01791    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01792       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01793       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01794       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01795       * that's all we need to do.
01796       */
01797       if (!(vms_p = create_vm_state_from_user(vmu))) {
01798          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01799          return -1;
01800       }
01801    }
01802    
01803    /* Greetings will never have a prepended message */
01804    *vms_p->introfn = '\0';
01805 
01806    ast_mutex_lock(&vms_p->lock);
01807    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01808    if (!vms_p->mailstream) {
01809       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01810       ast_mutex_unlock(&vms_p->lock);
01811       return -1;
01812    }
01813 
01814    /*XXX Yuck, this could probably be done a lot better */
01815    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01816       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01817       /* We have the body, now we extract the file name of the first attachment. */
01818       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01819          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01820       } else {
01821          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01822          ast_mutex_unlock(&vms_p->lock);
01823          return -1;
01824       }
01825       filename = strsep(&attachment, ".");
01826       if (!strcmp(filename, file)) {
01827          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01828          vms_p->msgArray[vms_p->curmsg] = i + 1;
01829          save_body(body, vms_p, "2", attachment, 0);
01830          ast_mutex_unlock(&vms_p->lock);
01831          return 0;
01832       }
01833    }
01834    ast_mutex_unlock(&vms_p->lock);
01835 
01836    return -1;
01837 }
01838 
01839 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01840 {
01841    BODY *body;
01842    char *header_content;
01843    char *attachedfilefmt;
01844    char buf[80];
01845    struct vm_state *vms;
01846    char text_file[PATH_MAX];
01847    FILE *text_file_ptr;
01848    int res = 0;
01849    struct ast_vm_user *vmu;
01850 
01851    if (!(vmu = find_user(NULL, context, mailbox))) {
01852       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01853       return -1;
01854    }
01855    
01856    if (msgnum < 0) {
01857       if (imapgreetings) {
01858          res = imap_retrieve_greeting(dir, msgnum, vmu);
01859          goto exit;
01860       } else {
01861          res = 0;
01862          goto exit;
01863       }
01864    }
01865 
01866    /* Before anything can happen, we need a vm_state so that we can
01867     * actually access the imap server through the vms->mailstream
01868     */
01869    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01870       /* This should not happen. If it does, then I guess we'd
01871        * need to create the vm_state, extract which mailbox to
01872        * open, and then set up the msgArray so that the correct
01873        * IMAP message could be accessed. If I have seen correctly
01874        * though, the vms should be obtainable from the vmstates list
01875        * and should have its msgArray properly set up.
01876        */
01877       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01878       res = -1;
01879       goto exit;
01880    }
01881    
01882    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01883    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01884 
01885    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01886    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01887       res = 0;
01888       goto exit;
01889    }
01890 
01891    if (option_debug > 2)
01892       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01893    if (vms->msgArray[msgnum] == 0) {
01894       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01895       res = -1;
01896       goto exit;
01897    }
01898 
01899    /* This will only work for new messages... */
01900    ast_mutex_lock(&vms->lock);
01901    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01902    ast_mutex_unlock(&vms->lock);
01903    /* empty string means no valid header */
01904    if (ast_strlen_zero(header_content)) {
01905       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01906       res = -1;
01907       goto exit;
01908    }
01909 
01910    ast_mutex_lock(&vms->lock);
01911    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01912    ast_mutex_unlock(&vms->lock);
01913 
01914    /* We have the body, now we extract the file name of the first attachment. */
01915    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01916       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01917    } else {
01918       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01919       res = -1;
01920       goto exit;
01921    }
01922    
01923    /* Find the format of the attached file */
01924 
01925    strsep(&attachedfilefmt, ".");
01926    if (!attachedfilefmt) {
01927       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01928       res = -1;
01929       goto exit;
01930    }
01931    
01932    save_body(body, vms, "2", attachedfilefmt, 0);
01933    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01934       *vms->introfn = '\0';
01935    }
01936 
01937    /* Get info from headers!! */
01938    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01939 
01940    if (!(text_file_ptr = fopen(text_file, "w"))) {
01941       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01942    }
01943 
01944    fprintf(text_file_ptr, "%s\n", "[message]");
01945 
01946    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01947    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01948    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01949    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01950    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01951    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01952    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01953    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01954    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01955    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01956    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01957    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01958    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01959    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01960    fclose(text_file_ptr);
01961 
01962 exit:
01963    free_user(vmu);
01964    return res;
01965 }
01966 
01967 static int folder_int(const char *folder)
01968 {
01969    /*assume a NULL folder means INBOX*/
01970    if (!folder) {
01971       return 0;
01972    }
01973    if (!strcasecmp(folder, imapfolder)) {
01974       return 0;
01975    } else if (!strcasecmp(folder, "Old")) {
01976       return 1;
01977    } else if (!strcasecmp(folder, "Work")) {
01978       return 2;
01979    } else if (!strcasecmp(folder, "Family")) {
01980       return 3;
01981    } else if (!strcasecmp(folder, "Friends")) {
01982       return 4;
01983    } else if (!strcasecmp(folder, "Cust1")) {
01984       return 5;
01985    } else if (!strcasecmp(folder, "Cust2")) {
01986       return 6;
01987    } else if (!strcasecmp(folder, "Cust3")) {
01988       return 7;
01989    } else if (!strcasecmp(folder, "Cust4")) {
01990       return 8;
01991    } else if (!strcasecmp(folder, "Cust5")) {
01992       return 9;
01993    } else if (!strcasecmp(folder, "Urgent")) {
01994       return 11;
01995    } else { /*assume they meant INBOX if folder is not found otherwise*/
01996       return 0;
01997    }
01998 }
01999 
02000 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02001 {
02002    SEARCHPGM *pgm;
02003    SEARCHHEADER *hdr;
02004 
02005    struct ast_vm_user *vmu, vmus;
02006    struct vm_state *vms_p;
02007    int ret = 0;
02008    int fold = folder_int(folder);
02009    int urgent = 0;
02010    
02011    /* If URGENT, then look at INBOX */
02012    if (fold == 11) {
02013       fold = NEW_FOLDER;
02014       urgent = 1;
02015    }
02016 
02017    if (ast_strlen_zero(mailbox))
02018       return 0;
02019 
02020    /* We have to get the user before we can open the stream! */
02021    vmu = find_user(&vmus, context, mailbox);
02022    if (!vmu) {
02023       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02024       return -1;
02025    } else {
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          return -1;
02030       }
02031    }
02032    
02033    /* No IMAP account available */
02034    if (vmu->imapuser[0] == '\0') {
02035       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02036       free_user(vmu);
02037       return -1;
02038    }
02039 
02040    /* check if someone is accessing this box right now... */
02041    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02042    if (!vms_p) {
02043       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02044    }
02045    if (vms_p) {
02046       ast_debug(3, "Returning before search - user is logged in\n");
02047       if (fold == 0) { /* INBOX */
02048          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02049       }
02050       if (fold == 1) { /* Old messages */
02051          return vms_p->oldmessages;
02052       }
02053    }
02054 
02055    /* add one if not there... */
02056    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02057    if (!vms_p) {
02058       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02059    }
02060 
02061    if (!vms_p) {
02062       vms_p = create_vm_state_from_user(vmu);
02063    }
02064    ret = init_mailstream(vms_p, fold);
02065    if (!vms_p->mailstream) {
02066       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02067       return -1;
02068    }
02069    if (ret == 0) {
02070       ast_mutex_lock(&vms_p->lock);
02071       pgm = mail_newsearchpgm ();
02072       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02073       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02074       pgm->header = hdr;
02075       if (fold != OLD_FOLDER) {
02076          pgm->unseen = 1;
02077          pgm->seen = 0;
02078       }
02079       /* In the special case where fold is 1 (old messages) we have to do things a bit
02080        * differently. Old messages are stored in the INBOX but are marked as "seen"
02081        */
02082       else {
02083          pgm->unseen = 0;
02084          pgm->seen = 1;
02085       }
02086       /* look for urgent messages */
02087       if (fold == NEW_FOLDER) {
02088          if (urgent) {
02089             pgm->flagged = 1;
02090             pgm->unflagged = 0;
02091          } else {
02092             pgm->flagged = 0;
02093             pgm->unflagged = 1;
02094          }
02095       }
02096       pgm->undeleted = 1;
02097       pgm->deleted = 0;
02098 
02099       vms_p->vmArrayIndex = 0;
02100       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02101       if (fold == 0 && urgent == 0)
02102          vms_p->newmessages = vms_p->vmArrayIndex;
02103       if (fold == 1)
02104          vms_p->oldmessages = vms_p->vmArrayIndex;
02105       if (fold == 0 && urgent == 1)
02106          vms_p->urgentmessages = vms_p->vmArrayIndex;
02107       /*Freeing the searchpgm also frees the searchhdr*/
02108       mail_free_searchpgm(&pgm);
02109       ast_mutex_unlock(&vms_p->lock);
02110       vms_p->updated = 0;
02111       return vms_p->vmArrayIndex;
02112    } else {
02113       ast_mutex_lock(&vms_p->lock);
02114       mail_ping(vms_p->mailstream);
02115       ast_mutex_unlock(&vms_p->lock);
02116    }
02117    return 0;
02118 }
02119 
02120 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02121 {
02122    /* Check if mailbox is full */
02123    check_quota(vms, vmu->imapfolder);
02124    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02125       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02126       ast_play_and_wait(chan, "vm-mailboxfull");
02127       return -1;
02128    }
02129    
02130    /* Check if we have exceeded maxmsg */
02131    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));
02132    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02133       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02134       ast_play_and_wait(chan, "vm-mailboxfull");
02135       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02136       return -1;
02137    }
02138 
02139    return 0;
02140 }
02141 
02142 /*!
02143  * \brief Gets the number of messages that exist in a mailbox folder.
02144  * \param context
02145  * \param mailbox
02146  * \param folder
02147  * 
02148  * This method is used when IMAP backend is used.
02149  * \return The number of messages in this mailbox folder (zero or more).
02150  */
02151 static int messagecount(const char *context, const char *mailbox, const char *folder)
02152 {
02153    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02154       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02155    } else {
02156       return __messagecount(context, mailbox, folder);
02157    }
02158 }
02159 
02160 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)
02161 {
02162    char *myserveremail = serveremail;
02163    char fn[PATH_MAX];
02164    char introfn[PATH_MAX];
02165    char mailbox[256];
02166    char *stringp;
02167    FILE *p = NULL;
02168    char tmp[80] = "/tmp/astmail-XXXXXX";
02169    long len;
02170    void *buf;
02171    int tempcopy = 0;
02172    STRING str;
02173    int ret; /* for better error checking */
02174    char *imap_flags = NIL;
02175    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02176 
02177     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02178     if (msgnum < 0 && !imapgreetings) {
02179         return 0;
02180     }
02181    
02182    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02183       return -1;
02184    }
02185 
02186    /* Set urgent flag for IMAP message */
02187    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02188       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02189       imap_flags = "\\FLAGGED";
02190    }
02191    
02192    /* Attach only the first format */
02193    fmt = ast_strdupa(fmt);
02194    stringp = fmt;
02195    strsep(&stringp, "|");
02196 
02197    if (!ast_strlen_zero(vmu->serveremail))
02198       myserveremail = vmu->serveremail;
02199 
02200    if (msgnum > -1)
02201       make_file(fn, sizeof(fn), dir, msgnum);
02202    else
02203       ast_copy_string (fn, dir, sizeof(fn));
02204 
02205    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02206    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02207       *introfn = '\0';
02208    }
02209    
02210    if (ast_strlen_zero(vmu->email)) {
02211       /* We need the vmu->email to be set when we call make_email_file, but
02212        * if we keep it set, a duplicate e-mail will be created. So at the end
02213        * of this function, we will revert back to an empty string if tempcopy
02214        * is 1.
02215        */
02216       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02217       tempcopy = 1;
02218    }
02219 
02220    if (!strcmp(fmt, "wav49"))
02221       fmt = "WAV";
02222    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02223 
02224    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02225       command hangs. */
02226    if (!(p = vm_mkftemp(tmp))) {
02227       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02228       if (tempcopy)
02229          *(vmu->email) = '\0';
02230       return -1;
02231    }
02232 
02233    if (msgnum < 0 && imapgreetings) {
02234       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02235          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02236          return -1;
02237       }
02238       imap_delete_old_greeting(fn, vms);
02239    }
02240 
02241    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02242       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02243       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02244       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02245    /* read mail file to memory */
02246    len = ftell(p);
02247    rewind(p);
02248    if (!(buf = ast_malloc(len + 1))) {
02249       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02250       fclose(p);
02251       if (tempcopy)
02252          *(vmu->email) = '\0';
02253       return -1;
02254    }
02255    if (fread(buf, len, 1, p) < len) {
02256       if (ferror(p)) {
02257          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02258          return -1;
02259       }
02260    }
02261    ((char *) buf)[len] = '\0';
02262    INIT(&str, mail_string, buf, len);
02263    ret = init_mailstream(vms, NEW_FOLDER);
02264    if (ret == 0) {
02265       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02266       ast_mutex_lock(&vms->lock);
02267       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02268          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02269       ast_mutex_unlock(&vms->lock);
02270       fclose(p);
02271       unlink(tmp);
02272       ast_free(buf);
02273    } else {
02274       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02275       fclose(p);
02276       unlink(tmp);
02277       ast_free(buf);
02278       return -1;
02279    }
02280    ast_debug(3, "%s stored\n", fn);
02281    
02282    if (tempcopy)
02283       *(vmu->email) = '\0';
02284    inprocess_count(vmu->mailbox, vmu->context, -1);
02285    return 0;
02286 
02287 }
02288 
02289 /*!
02290  * \brief Gets the number of messages that exist in the inbox folder.
02291  * \param mailbox_context
02292  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02293  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02294  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02295  * 
02296  * This method is used when IMAP backend is used.
02297  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02298  *
02299  * \return zero on success, -1 on error.
02300  */
02301 
02302 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02303 {
02304    char tmp[PATH_MAX] = "";
02305    char *mailboxnc;
02306    char *context;
02307    char *mb;
02308    char *cur;
02309    if (newmsgs)
02310       *newmsgs = 0;
02311    if (oldmsgs)
02312       *oldmsgs = 0;
02313    if (urgentmsgs)
02314       *urgentmsgs = 0;
02315 
02316    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02317    /* If no mailbox, return immediately */
02318    if (ast_strlen_zero(mailbox_context))
02319       return 0;
02320    
02321    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02322    context = strchr(tmp, '@');
02323    if (strchr(mailbox_context, ',')) {
02324       int tmpnew, tmpold, tmpurgent;
02325       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02326       mb = tmp;
02327       while ((cur = strsep(&mb, ", "))) {
02328          if (!ast_strlen_zero(cur)) {
02329             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02330                return -1;
02331             else {
02332                if (newmsgs)
02333                   *newmsgs += tmpnew; 
02334                if (oldmsgs)
02335                   *oldmsgs += tmpold;
02336                if (urgentmsgs)
02337                   *urgentmsgs += tmpurgent;
02338             }
02339          }
02340       }
02341       return 0;
02342    }
02343    if (context) {
02344       *context = '\0';
02345       mailboxnc = tmp;
02346       context++;
02347    } else {
02348       context = "default";
02349       mailboxnc = (char *) mailbox_context;
02350    }
02351 
02352    if (newmsgs) {
02353       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02354       if (!vmu) {
02355          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02356          return -1;
02357       }
02358       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02359          return -1;
02360       }
02361    }
02362    if (oldmsgs) {
02363       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02364          return -1;
02365       }
02366    }
02367    if (urgentmsgs) {
02368       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02369          return -1;
02370       }
02371    }
02372    return 0;
02373 }
02374 
02375 /** 
02376  * \brief Determines if the given folder has messages.
02377  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02378  * \param folder the folder to look in
02379  *
02380  * This function is used when the mailbox is stored in an IMAP back end.
02381  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02382  * \return 1 if the folder has one or more messages. zero otherwise.
02383  */
02384 
02385 static int has_voicemail(const char *mailbox, const char *folder)
02386 {
02387    char tmp[256], *tmp2, *box, *context;
02388    ast_copy_string(tmp, mailbox, sizeof(tmp));
02389    tmp2 = tmp;
02390    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02391       while ((box = strsep(&tmp2, ",&"))) {
02392          if (!ast_strlen_zero(box)) {
02393             if (has_voicemail(box, folder)) {
02394                return 1;
02395             }
02396          }
02397       }
02398    }
02399    if ((context = strchr(tmp, '@'))) {
02400       *context++ = '\0';
02401    } else {
02402       context = "default";
02403    }
02404    return __messagecount(context, tmp, folder) ? 1 : 0;
02405 }
02406 
02407 /*!
02408  * \brief Copies a message from one mailbox to another.
02409  * \param chan
02410  * \param vmu
02411  * \param imbox
02412  * \param msgnum
02413  * \param duration
02414  * \param recip
02415  * \param fmt
02416  * \param dir
02417  *
02418  * This works with IMAP storage based mailboxes.
02419  *
02420  * \return zero on success, -1 on error.
02421  */
02422 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)
02423 {
02424    struct vm_state *sendvms = NULL, *destvms = NULL;
02425    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02426    if (msgnum >= recip->maxmsg) {
02427       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02428       return -1;
02429    }
02430    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02431       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02432       return -1;
02433    }
02434    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02435       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02436       return -1;
02437    }
02438    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02439    ast_mutex_lock(&sendvms->lock);
02440    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02441       ast_mutex_unlock(&sendvms->lock);
02442       return 0;
02443    }
02444    ast_mutex_unlock(&sendvms->lock);
02445    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02446    return -1;
02447 }
02448 
02449 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02450 {
02451    char tmp[256], *t = tmp;
02452    size_t left = sizeof(tmp);
02453    
02454    if (box == OLD_FOLDER) {
02455       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02456    } else {
02457       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02458    }
02459 
02460    if (box == NEW_FOLDER) {
02461       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02462    } else {
02463       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02464    }
02465 
02466    /* Build up server information */
02467    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02468 
02469    /* Add authentication user if present */
02470    if (!ast_strlen_zero(authuser))
02471       ast_build_string(&t, &left, "/authuser=%s", authuser);
02472 
02473    /* Add flags if present */
02474    if (!ast_strlen_zero(imapflags))
02475       ast_build_string(&t, &left, "/%s", imapflags);
02476 
02477    /* End with username */
02478 #if 1
02479    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02480 #else
02481    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02482 #endif
02483    if (box == NEW_FOLDER || box == OLD_FOLDER)
02484       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02485    else if (box == GREETINGS_FOLDER)
02486       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02487    else {   /* Other folders such as Friends, Family, etc... */
02488       if (!ast_strlen_zero(imapparentfolder)) {
02489          /* imapparentfolder would typically be set to INBOX */
02490          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02491       } else {
02492          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02493       }
02494    }
02495 }
02496 
02497 static int init_mailstream(struct vm_state *vms, int box)
02498 {
02499    MAILSTREAM *stream = NIL;
02500    long debug;
02501    char tmp[256];
02502    
02503    if (!vms) {
02504       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02505       return -1;
02506    }
02507    if (option_debug > 2)
02508       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02509    if (vms->mailstream == NIL || !vms->mailstream) {
02510       if (option_debug)
02511          ast_log(LOG_DEBUG, "mailstream not set.\n");
02512    } else {
02513       stream = vms->mailstream;
02514    }
02515    /* debug = T;  user wants protocol telemetry? */
02516    debug = NIL;  /* NO protocol telemetry? */
02517 
02518    if (delimiter == '\0') {      /* did not probe the server yet */
02519       char *cp;
02520 #ifdef USE_SYSTEM_IMAP
02521 #include <imap/linkage.c>
02522 #elif defined(USE_SYSTEM_CCLIENT)
02523 #include <c-client/linkage.c>
02524 #else
02525 #include "linkage.c"
02526 #endif
02527       /* Connect to INBOX first to get folders delimiter */
02528       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02529       ast_mutex_lock(&vms->lock);
02530       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02531       ast_mutex_unlock(&vms->lock);
02532       if (stream == NIL) {
02533          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02534          return -1;
02535       }
02536       get_mailbox_delimiter(stream);
02537       /* update delimiter in imapfolder */
02538       for (cp = vms->imapfolder; *cp; cp++)
02539          if (*cp == '/')
02540             *cp = delimiter;
02541    }
02542    /* Now connect to the target folder */
02543    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02544    if (option_debug > 2)
02545       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02546    ast_mutex_lock(&vms->lock);
02547    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02548    ast_mutex_unlock(&vms->lock);
02549    if (vms->mailstream == NIL) {
02550       return -1;
02551    } else {
02552       return 0;
02553    }
02554 }
02555 
02556 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02557 {
02558    SEARCHPGM *pgm;
02559    SEARCHHEADER *hdr;
02560    int ret, urgent = 0;
02561 
02562    /* If Urgent, then look at INBOX */
02563    if (box == 11) {
02564       box = NEW_FOLDER;
02565       urgent = 1;
02566    }
02567 
02568    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02569    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02570    vms->imapversion = vmu->imapversion;
02571    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02572 
02573    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02574       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02575       return -1;
02576    }
02577    
02578    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02579    
02580    /* Check Quota */
02581    if  (box == 0)  {
02582       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02583       check_quota(vms, (char *) mbox(vmu, box));
02584    }
02585 
02586    ast_mutex_lock(&vms->lock);
02587    pgm = mail_newsearchpgm();
02588 
02589    /* Check IMAP folder for Asterisk messages only... */
02590    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02591    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02592    pgm->header = hdr;
02593    pgm->deleted = 0;
02594    pgm->undeleted = 1;
02595 
02596    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02597    if (box == NEW_FOLDER && urgent == 1) {
02598       pgm->unseen = 1;
02599       pgm->seen = 0;
02600       pgm->flagged = 1;
02601       pgm->unflagged = 0;
02602    } else if (box == NEW_FOLDER && urgent == 0) {
02603       pgm->unseen = 1;
02604       pgm->seen = 0;
02605       pgm->flagged = 0;
02606       pgm->unflagged = 1;
02607    } else if (box == OLD_FOLDER) {
02608       pgm->seen = 1;
02609       pgm->unseen = 0;
02610    }
02611 
02612    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02613 
02614    vms->vmArrayIndex = 0;
02615    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02616    vms->lastmsg = vms->vmArrayIndex - 1;
02617    mail_free_searchpgm(&pgm);
02618    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02619     * ensure to allocate enough space to account for all of them. Warn if old messages
02620     * have not been checked first as that is required.
02621     */
02622    if (box == 0 && !vms->dh_arraysize) {
02623       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02624    }
02625    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02626       ast_mutex_unlock(&vms->lock);
02627       return -1;
02628    }
02629 
02630    ast_mutex_unlock(&vms->lock);
02631    return 0;
02632 }
02633 
02634 static void write_file(char *filename, char *buffer, unsigned long len)
02635 {
02636    FILE *output;
02637 
02638    output = fopen (filename, "w");
02639    if (fwrite(buffer, len, 1, output) != 1) {
02640       if (ferror(output)) {
02641          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02642       }
02643    }
02644    fclose (output);
02645 }
02646 
02647 static void update_messages_by_imapuser(const char *user, unsigned long number)
02648 {
02649    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02650 
02651    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02652       return;
02653    }
02654 
02655    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02656    vms->msgArray[vms->vmArrayIndex++] = number;
02657 }
02658 
02659 void mm_searched(MAILSTREAM *stream, unsigned long number)
02660 {
02661    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02662 
02663    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02664       return;
02665 
02666    update_messages_by_imapuser(user, number);
02667 }
02668 
02669 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02670 {
02671    struct ast_variable *var;
02672    struct ast_vm_user *vmu;
02673 
02674    vmu = ast_calloc(1, sizeof *vmu);
02675    if (!vmu)
02676       return NULL;
02677    ast_set_flag(vmu, VM_ALLOCED);
02678    populate_defaults(vmu);
02679 
02680    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02681    if (var) {
02682       apply_options_full(vmu, var);
02683       ast_variables_destroy(var);
02684       return vmu;
02685    } else {
02686       ast_free(vmu);
02687       return NULL;
02688    }
02689 }
02690 
02691 /* Interfaces to C-client */
02692 
02693 void mm_exists(MAILSTREAM * stream, unsigned long number)
02694 {
02695    /* mail_ping will callback here if new mail! */
02696    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02697    if (number == 0) return;
02698    set_update(stream);
02699 }
02700 
02701 
02702 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02703 {
02704    /* mail_ping will callback here if expunged mail! */
02705    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02706    if (number == 0) return;
02707    set_update(stream);
02708 }
02709 
02710 
02711 void mm_flags(MAILSTREAM * stream, unsigned long number)
02712 {
02713    /* mail_ping will callback here if read mail! */
02714    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02715    if (number == 0) return;
02716    set_update(stream);
02717 }
02718 
02719 
02720 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02721 {
02722    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02723    mm_log (string, errflg);
02724 }
02725 
02726 
02727 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02728 {
02729    if (delimiter == '\0') {
02730       delimiter = delim;
02731    }
02732 
02733    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02734    if (attributes & LATT_NOINFERIORS)
02735       ast_debug(5, "no inferiors\n");
02736    if (attributes & LATT_NOSELECT)
02737       ast_debug(5, "no select\n");
02738    if (attributes & LATT_MARKED)
02739       ast_debug(5, "marked\n");
02740    if (attributes & LATT_UNMARKED)
02741       ast_debug(5, "unmarked\n");
02742 }
02743 
02744 
02745 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02746 {
02747    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02748    if (attributes & LATT_NOINFERIORS)
02749       ast_debug(5, "no inferiors\n");
02750    if (attributes & LATT_NOSELECT)
02751       ast_debug(5, "no select\n");
02752    if (attributes & LATT_MARKED)
02753       ast_debug(5, "marked\n");
02754    if (attributes & LATT_UNMARKED)
02755       ast_debug(5, "unmarked\n");
02756 }
02757 
02758 
02759 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02760 {
02761    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02762    if (status->flags & SA_MESSAGES)
02763       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02764    if (status->flags & SA_RECENT)
02765       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02766    if (status->flags & SA_UNSEEN)
02767       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02768    if (status->flags & SA_UIDVALIDITY)
02769       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02770    if (status->flags & SA_UIDNEXT)
02771       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02772    ast_log(AST_LOG_NOTICE, "\n");
02773 }
02774 
02775 
02776 void mm_log(char *string, long errflg)
02777 {
02778    switch ((short) errflg) {
02779       case NIL:
02780          ast_debug(1, "IMAP Info: %s\n", string);
02781          break;
02782       case PARSE:
02783       case WARN:
02784          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02785          break;
02786       case ERROR:
02787          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02788          break;
02789    }
02790 }
02791 
02792 
02793 void mm_dlog(char *string)
02794 {
02795    ast_log(AST_LOG_NOTICE, "%s\n", string);
02796 }
02797 
02798 
02799 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02800 {
02801    struct ast_vm_user *vmu;
02802 
02803    ast_debug(4, "Entering callback mm_login\n");
02804 
02805    ast_copy_string(user, mb->user, MAILTMPLEN);
02806 
02807    /* We should only do this when necessary */
02808    if (!ast_strlen_zero(authpassword)) {
02809       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02810    } else {
02811       AST_LIST_TRAVERSE(&users, vmu, list) {
02812          if (!strcasecmp(mb->user, vmu->imapuser)) {
02813             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02814             break;
02815          }
02816       }
02817       if (!vmu) {
02818          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02819             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02820             free_user(vmu);
02821          }
02822       }
02823    }
02824 }
02825 
02826 
02827 void mm_critical(MAILSTREAM * stream)
02828 {
02829 }
02830 
02831 
02832 void mm_nocritical(MAILSTREAM * stream)
02833 {
02834 }
02835 
02836 
02837 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02838 {
02839    kill (getpid (), SIGSTOP);
02840    return NIL;
02841 }
02842 
02843 
02844 void mm_fatal(char *string)
02845 {
02846    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02847 }
02848 
02849 /* C-client callback to handle quota */
02850 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02851 {
02852    struct vm_state *vms;
02853    char *mailbox = stream->mailbox, *user;
02854    char buf[1024] = "";
02855    unsigned long usage = 0, limit = 0;
02856    
02857    while (pquota) {
02858       usage = pquota->usage;
02859       limit = pquota->limit;
02860       pquota = pquota->next;
02861    }
02862    
02863    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)))) {
02864       ast_log(AST_LOG_ERROR, "No state found.\n");
02865       return;
02866    }
02867 
02868    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02869 
02870    vms->quota_usage = usage;
02871    vms->quota_limit = limit;
02872 }
02873 
02874 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02875 {
02876    char *start, *eol_pnt;
02877    int taglen;
02878 
02879    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02880       return NULL;
02881 
02882    taglen = strlen(tag) + 1;
02883    if (taglen < 1)
02884       return NULL;
02885 
02886    if (!(start = strstr(header, tag)))
02887       return NULL;
02888 
02889    /* Since we can be called multiple times we should clear our buffer */
02890    memset(buf, 0, len);
02891 
02892    ast_copy_string(buf, start+taglen, len);
02893    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02894       *eol_pnt = '\0';
02895    return buf;
02896 }
02897 
02898 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02899 {
02900    char *start, *quote, *eol_pnt;
02901 
02902    if (ast_strlen_zero(mailbox))
02903       return NULL;
02904 
02905    if (!(start = strstr(mailbox, "/user=")))
02906       return NULL;
02907 
02908    ast_copy_string(buf, start+6, len);
02909 
02910    if (!(quote = strchr(buf, '\"'))) {
02911       if (!(eol_pnt = strchr(buf, '/')))
02912          eol_pnt = strchr(buf,'}');
02913       *eol_pnt = '\0';
02914       return buf;
02915    } else {
02916       eol_pnt = strchr(buf+1,'\"');
02917       *eol_pnt = '\0';
02918       return buf+1;
02919    }
02920 }
02921 
02922 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02923 {
02924    struct vm_state *vms_p;
02925 
02926    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02927    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02928       return vms_p;
02929    }
02930    if (option_debug > 4)
02931       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02932    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02933       return NULL;
02934    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02935    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02936    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02937    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02938    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02939    vms_p->imapversion = vmu->imapversion;
02940    if (option_debug > 4)
02941       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02942    vms_p->updated = 1;
02943    /* set mailbox to INBOX! */
02944    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02945    init_vm_state(vms_p);
02946    vmstate_insert(vms_p);
02947    return vms_p;
02948 }
02949 
02950 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02951 {
02952    struct vmstate *vlist = NULL;
02953 
02954    if (interactive) {
02955       struct vm_state *vms;
02956       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02957       vms = pthread_getspecific(ts_vmstate.key);
02958       return vms;
02959    }
02960 
02961    AST_LIST_LOCK(&vmstates);
02962    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02963       if (!vlist->vms) {
02964          ast_debug(3, "error: vms is NULL for %s\n", user);
02965          continue;
02966       }
02967       if (vlist->vms->imapversion != imapversion) {
02968          continue;
02969       }
02970       if (!vlist->vms->imapuser) {
02971          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02972          continue;
02973       }
02974 
02975       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02976          AST_LIST_UNLOCK(&vmstates);
02977          return vlist->vms;
02978       }
02979    }
02980    AST_LIST_UNLOCK(&vmstates);
02981 
02982    ast_debug(3, "%s not found in vmstates\n", user);
02983 
02984    return NULL;
02985 }
02986 
02987 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02988 {
02989 
02990    struct vmstate *vlist = NULL;
02991    const char *local_context = S_OR(context, "default");
02992 
02993    if (interactive) {
02994       struct vm_state *vms;
02995       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02996       vms = pthread_getspecific(ts_vmstate.key);
02997       return vms;
02998    }
02999 
03000    AST_LIST_LOCK(&vmstates);
03001    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03002       if (!vlist->vms) {
03003          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03004          continue;
03005       }
03006       if (vlist->vms->imapversion != imapversion) {
03007          continue;
03008       }
03009       if (!vlist->vms->username || !vlist->vms->context) {
03010          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03011          continue;
03012       }
03013 
03014       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);
03015       
03016       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03017          ast_debug(3, "Found it!\n");
03018          AST_LIST_UNLOCK(&vmstates);
03019          return vlist->vms;
03020       }
03021    }
03022    AST_LIST_UNLOCK(&vmstates);
03023 
03024    ast_debug(3, "%s not found in vmstates\n", mailbox);
03025 
03026    return NULL;
03027 }
03028 
03029 static void vmstate_insert(struct vm_state *vms) 
03030 {
03031    struct vmstate *v;
03032    struct vm_state *altvms;
03033 
03034    /* If interactive, it probably already exists, and we should
03035       use the one we already have since it is more up to date.
03036       We can compare the username to find the duplicate */
03037    if (vms->interactive == 1) {
03038       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03039       if (altvms) {  
03040          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03041          vms->newmessages = altvms->newmessages;
03042          vms->oldmessages = altvms->oldmessages;
03043          vms->vmArrayIndex = altvms->vmArrayIndex;
03044          vms->lastmsg = altvms->lastmsg;
03045          vms->curmsg = altvms->curmsg;
03046          /* get a pointer to the persistent store */
03047          vms->persist_vms = altvms;
03048          /* Reuse the mailstream? */
03049 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03050          vms->mailstream = altvms->mailstream;
03051 #else
03052          vms->mailstream = NIL;
03053 #endif
03054       }
03055       return;
03056    }
03057 
03058    if (!(v = ast_calloc(1, sizeof(*v))))
03059       return;
03060    
03061    v->vms = vms;
03062 
03063    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03064 
03065    AST_LIST_LOCK(&vmstates);
03066    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03067    AST_LIST_UNLOCK(&vmstates);
03068 }
03069 
03070 static void vmstate_delete(struct vm_state *vms) 
03071 {
03072    struct vmstate *vc = NULL;
03073    struct vm_state *altvms = NULL;
03074 
03075    /* If interactive, we should copy pertinent info
03076       back to the persistent state (to make update immediate) */
03077    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03078       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03079       altvms->newmessages = vms->newmessages;
03080       altvms->oldmessages = vms->oldmessages;
03081       altvms->updated = 1;
03082       vms->mailstream = mail_close(vms->mailstream);
03083 
03084       /* Interactive states are not stored within the persistent list */
03085       return;
03086    }
03087    
03088    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03089    
03090    AST_LIST_LOCK(&vmstates);
03091    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03092       if (vc->vms == vms) {
03093          AST_LIST_REMOVE_CURRENT(list);
03094          break;
03095       }
03096    }
03097    AST_LIST_TRAVERSE_SAFE_END
03098    AST_LIST_UNLOCK(&vmstates);
03099    
03100    if (vc) {
03101       ast_mutex_destroy(&vc->vms->lock);
03102       ast_free(vc);
03103    }
03104    else
03105       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03106 }
03107 
03108 static void set_update(MAILSTREAM * stream) 
03109 {
03110    struct vm_state *vms;
03111    char *mailbox = stream->mailbox, *user;
03112    char buf[1024] = "";
03113 
03114    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03115       if (user && option_debug > 2)
03116          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03117       return;
03118    }
03119 
03120    ast_debug(3, "User %s mailbox set for update.\n", user);
03121 
03122    vms->updated = 1; /* Set updated flag since mailbox changed */
03123 }
03124 
03125 static void init_vm_state(struct vm_state *vms) 
03126 {
03127    int x;
03128    vms->vmArrayIndex = 0;
03129    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03130       vms->msgArray[x] = 0;
03131    }
03132    ast_mutex_init(&vms->lock);
03133 }
03134 
03135 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03136 {
03137    char *body_content;
03138    char *body_decoded;
03139    char *fn = is_intro ? vms->introfn : vms->fn;
03140    unsigned long len;
03141    unsigned long newlen;
03142    char filename[256];
03143    
03144    if (!body || body == NIL)
03145       return -1;
03146 
03147    ast_mutex_lock(&vms->lock);
03148    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03149    ast_mutex_unlock(&vms->lock);
03150    if (body_content != NIL) {
03151       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03152       /* ast_debug(1,body_content); */
03153       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03154       /* If the body of the file is empty, return an error */
03155       if (!newlen) {
03156          return -1;
03157       }
03158       write_file(filename, (char *) body_decoded, newlen);
03159    } else {
03160       ast_debug(5, "Body of message is NULL.\n");
03161       return -1;
03162    }
03163    return 0;
03164 }
03165 
03166 /*! 
03167  * \brief Get delimiter via mm_list callback 
03168  * \param stream
03169  *
03170  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03171  */
03172 /* MUTEX should already be held */
03173 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03174    char tmp[50];
03175    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03176    mail_list(stream, tmp, "*");
03177 }
03178 
03179 /*! 
03180  * \brief Check Quota for user 
03181  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03182  * \param mailbox the mailbox to check the quota for.
03183  *
03184  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03185  */
03186 static void check_quota(struct vm_state *vms, char *mailbox) {
03187    ast_mutex_lock(&vms->lock);
03188    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03189    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03190    if (vms && vms->mailstream != NULL) {
03191       imap_getquotaroot(vms->mailstream, mailbox);
03192    } else {
03193       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03194    }
03195    ast_mutex_unlock(&vms->lock);
03196 }
03197 
03198 #endif /* IMAP_STORAGE */
03199 
03200 /*! \brief Lock file path
03201     only return failure if ast_lock_path returns 'timeout',
03202    not if the path does not exist or any other reason
03203 */
03204 static int vm_lock_path(const char *path)
03205 {
03206    switch (ast_lock_path(path)) {
03207    case AST_LOCK_TIMEOUT:
03208       return -1;
03209    default:
03210       return 0;
03211    }
03212 }
03213 
03214 
03215 #ifdef ODBC_STORAGE
03216 struct generic_prepare_struct {
03217    char *sql;
03218    int argc;
03219    char **argv;
03220 };
03221 
03222 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03223 {
03224    struct generic_prepare_struct *gps = data;
03225    int res, i;
03226    SQLHSTMT stmt;
03227 
03228    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03229    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03230       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03231       return NULL;
03232    }
03233    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03234    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03235       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03236       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03237       return NULL;
03238    }
03239    for (i = 0; i < gps->argc; i++)
03240       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03241 
03242    return stmt;
03243 }
03244 
03245 /*!
03246  * \brief Retrieves a file from an ODBC data store.
03247  * \param dir the path to the file to be retreived.
03248  * \param msgnum the message number, such as within a mailbox folder.
03249  * 
03250  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03251  * 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.
03252  *
03253  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03254  * The output is the message information file with the name msgnum and the extension .txt
03255  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03256  * 
03257  * \return 0 on success, -1 on error.
03258  */
03259 static int retrieve_file(char *dir, int msgnum)
03260 {
03261    int x = 0;
03262    int res;
03263    int fd = -1;
03264    size_t fdlen = 0;
03265    void *fdm = MAP_FAILED;
03266    SQLSMALLINT colcount = 0;
03267    SQLHSTMT stmt;
03268    char sql[PATH_MAX];
03269    char fmt[80]="";
03270    char *c;
03271    char coltitle[256];
03272    SQLSMALLINT collen;
03273    SQLSMALLINT datatype;
03274    SQLSMALLINT decimaldigits;
03275    SQLSMALLINT nullable;
03276    SQLULEN colsize;
03277    SQLLEN colsize2;
03278    FILE *f = NULL;
03279    char rowdata[80];
03280    char fn[PATH_MAX];
03281    char full_fn[PATH_MAX];
03282    char msgnums[80];
03283    char *argv[] = { dir, msgnums };
03284    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03285 
03286    struct odbc_obj *obj;
03287    obj = ast_odbc_request_obj(odbc_database, 0);
03288    if (obj) {
03289       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03290       c = strchr(fmt, '|');
03291       if (c)
03292          *c = '\0';
03293       if (!strcasecmp(fmt, "wav49"))
03294          strcpy(fmt, "WAV");
03295       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03296       if (msgnum > -1)
03297          make_file(fn, sizeof(fn), dir, msgnum);
03298       else
03299          ast_copy_string(fn, dir, sizeof(fn));
03300 
03301       /* Create the information file */
03302       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03303       
03304       if (!(f = fopen(full_fn, "w+"))) {
03305          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03306          goto yuck;
03307       }
03308       
03309       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03310       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03311       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03312       if (!stmt) {
03313          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03314          ast_odbc_release_obj(obj);
03315          goto yuck;
03316       }
03317       res = SQLFetch(stmt);
03318       if (res == SQL_NO_DATA) {
03319          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03320          ast_odbc_release_obj(obj);
03321          goto yuck;
03322       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03323          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03324          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03325          ast_odbc_release_obj(obj);
03326          goto yuck;
03327       }
03328       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03329       if (fd < 0) {
03330          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03331          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03332          ast_odbc_release_obj(obj);
03333          goto yuck;
03334       }
03335       res = SQLNumResultCols(stmt, &colcount);
03336       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03337          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03338          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03339          ast_odbc_release_obj(obj);
03340          goto yuck;
03341       }
03342       if (f) 
03343          fprintf(f, "[message]\n");
03344       for (x = 0; x < colcount; x++) {
03345          rowdata[0] = '\0';
03346          collen = sizeof(coltitle);
03347          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03348                   &datatype, &colsize, &decimaldigits, &nullable);
03349          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03350             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03351             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03352             ast_odbc_release_obj(obj);
03353             goto yuck;
03354          }
03355          if (!strcasecmp(coltitle, "recording")) {
03356             off_t offset;
03357             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03358             fdlen = colsize2;
03359             if (fd > -1) {
03360                char tmp[1]="";
03361                lseek(fd, fdlen - 1, SEEK_SET);
03362                if (write(fd, tmp, 1) != 1) {
03363                   close(fd);
03364                   fd = -1;
03365                   continue;
03366                }
03367                /* Read out in small chunks */
03368                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03369                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03370                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03371                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03372                      ast_odbc_release_obj(obj);
03373                      goto yuck;
03374                   } else {
03375                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03376                      munmap(fdm, CHUNKSIZE);
03377                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03378                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03379                         unlink(full_fn);
03380                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03381                         ast_odbc_release_obj(obj);
03382                         goto yuck;
03383                      }
03384                   }
03385                }
03386                if (truncate(full_fn, fdlen) < 0) {
03387                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03388                }
03389             }
03390          } else {
03391             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03392             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03393                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03394                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03395                ast_odbc_release_obj(obj);
03396                goto yuck;
03397             }
03398             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03399                fprintf(f, "%s=%s\n", coltitle, rowdata);
03400          }
03401       }
03402       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03403       ast_odbc_release_obj(obj);
03404    } else
03405       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03406 yuck: 
03407    if (f)
03408       fclose(f);
03409    if (fd > -1)
03410       close(fd);
03411    return x - 1;
03412 }
03413 
03414 /*!
03415  * \brief Determines the highest message number in use for a given user and mailbox folder.
03416  * \param vmu 
03417  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03418  *
03419  * This method is used when mailboxes are stored in an ODBC back end.
03420  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03421  *
03422  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03423  */
03424 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03425 {
03426    int x = 0;
03427    int res;
03428    SQLHSTMT stmt;
03429    char sql[PATH_MAX];
03430    char rowdata[20];
03431    char *argv[] = { dir };
03432    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03433 
03434    struct odbc_obj *obj;
03435    obj = ast_odbc_request_obj(odbc_database, 0);
03436    if (obj) {
03437       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03438       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03439       if (!stmt) {
03440          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03441          ast_odbc_release_obj(obj);
03442          goto yuck;
03443       }
03444       res = SQLFetch(stmt);
03445       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03446          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03447          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03448          ast_odbc_release_obj(obj);
03449          goto yuck;
03450       }
03451       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03452       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03453          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03454          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03455          ast_odbc_release_obj(obj);
03456          goto yuck;
03457       }
03458       if (sscanf(rowdata, "%30d", &x) != 1)
03459          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03460       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03461       ast_odbc_release_obj(obj);
03462    } else
03463       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03464 yuck: 
03465    return x - 1;
03466 }
03467 
03468 /*!
03469  * \brief Determines if the specified message exists.
03470  * \param dir the folder the mailbox folder to look for messages. 
03471  * \param msgnum the message index to query for.
03472  *
03473  * This method is used when mailboxes are stored in an ODBC back end.
03474  *
03475  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03476  */
03477 static int message_exists(char *dir, int msgnum)
03478 {
03479    int x = 0;
03480    int res;
03481    SQLHSTMT stmt;
03482    char sql[PATH_MAX];
03483    char rowdata[20];
03484    char msgnums[20];
03485    char *argv[] = { dir, msgnums };
03486    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03487 
03488    struct odbc_obj *obj;
03489    obj = ast_odbc_request_obj(odbc_database, 0);
03490    if (obj) {
03491       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03492       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03493       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03494       if (!stmt) {
03495          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03496          ast_odbc_release_obj(obj);
03497          goto yuck;
03498       }
03499       res = SQLFetch(stmt);
03500       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03501          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03502          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03503          ast_odbc_release_obj(obj);
03504          goto yuck;
03505       }
03506       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03507       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03508          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03509          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03510          ast_odbc_release_obj(obj);
03511          goto yuck;
03512       }
03513       if (sscanf(rowdata, "%30d", &x) != 1)
03514          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03515       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03516       ast_odbc_release_obj(obj);
03517    } else
03518       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03519 yuck: 
03520    return x;
03521 }
03522 
03523 /*!
03524  * \brief returns the one-based count for messages.
03525  * \param vmu
03526  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03527  *
03528  * This method is used when mailboxes are stored in an ODBC back end.
03529  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03530  * one-based messages.
03531  * This method just calls last_message_index and returns +1 of its value.
03532  *
03533  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03534  */
03535 static int count_messages(struct ast_vm_user *vmu, char *dir)
03536 {
03537    return last_message_index(vmu, dir) + 1;
03538 }
03539 
03540 /*!
03541  * \brief Deletes a message from the mailbox folder.
03542  * \param sdir The mailbox folder to work in.
03543  * \param smsg The message index to be deleted.
03544  *
03545  * This method is used when mailboxes are stored in an ODBC back end.
03546  * The specified message is directly deleted from the database 'voicemessages' table.
03547  * 
03548  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03549  */
03550 static void delete_file(const char *sdir, int smsg)
03551 {
03552    SQLHSTMT stmt;
03553    char sql[PATH_MAX];
03554    char msgnums[20];
03555    char *argv[] = { NULL, msgnums };
03556    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03557    struct odbc_obj *obj;
03558 
03559    argv[0] = ast_strdupa(sdir);
03560 
03561    obj = ast_odbc_request_obj(odbc_database, 0);
03562    if (obj) {
03563       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03564       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03565       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03566       if (!stmt)
03567          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03568       else
03569          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03570       ast_odbc_release_obj(obj);
03571    } else
03572       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03573    return;  
03574 }
03575 
03576 /*!
03577  * \brief Copies a voicemail from one mailbox to another.
03578  * \param sdir the folder for which to look for the message to be copied.
03579  * \param smsg the index of the message to be copied.
03580  * \param ddir the destination folder to copy the message into.
03581  * \param dmsg the index to be used for the copied message.
03582  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03583  * \param dmailboxcontext The context for the destination user.
03584  *
03585  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03586  */
03587 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03588 {
03589    SQLHSTMT stmt;
03590    char sql[512];
03591    char msgnums[20];
03592    char msgnumd[20];
03593    struct odbc_obj *obj;
03594    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03595    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03596 
03597    delete_file(ddir, dmsg);
03598    obj = ast_odbc_request_obj(odbc_database, 0);
03599    if (obj) {
03600       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03601       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03602       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);
03603       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03604       if (!stmt)
03605          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03606       else
03607          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03608       ast_odbc_release_obj(obj);
03609    } else
03610       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03611    return;  
03612 }
03613 
03614 struct insert_data {
03615    char *sql;
03616    const char *dir;
03617    const char *msgnums;
03618    void *data;
03619    SQLLEN datalen;
03620    SQLLEN indlen;
03621    const char *context;
03622    const char *macrocontext;
03623    const char *callerid;
03624    const char *origtime;
03625    const char *duration;
03626    const char *mailboxuser;
03627    const char *mailboxcontext;
03628    const char *category;
03629    const char *flag;
03630 };
03631 
03632 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03633 {
03634    struct insert_data *data = vdata;
03635    int res;
03636    SQLHSTMT stmt;
03637 
03638    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03639    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03640       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03641       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03642       return NULL;
03643    }
03644 
03645    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03646    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03647    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03648    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03649    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03650    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03651    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03652    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03653    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03654    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03655    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03656    if (!ast_strlen_zero(data->category)) {
03657       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03658    }
03659    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03660    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03661       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03662       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03663       return NULL;
03664    }
03665 
03666    return stmt;
03667 }
03668 
03669 /*!
03670  * \brief Stores a voicemail into the database.
03671  * \param dir the folder the mailbox folder to store the message.
03672  * \param mailboxuser the user owning the mailbox folder.
03673  * \param mailboxcontext
03674  * \param msgnum the message index for the message to be stored.
03675  *
03676  * This method is used when mailboxes are stored in an ODBC back end.
03677  * The message sound file and information file is looked up on the file system. 
03678  * A SQL query is invoked to store the message into the (MySQL) database.
03679  *
03680  * \return the zero on success -1 on error.
03681  */
03682 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03683 {
03684    int res = 0;
03685    int fd = -1;
03686    void *fdm = MAP_FAILED;
03687    size_t fdlen = -1;
03688    SQLHSTMT stmt;
03689    char sql[PATH_MAX];
03690    char msgnums[20];
03691    char fn[PATH_MAX];
03692    char full_fn[PATH_MAX];
03693    char fmt[80]="";
03694    char *c;
03695    struct ast_config *cfg = NULL;
03696    struct odbc_obj *obj;
03697    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03698       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03699    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03700 
03701    delete_file(dir, msgnum);
03702    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03703       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03704       return -1;
03705    }
03706 
03707    do {
03708       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03709       c = strchr(fmt, '|');
03710       if (c)
03711          *c = '\0';
03712       if (!strcasecmp(fmt, "wav49"))
03713          strcpy(fmt, "WAV");
03714       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03715       if (msgnum > -1)
03716          make_file(fn, sizeof(fn), dir, msgnum);
03717       else
03718          ast_copy_string(fn, dir, sizeof(fn));
03719       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03720       cfg = ast_config_load(full_fn, config_flags);
03721       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03722       fd = open(full_fn, O_RDWR);
03723       if (fd < 0) {
03724          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03725          res = -1;
03726          break;
03727       }
03728       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03729          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03730             idata.context = "";
03731          }
03732          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03733             idata.macrocontext = "";
03734          }
03735          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03736             idata.callerid = "";
03737          }
03738          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03739             idata.origtime = "";
03740          }
03741          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03742             idata.duration = "";
03743          }
03744          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03745             idata.category = "";
03746          }
03747          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03748             idata.flag = "";
03749          }
03750       }
03751       fdlen = lseek(fd, 0, SEEK_END);
03752       lseek(fd, 0, SEEK_SET);
03753       printf("Length is %zd\n", fdlen);
03754       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03755       if (fdm == MAP_FAILED) {
03756          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03757          res = -1;
03758          break;
03759       } 
03760       idata.data = fdm;
03761       idata.datalen = idata.indlen = fdlen;
03762 
03763       if (!ast_strlen_zero(idata.category)) 
03764          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03765       else
03766          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03767 
03768       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03769          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03770       } else {
03771          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03772          res = -1;
03773       }
03774    } while (0);
03775    if (obj) {
03776       ast_odbc_release_obj(obj);
03777    }
03778    if (cfg)
03779       ast_config_destroy(cfg);
03780    if (fdm != MAP_FAILED)
03781       munmap(fdm, fdlen);
03782    if (fd > -1)
03783       close(fd);
03784    return res;
03785 }
03786 
03787 /*!
03788  * \brief Renames a message in a mailbox folder.
03789  * \param sdir The folder of the message to be renamed.
03790  * \param smsg The index of the message to be renamed.
03791  * \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.
03792  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03793  * \param ddir The destination folder for the message to be renamed into
03794  * \param dmsg The destination message for the message to be renamed.
03795  *
03796  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03797  * 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.
03798  * 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.
03799  */
03800 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03801 {
03802    SQLHSTMT stmt;
03803    char sql[PATH_MAX];
03804    char msgnums[20];
03805    char msgnumd[20];
03806    struct odbc_obj *obj;
03807    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03808    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03809 
03810    delete_file(ddir, dmsg);
03811    obj = ast_odbc_request_obj(odbc_database, 0);
03812    if (obj) {
03813       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03814       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03815       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03816       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03817       if (!stmt)
03818          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03819       else
03820          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03821       ast_odbc_release_obj(obj);
03822    } else
03823       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03824    return;  
03825 }
03826 
03827 /*!
03828  * \brief Removes a voicemail message file.
03829  * \param dir the path to the message file.
03830  * \param msgnum the unique number for the message within the mailbox.
03831  *
03832  * Removes the message content file and the information file.
03833  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03834  * Typical use is to clean up after a RETRIEVE operation. 
03835  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03836  * \return zero on success, -1 on error.
03837  */
03838 static int remove_file(char *dir, int msgnum)
03839 {
03840    char fn[PATH_MAX];
03841    char full_fn[PATH_MAX];
03842    char msgnums[80];
03843    
03844    if (msgnum > -1) {
03845       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03846       make_file(fn, sizeof(fn), dir, msgnum);
03847    } else
03848       ast_copy_string(fn, dir, sizeof(fn));
03849    ast_filedelete(fn, NULL);  
03850    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03851    unlink(full_fn);
03852    return 0;
03853 }
03854 #else
03855 #ifndef IMAP_STORAGE
03856 /*!
03857  * \brief Find all .txt files - even if they are not in sequence from 0000.
03858  * \param vmu
03859  * \param dir
03860  *
03861  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03862  *
03863  * \return the count of messages, zero or more.
03864  */
03865 static int count_messages(struct ast_vm_user *vmu, char *dir)
03866 {
03867 
03868    int vmcount = 0;
03869    DIR *vmdir = NULL;
03870    struct dirent *vment = NULL;
03871 
03872    if (vm_lock_path(dir))
03873       return ERROR_LOCK_PATH;
03874 
03875    if ((vmdir = opendir(dir))) {
03876       while ((vment = readdir(vmdir))) {
03877          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03878             vmcount++;
03879          }
03880       }
03881       closedir(vmdir);
03882    }
03883    ast_unlock_path(dir);
03884    
03885    return vmcount;
03886 }
03887 
03888 /*!
03889  * \brief Renames a message in a mailbox folder.
03890  * \param sfn The path to the mailbox information and data file to be renamed.
03891  * \param dfn The path for where the message data and information files will be renamed to.
03892  *
03893  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03894  */
03895 static void rename_file(char *sfn, char *dfn)
03896 {
03897    char stxt[PATH_MAX];
03898    char dtxt[PATH_MAX];
03899    ast_filerename(sfn, dfn, NULL);
03900    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03901    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03902    if (ast_check_realtime("voicemail_data")) {
03903       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03904    }
03905    rename(stxt, dtxt);
03906 }
03907 
03908 /*! 
03909  * \brief Determines the highest message number in use for a given user and mailbox folder.
03910  * \param vmu 
03911  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03912  *
03913  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03914  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03915  *
03916  * \note Should always be called with a lock already set on dir.
03917  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03918  */
03919 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03920 {
03921    int x;
03922    unsigned char map[MAXMSGLIMIT] = "";
03923    DIR *msgdir;
03924    struct dirent *msgdirent;
03925    int msgdirint;
03926 
03927    /* Reading the entire directory into a file map scales better than
03928     * doing a stat repeatedly on a predicted sequence.  I suspect this
03929     * is partially due to stat(2) internally doing a readdir(2) itself to
03930     * find each file. */
03931    if (!(msgdir = opendir(dir))) {
03932       return -1;
03933    }
03934 
03935    while ((msgdirent = readdir(msgdir))) {
03936       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03937          map[msgdirint] = 1;
03938    }
03939    closedir(msgdir);
03940 
03941    for (x = 0; x < vmu->maxmsg; x++) {
03942       if (map[x] == 0)
03943          break;
03944    }
03945 
03946    return x - 1;
03947 }
03948 
03949 #endif /* #ifndef IMAP_STORAGE */
03950 #endif /* #else of #ifdef ODBC_STORAGE */
03951 #ifndef IMAP_STORAGE
03952 /*!
03953  * \brief Utility function to copy a file.
03954  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03955  * \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.
03956  *
03957  * 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.
03958  * The copy operation copies up to 4096 bytes at once.
03959  *
03960  * \return zero on success, -1 on error.
03961  */
03962 static int copy(char *infile, char *outfile)
03963 {
03964    int ifd;
03965    int ofd;
03966    int res;
03967    int len;
03968    char buf[4096];
03969 
03970 #ifdef HARDLINK_WHEN_POSSIBLE
03971    /* Hard link if possible; saves disk space & is faster */
03972    if (link(infile, outfile)) {
03973 #endif
03974       if ((ifd = open(infile, O_RDONLY)) < 0) {
03975          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03976          return -1;
03977       }
03978       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03979          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03980          close(ifd);
03981          return -1;
03982       }
03983       do {
03984          len = read(ifd, buf, sizeof(buf));
03985          if (len < 0) {
03986             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03987             close(ifd);
03988             close(ofd);
03989             unlink(outfile);
03990          }
03991          if (len) {
03992             res = write(ofd, buf, len);
03993             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03994                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03995                close(ifd);
03996                close(ofd);
03997                unlink(outfile);
03998             }
03999          }
04000       } while (len);
04001       close(ifd);
04002       close(ofd);
04003       return 0;
04004 #ifdef HARDLINK_WHEN_POSSIBLE
04005    } else {
04006       /* Hard link succeeded */
04007       return 0;
04008    }
04009 #endif
04010 }
04011 
04012 /*!
04013  * \brief Copies a voicemail information (envelope) file.
04014  * \param frompath
04015  * \param topath 
04016  *
04017  * Every voicemail has the data (.wav) file, and the information file.
04018  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04019  * This is used by the COPY macro when not using IMAP storage.
04020  */
04021 static void copy_plain_file(char *frompath, char *topath)
04022 {
04023    char frompath2[PATH_MAX], topath2[PATH_MAX];
04024    struct ast_variable *tmp,*var = NULL;
04025    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04026    ast_filecopy(frompath, topath, NULL);
04027    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04028    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04029    if (ast_check_realtime("voicemail_data")) {
04030       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04031       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04032       for (tmp = var; tmp; tmp = tmp->next) {
04033          if (!strcasecmp(tmp->name, "origmailbox")) {
04034             origmailbox = tmp->value;
04035          } else if (!strcasecmp(tmp->name, "context")) {
04036             context = tmp->value;
04037          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04038             macrocontext = tmp->value;
04039          } else if (!strcasecmp(tmp->name, "exten")) {
04040             exten = tmp->value;
04041          } else if (!strcasecmp(tmp->name, "priority")) {
04042             priority = tmp->value;
04043          } else if (!strcasecmp(tmp->name, "callerchan")) {
04044             callerchan = tmp->value;
04045          } else if (!strcasecmp(tmp->name, "callerid")) {
04046             callerid = tmp->value;
04047          } else if (!strcasecmp(tmp->name, "origdate")) {
04048             origdate = tmp->value;
04049          } else if (!strcasecmp(tmp->name, "origtime")) {
04050             origtime = tmp->value;
04051          } else if (!strcasecmp(tmp->name, "category")) {
04052             category = tmp->value;
04053          } else if (!strcasecmp(tmp->name, "duration")) {
04054             duration = tmp->value;
04055          }
04056       }
04057       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);
04058    }
04059    copy(frompath2, topath2);
04060    ast_variables_destroy(var);
04061 }
04062 #endif
04063 
04064 /*! 
04065  * \brief Removes the voicemail sound and information file.
04066  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04067  *
04068  * This is used by the DELETE macro when voicemails are stored on the file system.
04069  *
04070  * \return zero on success, -1 on error.
04071  */
04072 static int vm_delete(char *file)
04073 {
04074    char *txt;
04075    int txtsize = 0;
04076 
04077    txtsize = (strlen(file) + 5)*sizeof(char);
04078    txt = alloca(txtsize);
04079    /* Sprintf here would safe because we alloca'd exactly the right length,
04080     * but trying to eliminate all sprintf's anyhow
04081     */
04082    if (ast_check_realtime("voicemail_data")) {
04083       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04084    }
04085    snprintf(txt, txtsize, "%s.txt", file);
04086    unlink(txt);
04087    return ast_filedelete(file, NULL);
04088 }
04089 
04090 /*!
04091  * \brief utility used by inchar(), for base_encode()
04092  */
04093 static int inbuf(struct baseio *bio, FILE *fi)
04094 {
04095    int l;
04096 
04097    if (bio->ateof)
04098       return 0;
04099 
04100    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04101       if (ferror(fi))
04102          return -1;
04103 
04104       bio->ateof = 1;
04105       return 0;
04106    }
04107 
04108    bio->iolen = l;
04109    bio->iocp = 0;
04110 
04111    return 1;
04112 }
04113 
04114 /*!
04115  * \brief utility used by base_encode()
04116  */
04117 static int inchar(struct baseio *bio, FILE *fi)
04118 {
04119    if (bio->iocp>=bio->iolen) {
04120       if (!inbuf(bio, fi))
04121          return EOF;
04122    }
04123 
04124    return bio->iobuf[bio->iocp++];
04125 }
04126 
04127 /*!
04128  * \brief utility used by base_encode()
04129  */
04130 static int ochar(struct baseio *bio, int c, FILE *so)
04131 {
04132    if (bio->linelength >= BASELINELEN) {
04133       if (fputs(ENDL, so) == EOF) {
04134          return -1;
04135       }
04136 
04137       bio->linelength = 0;
04138    }
04139 
04140    if (putc(((unsigned char) c), so) == EOF) {
04141       return -1;
04142    }
04143 
04144    bio->linelength++;
04145 
04146    return 1;
04147 }
04148 
04149 /*!
04150  * \brief Performs a base 64 encode algorithm on the contents of a File
04151  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04152  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04153  *
04154  * 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 ?
04155  *
04156  * \return zero on success, -1 on error.
04157  */
04158 static int base_encode(char *filename, FILE *so)
04159 {
04160    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04161       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04162       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04163       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04164    int i, hiteof = 0;
04165    FILE *fi;
04166    struct baseio bio;
04167 
04168    memset(&bio, 0, sizeof(bio));
04169    bio.iocp = BASEMAXINLINE;
04170 
04171    if (!(fi = fopen(filename, "rb"))) {
04172       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04173       return -1;
04174    }
04175 
04176    while (!hiteof){
04177       unsigned char igroup[3], ogroup[4];
04178       int c, n;
04179 
04180       memset(igroup, 0, sizeof(igroup));
04181 
04182       for (n = 0; n < 3; n++) {
04183          if ((c = inchar(&bio, fi)) == EOF) {
04184             hiteof = 1;
04185             break;
04186          }
04187 
04188          igroup[n] = (unsigned char) c;
04189       }
04190 
04191       if (n > 0) {
04192          ogroup[0]= dtable[igroup[0] >> 2];
04193          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04194          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04195          ogroup[3]= dtable[igroup[2] & 0x3F];
04196 
04197          if (n < 3) {
04198             ogroup[3] = '=';
04199 
04200             if (n < 2)
04201                ogroup[2] = '=';
04202          }
04203 
04204          for (i = 0; i < 4; i++)
04205             ochar(&bio, ogroup[i], so);
04206       }
04207    }
04208 
04209    fclose(fi);
04210    
04211    if (fputs(ENDL, so) == EOF) {
04212       return 0;
04213    }
04214 
04215    return 1;
04216 }
04217 
04218 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)
04219 {
04220    char callerid[256];
04221    char num[12];
04222    char fromdir[256], fromfile[256];
04223    struct ast_config *msg_cfg;
04224    const char *origcallerid, *origtime;
04225    char origcidname[80], origcidnum[80], origdate[80];
04226    int inttime;
04227    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04228 
04229    /* Prepare variables for substitution in email body and subject */
04230    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04231    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04232    snprintf(num, sizeof(num), "%d", msgnum);
04233    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04234    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04235    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04236    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04237       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04238    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04239    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04240    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04241    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04242    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04243 
04244    /* Retrieve info from VM attribute file */
04245    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04246    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04247    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04248       strcat(fromfile, ".txt");
04249    }
04250    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04251       if (option_debug > 0) {
04252          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04253       }
04254       return;
04255    }
04256 
04257    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04258       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04259       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04260       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04261       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04262    }
04263 
04264    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04265       struct timeval tv = { inttime, };
04266       struct ast_tm tm;
04267       ast_localtime(&tv, &tm, NULL);
04268       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04269       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04270    }
04271    ast_config_destroy(msg_cfg);
04272 }
04273 
04274 /*!
04275  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04276  * \param from The string to work with.
04277  * \param buf The buffer into which to write the modified quoted string.
04278  * \param maxlen Always zero, but see \see ast_str
04279  * 
04280  * \return The destination string with quotes wrapped on it (the to field).
04281  */
04282 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04283 {
04284    const char *ptr;
04285 
04286    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04287    ast_str_set(buf, maxlen, "\"");
04288    for (ptr = from; *ptr; ptr++) {
04289       if (*ptr == '"' || *ptr == '\\') {
04290          ast_str_append(buf, maxlen, "\\%c", *ptr);
04291       } else {
04292          ast_str_append(buf, maxlen, "%c", *ptr);
04293       }
04294    }
04295    ast_str_append(buf, maxlen, "\"");
04296 
04297    return ast_str_buffer(*buf);
04298 }
04299 
04300 /*! \brief
04301  * fill in *tm for current time according to the proper timezone, if any.
04302  * \return tm so it can be used as a function argument.
04303  */
04304 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04305 {
04306    const struct vm_zone *z = NULL;
04307    struct timeval t = ast_tvnow();
04308 
04309    /* Does this user have a timezone specified? */
04310    if (!ast_strlen_zero(vmu->zonetag)) {
04311       /* Find the zone in the list */
04312       AST_LIST_LOCK(&zones);
04313       AST_LIST_TRAVERSE(&zones, z, list) {
04314          if (!strcmp(z->name, vmu->zonetag))
04315             break;
04316       }
04317       AST_LIST_UNLOCK(&zones);
04318    }
04319    ast_localtime(&t, tm, z ? z->timezone : NULL);
04320    return tm;
04321 }
04322 
04323 /*!\brief Check if the string would need encoding within the MIME standard, to
04324  * avoid confusing certain mail software that expects messages to be 7-bit
04325  * clean.
04326  */
04327 static int check_mime(const char *str)
04328 {
04329    for (; *str; str++) {
04330       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04331          return 1;
04332       }
04333    }
04334    return 0;
04335 }
04336 
04337 /*!\brief Encode a string according to the MIME rules for encoding strings
04338  * that are not 7-bit clean or contain control characters.
04339  *
04340  * Additionally, if the encoded string would exceed the MIME limit of 76
04341  * characters per line, then the encoding will be broken up into multiple
04342  * sections, separated by a space character, in order to facilitate
04343  * breaking up the associated header across multiple lines.
04344  *
04345  * \param end An expandable buffer for holding the result
04346  * \param maxlen Always zero, but see \see ast_str
04347  * \param start A string to be encoded
04348  * \param preamble The length of the first line already used for this string,
04349  * to ensure that each line maintains a maximum length of 76 chars.
04350  * \param postamble the length of any additional characters appended to the
04351  * line, used to ensure proper field wrapping.
04352  * \retval The encoded string.
04353  */
04354 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04355 {
04356    struct ast_str *tmp = ast_str_alloca(80);
04357    int first_section = 1;
04358 
04359    ast_str_reset(*end);
04360    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04361    for (; *start; start++) {
04362       int need_encoding = 0;
04363       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04364          need_encoding = 1;
04365       }
04366       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04367          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04368          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04369          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04370          /* Start new line */
04371          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04372          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04373          first_section = 0;
04374       }
04375       if (need_encoding && *start == ' ') {
04376          ast_str_append(&tmp, -1, "_");
04377       } else if (need_encoding) {
04378          ast_str_append(&tmp, -1, "=%hhX", *start);
04379       } else {
04380          ast_str_append(&tmp, -1, "%c", *start);
04381       }
04382    }
04383    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04384    return ast_str_buffer(*end);
04385 }
04386 
04387 /*!
04388  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04389  * \param p The output file to generate the email contents into.
04390  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04391  * \param vmu The voicemail user who is sending the voicemail.
04392  * \param msgnum The message index in the mailbox folder.
04393  * \param context 
04394  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04395  * \param fromfolder
04396  * \param cidnum The caller ID number.
04397  * \param cidname The caller ID name.
04398  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04399  * \param attach2 
04400  * \param format The message sound file format. i.e. .wav
04401  * \param duration The time of the message content, in seconds.
04402  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04403  * \param chan
04404  * \param category
04405  * \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.
04406  * \param flag
04407  *
04408  * 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.
04409  */
04410 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)
04411 {
04412    char date[256];
04413    char host[MAXHOSTNAMELEN] = "";
04414    char who[256];
04415    char bound[256];
04416    char dur[256];
04417    struct ast_tm tm;
04418    char enc_cidnum[256] = "", enc_cidname[256] = "";
04419    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04420    char *greeting_attachment; 
04421    char filename[256];
04422 
04423    if (!str1 || !str2) {
04424       ast_free(str1);
04425       ast_free(str2);
04426       return;
04427    }
04428 
04429    if (cidnum) {
04430       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04431    }
04432    if (cidname) {
04433       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04434    }
04435    gethostname(host, sizeof(host) - 1);
04436 
04437    if (strchr(srcemail, '@')) {
04438       ast_copy_string(who, srcemail, sizeof(who));
04439    } else {
04440       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04441    }
04442 
04443    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04444    if (greeting_attachment) {
04445       *greeting_attachment++ = '\0';
04446    }
04447 
04448    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04449    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04450    fprintf(p, "Date: %s" ENDL, date);
04451 
04452    /* Set date format for voicemail mail */
04453    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04454 
04455    if (!ast_strlen_zero(fromstring)) {
04456       struct ast_channel *ast;
04457       if ((ast = ast_dummy_channel_alloc())) {
04458          char *ptr;
04459          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04460          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04461 
04462          if (check_mime(ast_str_buffer(str1))) {
04463             int first_line = 1;
04464             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04465             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04466                *ptr = '\0';
04467                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04468                first_line = 0;
04469                /* Substring is smaller, so this will never grow */
04470                ast_str_set(&str2, 0, "%s", ptr + 1);
04471             }
04472             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04473          } else {
04474             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04475          }
04476          ast = ast_channel_release(ast);
04477       } else {
04478          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04479       }
04480    } else {
04481       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04482    }
04483 
04484    if (check_mime(vmu->fullname)) {
04485       int first_line = 1;
04486       char *ptr;
04487       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04488       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04489          *ptr = '\0';
04490          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04491          first_line = 0;
04492          /* Substring is smaller, so this will never grow */
04493          ast_str_set(&str2, 0, "%s", ptr + 1);
04494       }
04495       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04496    } else {
04497       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04498    }
04499 
04500    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04501       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04502       struct ast_channel *ast;
04503       if ((ast = ast_dummy_channel_alloc())) {
04504          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04505          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04506          if (check_mime(ast_str_buffer(str1))) {
04507             int first_line = 1;
04508             char *ptr;
04509             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04510             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04511                *ptr = '\0';
04512                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04513                first_line = 0;
04514                /* Substring is smaller, so this will never grow */
04515                ast_str_set(&str2, 0, "%s", ptr + 1);
04516             }
04517             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04518          } else {
04519             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04520          }
04521          ast = ast_channel_release(ast);
04522       } else {
04523          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04524       }
04525    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04526       if (ast_strlen_zero(flag)) {
04527          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04528       } else {
04529          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04530       }
04531    } else {
04532       if (ast_strlen_zero(flag)) {
04533          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04534       } else {
04535          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04536       }
04537    }
04538 
04539    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04540       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04541    if (imap) {
04542       /* additional information needed for IMAP searching */
04543       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04544       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04545       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04546       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04547 #ifdef IMAP_STORAGE
04548       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04549 #else
04550       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04551 #endif
04552       /* flag added for Urgent */
04553       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04554       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04555       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04556       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04557       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04558       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04559       if (!ast_strlen_zero(category)) {
04560          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04561       } else {
04562          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04563       }
04564       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04565       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04566       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04567    }
04568    if (!ast_strlen_zero(cidnum)) {
04569       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04570    }
04571    if (!ast_strlen_zero(cidname)) {
04572       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04573    }
04574    fprintf(p, "MIME-Version: 1.0" ENDL);
04575    if (attach_user_voicemail) {
04576       /* Something unique. */
04577       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04578          (int) getpid(), (unsigned int) ast_random());
04579 
04580       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04581       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04582       fprintf(p, "--%s" ENDL, bound);
04583    }
04584    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04585    if (emailbody || vmu->emailbody) {
04586       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04587       struct ast_channel *ast;
04588       if ((ast = ast_dummy_channel_alloc())) {
04589          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04590          ast_str_substitute_variables(&str1, 0, ast, e_body);
04591 #ifdef IMAP_STORAGE
04592             {
04593                /* Convert body to native line terminators for IMAP backend */
04594                char *line = ast_str_buffer(str1), *next;
04595                do {
04596                   /* Terminate line before outputting it to the file */
04597                   if ((next = strchr(line, '\n'))) {
04598                      *next++ = '\0';
04599                   }
04600                   fprintf(p, "%s" ENDL, line);
04601                   line = next;
04602                } while (!ast_strlen_zero(line));
04603             }
04604 #else
04605          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04606 #endif
04607          ast = ast_channel_release(ast);
04608       } else {
04609          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04610       }
04611    } else if (msgnum > -1) {
04612       if (strcmp(vmu->mailbox, mailbox)) {
04613          /* Forwarded type */
04614          struct ast_config *msg_cfg;
04615          const char *v;
04616          int inttime;
04617          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04618          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04619          /* Retrieve info from VM attribute file */
04620          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04621          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04622          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04623             strcat(fromfile, ".txt");
04624          }
04625          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04626             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04627                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04628             }
04629 
04630             /* You might be tempted to do origdate, except that a) it's in the wrong
04631              * format, and b) it's missing for IMAP recordings. */
04632             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04633                struct timeval tv = { inttime, };
04634                struct ast_tm tm;
04635                ast_localtime(&tv, &tm, NULL);
04636                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04637             }
04638             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04639                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04640                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04641                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04642                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04643                date, origcallerid, origdate);
04644             ast_config_destroy(msg_cfg);
04645          } else {
04646             goto plain_message;
04647          }
04648       } else {
04649 plain_message:
04650          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04651             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04652             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04653             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04654             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04655       }
04656    } else {
04657       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04658             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04659    }
04660 
04661    if (imap || attach_user_voicemail) {
04662       if (!ast_strlen_zero(attach2)) {
04663          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04664          ast_debug(5, "creating second attachment filename %s\n", filename);
04665          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04666          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04667          ast_debug(5, "creating attachment filename %s\n", filename);
04668          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04669       } else {
04670          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04671          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04672          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04673       }
04674    }
04675    ast_free(str1);
04676    ast_free(str2);
04677 }
04678 
04679 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)
04680 {
04681    char tmpdir[256], newtmp[256];
04682    char fname[256];
04683    char tmpcmd[256];
04684    int tmpfd = -1;
04685 
04686    /* Eww. We want formats to tell us their own MIME type */
04687    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04688 
04689    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04690       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04691       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04692       tmpfd = mkstemp(newtmp);
04693       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04694       ast_debug(3, "newtmp: %s\n", newtmp);
04695       if (tmpfd > -1) {
04696          int soxstatus;
04697          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04698          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04699             attach = newtmp;
04700             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04701          } else {
04702             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04703                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04704             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04705          }
04706       }
04707    }
04708    fprintf(p, "--%s" ENDL, bound);
04709    if (msgnum > -1)
04710       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04711    else
04712       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04713    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04714    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04715    if (msgnum > -1)
04716       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04717    else
04718       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04719    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04720    base_encode(fname, p);
04721    if (last)
04722       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04723    if (tmpfd > -1) {
04724       unlink(fname);
04725       close(tmpfd);
04726       unlink(newtmp);
04727    }
04728    return 0;
04729 }
04730 
04731 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)
04732 {
04733    FILE *p = NULL;
04734    char tmp[80] = "/tmp/astmail-XXXXXX";
04735    char tmp2[256];
04736    char *stringp;
04737 
04738    if (vmu && ast_strlen_zero(vmu->email)) {
04739       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04740       return(0);
04741    }
04742 
04743    /* Mail only the first format */
04744    format = ast_strdupa(format);
04745    stringp = format;
04746    strsep(&stringp, "|");
04747 
04748    if (!strcmp(format, "wav49"))
04749       format = "WAV";
04750    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));
04751    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04752       command hangs */
04753    if ((p = vm_mkftemp(tmp)) == NULL) {
04754       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04755       return -1;
04756    } else {
04757       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04758       fclose(p);
04759       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04760       ast_safe_system(tmp2);
04761       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04762    }
04763    return 0;
04764 }
04765 
04766 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)
04767 {
04768    char enc_cidnum[256], enc_cidname[256];
04769    char date[256];
04770    char host[MAXHOSTNAMELEN] = "";
04771    char who[256];
04772    char dur[PATH_MAX];
04773    char tmp[80] = "/tmp/astmail-XXXXXX";
04774    char tmp2[PATH_MAX];
04775    struct ast_tm tm;
04776    FILE *p;
04777    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04778 
04779    if (!str1 || !str2) {
04780       ast_free(str1);
04781       ast_free(str2);
04782       return -1;
04783    }
04784 
04785    if (cidnum) {
04786       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04787    }
04788    if (cidname) {
04789       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04790    }
04791 
04792    if ((p = vm_mkftemp(tmp)) == NULL) {
04793       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04794       ast_free(str1);
04795       ast_free(str2);
04796       return -1;
04797    }
04798    gethostname(host, sizeof(host)-1);
04799    if (strchr(srcemail, '@')) {
04800       ast_copy_string(who, srcemail, sizeof(who));
04801    } else {
04802       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04803    }
04804    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04805    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04806    fprintf(p, "Date: %s\n", date);
04807 
04808    /* Reformat for custom pager format */
04809    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04810 
04811    if (!ast_strlen_zero(pagerfromstring)) {
04812       struct ast_channel *ast;
04813       if ((ast = ast_dummy_channel_alloc())) {
04814          char *ptr;
04815          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04816          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04817 
04818          if (check_mime(ast_str_buffer(str1))) {
04819             int first_line = 1;
04820             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04821             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04822                *ptr = '\0';
04823                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04824                first_line = 0;
04825                /* Substring is smaller, so this will never grow */
04826                ast_str_set(&str2, 0, "%s", ptr + 1);
04827             }
04828             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04829          } else {
04830             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04831          }
04832          ast = ast_channel_release(ast);
04833       } else {
04834          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04835       }
04836    } else {
04837       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04838    }
04839 
04840    if (check_mime(vmu->fullname)) {
04841       int first_line = 1;
04842       char *ptr;
04843       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04844       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04845          *ptr = '\0';
04846          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04847          first_line = 0;
04848          /* Substring is smaller, so this will never grow */
04849          ast_str_set(&str2, 0, "%s", ptr + 1);
04850       }
04851       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04852    } else {
04853       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04854    }
04855 
04856    if (!ast_strlen_zero(pagersubject)) {
04857       struct ast_channel *ast;
04858       if ((ast = ast_dummy_channel_alloc())) {
04859          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04860          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04861          if (check_mime(ast_str_buffer(str1))) {
04862             int first_line = 1;
04863             char *ptr;
04864             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04865             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04866                *ptr = '\0';
04867                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04868                first_line = 0;
04869                /* Substring is smaller, so this will never grow */
04870                ast_str_set(&str2, 0, "%s", ptr + 1);
04871             }
04872             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04873          } else {
04874             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04875          }
04876          ast = ast_channel_release(ast);
04877       } else {
04878          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04879       }
04880    } else {
04881       if (ast_strlen_zero(flag)) {
04882          fprintf(p, "Subject: New VM\n\n");
04883       } else {
04884          fprintf(p, "Subject: New %s VM\n\n", flag);
04885       }
04886    }
04887 
04888    if (pagerbody) {
04889       struct ast_channel *ast;
04890       if ((ast = ast_dummy_channel_alloc())) {
04891          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04892          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04893          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04894          ast = ast_channel_release(ast);
04895       } else {
04896          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04897       }
04898    } else {
04899       fprintf(p, "New %s long %s msg in box %s\n"
04900             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04901    }
04902 
04903    fclose(p);
04904    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04905    ast_safe_system(tmp2);
04906    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04907    ast_free(str1);
04908    ast_free(str2);
04909    return 0;
04910 }
04911 
04912 /*!
04913  * \brief Gets the current date and time, as formatted string.
04914  * \param s The buffer to hold the output formatted date.
04915  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04916  * 
04917  * The date format string used is "%a %b %e %r UTC %Y".
04918  * 
04919  * \return zero on success, -1 on error.
04920  */
04921 static int get_date(char *s, int len)
04922 {
04923    struct ast_tm tm;
04924    struct timeval t = ast_tvnow();
04925    
04926    ast_localtime(&t, &tm, "UTC");
04927 
04928    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04929 }
04930 
04931 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04932 {
04933    int res;
04934    char fn[PATH_MAX];
04935    char dest[PATH_MAX];
04936 
04937    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04938 
04939    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04940       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04941       return -1;
04942    }
04943 
04944    RETRIEVE(fn, -1, ext, context);
04945    if (ast_fileexists(fn, NULL, NULL) > 0) {
04946       res = ast_stream_and_wait(chan, fn, ecodes);
04947       if (res) {
04948          DISPOSE(fn, -1);
04949          return res;
04950       }
04951    } else {
04952       /* Dispose just in case */
04953       DISPOSE(fn, -1);
04954       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04955       if (res)
04956          return res;
04957       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04958       if (res)
04959          return res;
04960    }
04961    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04962    return res;
04963 }
04964 
04965 static void free_zone(struct vm_zone *z)
04966 {
04967    ast_free(z);
04968 }
04969 
04970 #ifdef ODBC_STORAGE
04971 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04972 {
04973    int x = -1;
04974    int res;
04975    SQLHSTMT stmt = NULL;
04976    char sql[PATH_MAX];
04977    char rowdata[20];
04978    char tmp[PATH_MAX] = "";
04979    struct odbc_obj *obj = NULL;
04980    char *context;
04981    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04982 
04983    if (newmsgs)
04984       *newmsgs = 0;
04985    if (oldmsgs)
04986       *oldmsgs = 0;
04987    if (urgentmsgs)
04988       *urgentmsgs = 0;
04989 
04990    /* If no mailbox, return immediately */
04991    if (ast_strlen_zero(mailbox))
04992       return 0;
04993 
04994    ast_copy_string(tmp, mailbox, sizeof(tmp));
04995 
04996    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04997       int u, n, o;
04998       char *next, *remaining = tmp;
04999       while ((next = strsep(&remaining, " ,"))) {
05000          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05001             return -1;
05002          }
05003          if (urgentmsgs) {
05004             *urgentmsgs += u;
05005          }
05006          if (newmsgs) {
05007             *newmsgs += n;
05008          }
05009          if (oldmsgs) {
05010             *oldmsgs += o;
05011          }
05012       }
05013       return 0;
05014    }
05015 
05016    context = strchr(tmp, '@');
05017    if (context) {
05018       *context = '\0';
05019       context++;
05020    } else
05021       context = "default";
05022 
05023    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05024       do {
05025          if (newmsgs) {
05026             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05027             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05028                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05029                break;
05030             }
05031             res = SQLFetch(stmt);
05032             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05033                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05034                break;
05035             }
05036             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05037             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05038                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05039                break;
05040             }
05041             *newmsgs = atoi(rowdata);
05042             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05043          }
05044 
05045          if (oldmsgs) {
05046             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05047             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05048                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05049                break;
05050             }
05051             res = SQLFetch(stmt);
05052             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05053                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05054                break;
05055             }
05056             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05057             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05058                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05059                break;
05060             }
05061             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05062             *oldmsgs = atoi(rowdata);
05063          }
05064 
05065          if (urgentmsgs) {
05066             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05067             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05068                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05069                break;
05070             }
05071             res = SQLFetch(stmt);
05072             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05073                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05074                break;
05075             }
05076             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05077             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05078                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05079                break;
05080             }
05081             *urgentmsgs = atoi(rowdata);
05082          }
05083 
05084          x = 0;
05085       } while (0);
05086    } else {
05087       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05088    }
05089 
05090    if (stmt) {
05091       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05092    }
05093    if (obj) {
05094       ast_odbc_release_obj(obj);
05095    }
05096 
05097    return x;
05098 }
05099 
05100 /*!
05101  * \brief Gets the number of messages that exist in a mailbox folder.
05102  * \param context
05103  * \param mailbox
05104  * \param folder
05105  * 
05106  * This method is used when ODBC backend is used.
05107  * \return The number of messages in this mailbox folder (zero or more).
05108  */
05109 static int messagecount(const char *context, const char *mailbox, const char *folder)
05110 {
05111    struct odbc_obj *obj = NULL;
05112    int nummsgs = 0;
05113    int res;
05114    SQLHSTMT stmt = NULL;
05115    char sql[PATH_MAX];
05116    char rowdata[20];
05117    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05118    if (!folder)
05119       folder = "INBOX";
05120    /* If no mailbox, return immediately */
05121    if (ast_strlen_zero(mailbox))
05122       return 0;
05123 
05124    obj = ast_odbc_request_obj(odbc_database, 0);
05125    if (obj) {
05126       if (!strcmp(folder, "INBOX")) {
05127          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);
05128       } else {
05129          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05130       }
05131       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05132       if (!stmt) {
05133          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05134          goto yuck;
05135       }
05136       res = SQLFetch(stmt);
05137       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05138          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05139          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05140          goto yuck;
05141       }
05142       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05143       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05144          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05145          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05146          goto yuck;
05147       }
05148       nummsgs = atoi(rowdata);
05149       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05150    } else
05151       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05152 
05153 yuck:
05154    if (obj)
05155       ast_odbc_release_obj(obj);
05156    return nummsgs;
05157 }
05158 
05159 /** 
05160  * \brief Determines if the given folder has messages.
05161  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05162  * 
05163  * This function is used when the mailbox is stored in an ODBC back end.
05164  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05165  * \return 1 if the folder has one or more messages. zero otherwise.
05166  */
05167 static int has_voicemail(const char *mailbox, const char *folder)
05168 {
05169    char tmp[256], *tmp2 = tmp, *box, *context;
05170    ast_copy_string(tmp, mailbox, sizeof(tmp));
05171    while ((context = box = strsep(&tmp2, ",&"))) {
05172       strsep(&context, "@");
05173       if (ast_strlen_zero(context))
05174          context = "default";
05175       if (messagecount(context, box, folder))
05176          return 1;
05177    }
05178    return 0;
05179 }
05180 #endif
05181 #ifndef IMAP_STORAGE
05182 /*! 
05183  * \brief Copies a message from one mailbox to another.
05184  * \param chan
05185  * \param vmu
05186  * \param imbox
05187  * \param msgnum
05188  * \param duration
05189  * \param recip
05190  * \param fmt
05191  * \param dir
05192  * \param flag
05193  *
05194  * This is only used by file storage based mailboxes.
05195  *
05196  * \return zero on success, -1 on error.
05197  */
05198 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)
05199 {
05200    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05201    const char *frombox = mbox(vmu, imbox);
05202    int recipmsgnum;
05203    int res = 0;
05204 
05205    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05206 
05207    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05208       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05209    } else {
05210       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05211    }
05212    
05213    if (!dir)
05214       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05215    else
05216       ast_copy_string(fromdir, dir, sizeof(fromdir));
05217 
05218    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05219    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05220 
05221    if (vm_lock_path(todir))
05222       return ERROR_LOCK_PATH;
05223 
05224    recipmsgnum = last_message_index(recip, todir) + 1;
05225    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05226       make_file(topath, sizeof(topath), todir, recipmsgnum);
05227 #ifndef ODBC_STORAGE
05228       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05229          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05230       } else {
05231 #endif
05232          /* If we are prepending a message for ODBC, then the message already
05233           * exists in the database, but we want to force copying from the
05234           * filesystem (since only the FS contains the prepend). */
05235          copy_plain_file(frompath, topath);
05236          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05237          vm_delete(topath);
05238 #ifndef ODBC_STORAGE
05239       }
05240 #endif
05241    } else {
05242       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05243       res = -1;
05244    }
05245    ast_unlock_path(todir);
05246    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05247       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05248       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05249       flag);
05250    
05251    return res;
05252 }
05253 #endif
05254 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05255 
05256 static int messagecount(const char *context, const char *mailbox, const char *folder)
05257 {
05258    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05259 }
05260 
05261 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05262 {
05263    DIR *dir;
05264    struct dirent *de;
05265    char fn[256];
05266    int ret = 0;
05267 
05268    /* If no mailbox, return immediately */
05269    if (ast_strlen_zero(mailbox))
05270       return 0;
05271 
05272    if (ast_strlen_zero(folder))
05273       folder = "INBOX";
05274    if (ast_strlen_zero(context))
05275       context = "default";
05276 
05277    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05278 
05279    if (!(dir = opendir(fn)))
05280       return 0;
05281 
05282    while ((de = readdir(dir))) {
05283       if (!strncasecmp(de->d_name, "msg", 3)) {
05284          if (shortcircuit) {
05285             ret = 1;
05286             break;
05287          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05288             ret++;
05289          }
05290       }
05291    }
05292 
05293    closedir(dir);
05294 
05295    return ret;
05296 }
05297 
05298 /** 
05299  * \brief Determines if the given folder has messages.
05300  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05301  * \param folder the folder to look in
05302  *
05303  * This function is used when the mailbox is stored in a filesystem back end.
05304  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05305  * \return 1 if the folder has one or more messages. zero otherwise.
05306  */
05307 static int has_voicemail(const char *mailbox, const char *folder)
05308 {
05309    char tmp[256], *tmp2 = tmp, *box, *context;
05310    ast_copy_string(tmp, mailbox, sizeof(tmp));
05311    if (ast_strlen_zero(folder)) {
05312       folder = "INBOX";
05313    }
05314    while ((box = strsep(&tmp2, ",&"))) {
05315       if ((context = strchr(box, '@')))
05316          *context++ = '\0';
05317       else
05318          context = "default";
05319       if (__has_voicemail(context, box, folder, 1))
05320          return 1;
05321       /* If we are checking INBOX, we should check Urgent as well */
05322       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05323          return 1;
05324       }
05325    }
05326    return 0;
05327 }
05328 
05329 
05330 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05331 {
05332    char tmp[256];
05333    char *context;
05334 
05335    /* If no mailbox, return immediately */
05336    if (ast_strlen_zero(mailbox))
05337       return 0;
05338 
05339    if (newmsgs)
05340       *newmsgs = 0;
05341    if (oldmsgs)
05342       *oldmsgs = 0;
05343    if (urgentmsgs)
05344       *urgentmsgs = 0;
05345 
05346    if (strchr(mailbox, ',')) {
05347       int tmpnew, tmpold, tmpurgent;
05348       char *mb, *cur;
05349 
05350       ast_copy_string(tmp, mailbox, sizeof(tmp));
05351       mb = tmp;
05352       while ((cur = strsep(&mb, ", "))) {
05353          if (!ast_strlen_zero(cur)) {
05354             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05355                return -1;
05356             else {
05357                if (newmsgs)
05358                   *newmsgs += tmpnew; 
05359                if (oldmsgs)
05360                   *oldmsgs += tmpold;
05361                if (urgentmsgs)
05362                   *urgentmsgs += tmpurgent;
05363             }
05364          }
05365       }
05366       return 0;
05367    }
05368 
05369    ast_copy_string(tmp, mailbox, sizeof(tmp));
05370    
05371    if ((context = strchr(tmp, '@')))
05372       *context++ = '\0';
05373    else
05374       context = "default";
05375 
05376    if (newmsgs)
05377       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05378    if (oldmsgs)
05379       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05380    if (urgentmsgs)
05381       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05382 
05383    return 0;
05384 }
05385 
05386 #endif
05387 
05388 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05389 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05390 {
05391    int urgentmsgs = 0;
05392    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05393    if (newmsgs) {
05394       *newmsgs += urgentmsgs;
05395    }
05396    return res;
05397 }
05398 
05399 static void run_externnotify(char *context, char *extension, const char *flag)
05400 {
05401    char arguments[255];
05402    char ext_context[256] = "";
05403    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05404    struct ast_smdi_mwi_message *mwi_msg;
05405 
05406    if (!ast_strlen_zero(context))
05407       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05408    else
05409       ast_copy_string(ext_context, extension, sizeof(ext_context));
05410 
05411    if (smdi_iface) {
05412       if (ast_app_has_voicemail(ext_context, NULL)) 
05413          ast_smdi_mwi_set(smdi_iface, extension);
05414       else
05415          ast_smdi_mwi_unset(smdi_iface, extension);
05416 
05417       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05418          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05419          if (!strncmp(mwi_msg->cause, "INV", 3))
05420             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05421          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05422             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05423          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05424          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05425       } else {
05426          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05427       }
05428    }
05429 
05430    if (!ast_strlen_zero(externnotify)) {
05431       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05432          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05433       } else {
05434          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05435          ast_debug(1, "Executing %s\n", arguments);
05436          ast_safe_system(arguments);
05437       }
05438    }
05439 }
05440 
05441 /*!
05442  * \brief Variables used for saving a voicemail.
05443  *
05444  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05445  */
05446 struct leave_vm_options {
05447    unsigned int flags;
05448    signed char record_gain;
05449    char *exitcontext;
05450 };
05451 
05452 /*!
05453  * \brief Prompts the user and records a voicemail to a mailbox.
05454  * \param chan
05455  * \param ext
05456  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05457  * 
05458  * 
05459  * 
05460  * \return zero on success, -1 on error.
05461  */
05462 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05463 {
05464 #ifdef IMAP_STORAGE
05465    int newmsgs, oldmsgs;
05466 #else
05467    char urgdir[PATH_MAX];
05468 #endif
05469    char txtfile[PATH_MAX];
05470    char tmptxtfile[PATH_MAX];
05471    struct vm_state *vms = NULL;
05472    char callerid[256];
05473    FILE *txt;
05474    char date[256];
05475    int txtdes;
05476    int res = 0;
05477    int msgnum;
05478    int duration = 0;
05479    int ausemacro = 0;
05480    int ousemacro = 0;
05481    int ouseexten = 0;
05482    char tmpdur[16];
05483    char priority[16];
05484    char origtime[16];
05485    char dir[PATH_MAX];
05486    char tmpdir[PATH_MAX];
05487    char fn[PATH_MAX];
05488    char prefile[PATH_MAX] = "";
05489    char tempfile[PATH_MAX] = "";
05490    char ext_context[256] = "";
05491    char fmt[80];
05492    char *context;
05493    char ecodes[17] = "#";
05494    struct ast_str *tmp = ast_str_create(16);
05495    char *tmpptr;
05496    struct ast_vm_user *vmu;
05497    struct ast_vm_user svm;
05498    const char *category = NULL;
05499    const char *code;
05500    const char *alldtmf = "0123456789ABCD*#";
05501    char flag[80];
05502 
05503    if (!tmp) {
05504       return -1;
05505    }
05506 
05507    ast_str_set(&tmp, 0, "%s", ext);
05508    ext = ast_str_buffer(tmp);
05509    if ((context = strchr(ext, '@'))) {
05510       *context++ = '\0';
05511       tmpptr = strchr(context, '&');
05512    } else {
05513       tmpptr = strchr(ext, '&');
05514    }
05515 
05516    if (tmpptr)
05517       *tmpptr++ = '\0';
05518 
05519    ast_channel_lock(chan);
05520    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05521       category = ast_strdupa(category);
05522    }
05523    ast_channel_unlock(chan);
05524 
05525    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05526       ast_copy_string(flag, "Urgent", sizeof(flag));
05527    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05528       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05529    } else {
05530       flag[0] = '\0';
05531    }
05532 
05533    ast_debug(3, "Before find_user\n");
05534    if (!(vmu = find_user(&svm, context, ext))) {
05535       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05536       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05537       ast_free(tmp);
05538       return res;
05539    }
05540    /* Setup pre-file if appropriate */
05541    if (strcmp(vmu->context, "default"))
05542       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05543    else
05544       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05545 
05546    /* Set the path to the prefile. Will be one of 
05547       VM_SPOOL_DIRcontext/ext/busy
05548       VM_SPOOL_DIRcontext/ext/unavail
05549       Depending on the flag set in options.
05550    */
05551    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05552       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05553    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05554       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05555    }
05556    /* Set the path to the tmpfile as
05557       VM_SPOOL_DIR/context/ext/temp
05558       and attempt to create the folder structure.
05559    */
05560    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05561    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05562       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05563       ast_free(tmp);
05564       return -1;
05565    }
05566    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05567    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05568       ast_copy_string(prefile, tempfile, sizeof(prefile));
05569 
05570    DISPOSE(tempfile, -1);
05571    /* It's easier just to try to make it than to check for its existence */
05572 #ifndef IMAP_STORAGE
05573    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05574 #else
05575    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05576    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05577       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05578    }
05579 #endif
05580 
05581    /* Check current or macro-calling context for special extensions */
05582    if (ast_test_flag(vmu, VM_OPERATOR)) {
05583       if (!ast_strlen_zero(vmu->exit)) {
05584          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05585             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05586             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05587             ouseexten = 1;
05588          }
05589       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05590          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05591          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05592          ouseexten = 1;
05593       } else if (!ast_strlen_zero(chan->macrocontext)
05594          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05595             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05596          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05597          ousemacro = 1;
05598       }
05599    }
05600 
05601    if (!ast_strlen_zero(vmu->exit)) {
05602       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05603          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05604          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05605       }
05606    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05607       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05608       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05609    } else if (!ast_strlen_zero(chan->macrocontext)
05610       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05611          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05612       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05613       ausemacro = 1;
05614    }
05615 
05616    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05617       for (code = alldtmf; *code; code++) {
05618          char e[2] = "";
05619          e[0] = *code;
05620          if (strchr(ecodes, e[0]) == NULL
05621             && ast_canmatch_extension(chan, chan->context, e, 1,
05622                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05623             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05624          }
05625       }
05626    }
05627 
05628    /* Play the beginning intro if desired */
05629    if (!ast_strlen_zero(prefile)) {
05630 #ifdef ODBC_STORAGE
05631       int success = 
05632 #endif
05633          RETRIEVE(prefile, -1, ext, context);
05634       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05635          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05636             res = ast_waitstream(chan, ecodes);
05637 #ifdef ODBC_STORAGE
05638          if (success == -1) {
05639             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05640             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05641             store_file(prefile, vmu->mailbox, vmu->context, -1);
05642          }
05643 #endif
05644       } else {
05645          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05646          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05647       }
05648       DISPOSE(prefile, -1);
05649       if (res < 0) {
05650          ast_debug(1, "Hang up during prefile playback\n");
05651          free_user(vmu);
05652          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05653          ast_free(tmp);
05654          return -1;
05655       }
05656    }
05657    if (res == '#') {
05658       /* On a '#' we skip the instructions */
05659       ast_set_flag(options, OPT_SILENT);
05660       res = 0;
05661    }
05662    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05663    if (vmu->maxmsg == 0) {
05664       if (option_debug > 2)
05665          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05666       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05667       goto leave_vm_out;
05668    }
05669    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05670       res = ast_stream_and_wait(chan, INTRO, ecodes);
05671       if (res == '#') {
05672          ast_set_flag(options, OPT_SILENT);
05673          res = 0;
05674       }
05675    }
05676    if (res > 0)
05677       ast_stopstream(chan);
05678    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05679     other than the operator -- an automated attendant or mailbox login for example */
05680    if (res == '*') {
05681       chan->exten[0] = 'a';
05682       chan->exten[1] = '\0';
05683       if (!ast_strlen_zero(vmu->exit)) {
05684          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05685       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05686          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05687       }
05688       chan->priority = 0;
05689       free_user(vmu);
05690       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05691       ast_free(tmp);
05692       return 0;
05693    }
05694 
05695    /* Check for a '0' here */
05696    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05697    transfer:
05698       if (ouseexten || ousemacro) {
05699          chan->exten[0] = 'o';
05700          chan->exten[1] = '\0';
05701          if (!ast_strlen_zero(vmu->exit)) {
05702             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05703          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05704             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05705          }
05706          ast_play_and_wait(chan, "transfer");
05707          chan->priority = 0;
05708          free_user(vmu);
05709          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05710       }
05711       ast_free(tmp);
05712       return OPERATOR_EXIT;
05713    }
05714 
05715    /* Allow all other digits to exit Voicemail and return to the dialplan */
05716    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05717       if (!ast_strlen_zero(options->exitcontext))
05718          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05719       free_user(vmu);
05720       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05721       ast_free(tmp);
05722       return res;
05723    }
05724 
05725    if (res < 0) {
05726       free_user(vmu);
05727       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05728       ast_free(tmp);
05729       return -1;
05730    }
05731    /* The meat of recording the message...  All the announcements and beeps have been played*/
05732    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05733    if (!ast_strlen_zero(fmt)) {
05734       msgnum = 0;
05735 
05736 #ifdef IMAP_STORAGE
05737       /* Is ext a mailbox? */
05738       /* must open stream for this user to get info! */
05739       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05740       if (res < 0) {
05741          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05742          ast_free(tmp);
05743          return -1;
05744       }
05745       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05746       /* It is possible under certain circumstances that inboxcount did not
05747        * create a vm_state when it was needed. This is a catchall which will
05748        * rarely be used.
05749        */
05750          if (!(vms = create_vm_state_from_user(vmu))) {
05751             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05752             ast_free(tmp);
05753             return -1;
05754          }
05755       }
05756       vms->newmessages++;
05757       
05758       /* here is a big difference! We add one to it later */
05759       msgnum = newmsgs + oldmsgs;
05760       ast_debug(3, "Messagecount set to %d\n", msgnum);
05761       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05762       /* set variable for compatibility */
05763       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05764 
05765       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05766          goto leave_vm_out;
05767       }
05768 #else
05769       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05770          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05771          if (!res)
05772             res = ast_waitstream(chan, "");
05773          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05774          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05775          inprocess_count(vmu->mailbox, vmu->context, -1);
05776          goto leave_vm_out;
05777       }
05778 
05779 #endif
05780       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05781       txtdes = mkstemp(tmptxtfile);
05782       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05783       if (txtdes < 0) {
05784          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05785          if (!res)
05786             res = ast_waitstream(chan, "");
05787          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05788          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05789          inprocess_count(vmu->mailbox, vmu->context, -1);
05790          goto leave_vm_out;
05791       }
05792 
05793       /* Now play the beep once we have the message number for our next message. */
05794       if (res >= 0) {
05795          /* Unless we're *really* silent, try to send the beep */
05796          res = ast_stream_and_wait(chan, "beep", "");
05797       }
05798             
05799       /* Store information in real-time storage */
05800       if (ast_check_realtime("voicemail_data")) {
05801          snprintf(priority, sizeof(priority), "%d", chan->priority);
05802          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05803          get_date(date, sizeof(date));
05804          ast_callerid_merge(callerid, sizeof(callerid),
05805             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05806             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05807             "Unknown");
05808          ast_store_realtime("voicemail_data",
05809             "origmailbox", ext,
05810             "context", chan->context,
05811             "macrocontext", chan->macrocontext,
05812             "exten", chan->exten,
05813             "priority", priority,
05814             "callerchan", chan->name,
05815             "callerid", callerid,
05816             "origdate", date,
05817             "origtime", origtime,
05818             "category", S_OR(category, ""),
05819             "filename", tmptxtfile,
05820             SENTINEL);
05821       }
05822 
05823       /* Store information */
05824       txt = fdopen(txtdes, "w+");
05825       if (txt) {
05826          get_date(date, sizeof(date));
05827          ast_callerid_merge(callerid, sizeof(callerid),
05828             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05829             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05830             "Unknown");
05831          fprintf(txt, 
05832             ";\n"
05833             "; Message Information file\n"
05834             ";\n"
05835             "[message]\n"
05836             "origmailbox=%s\n"
05837             "context=%s\n"
05838             "macrocontext=%s\n"
05839             "exten=%s\n"
05840             "rdnis=%s\n"
05841             "priority=%d\n"
05842             "callerchan=%s\n"
05843             "callerid=%s\n"
05844             "origdate=%s\n"
05845             "origtime=%ld\n"
05846             "category=%s\n",
05847             ext,
05848             chan->context,
05849             chan->macrocontext, 
05850             chan->exten,
05851             S_COR(chan->redirecting.from.number.valid,
05852                chan->redirecting.from.number.str, "unknown"),
05853             chan->priority,
05854             chan->name,
05855             callerid,
05856             date, (long) time(NULL),
05857             category ? category : "");
05858       } else {
05859          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05860          inprocess_count(vmu->mailbox, vmu->context, -1);
05861          if (ast_check_realtime("voicemail_data")) {
05862             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05863          }
05864          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05865          goto leave_vm_out;
05866       }
05867       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05868 
05869       if (txt) {
05870          fprintf(txt, "flag=%s\n", flag);
05871          if (duration < vmu->minsecs) {
05872             fclose(txt);
05873             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05874             ast_filedelete(tmptxtfile, NULL);
05875             unlink(tmptxtfile);
05876             if (ast_check_realtime("voicemail_data")) {
05877                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05878             }
05879             inprocess_count(vmu->mailbox, vmu->context, -1);
05880          } else {
05881             fprintf(txt, "duration=%d\n", duration);
05882             fclose(txt);
05883             if (vm_lock_path(dir)) {
05884                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05885                /* Delete files */
05886                ast_filedelete(tmptxtfile, NULL);
05887                unlink(tmptxtfile);
05888                inprocess_count(vmu->mailbox, vmu->context, -1);
05889             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05890                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05891                unlink(tmptxtfile);
05892                ast_unlock_path(dir);
05893                inprocess_count(vmu->mailbox, vmu->context, -1);
05894                if (ast_check_realtime("voicemail_data")) {
05895                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05896                }
05897             } else {
05898 #ifndef IMAP_STORAGE
05899                msgnum = last_message_index(vmu, dir) + 1;
05900 #endif
05901                make_file(fn, sizeof(fn), dir, msgnum);
05902 
05903                /* assign a variable with the name of the voicemail file */ 
05904 #ifndef IMAP_STORAGE
05905                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05906 #else
05907                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05908 #endif
05909 
05910                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05911                ast_filerename(tmptxtfile, fn, NULL);
05912                rename(tmptxtfile, txtfile);
05913                inprocess_count(vmu->mailbox, vmu->context, -1);
05914 
05915                /* Properly set permissions on voicemail text descriptor file.
05916                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05917                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05918                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05919 
05920                ast_unlock_path(dir);
05921                if (ast_check_realtime("voicemail_data")) {
05922                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05923                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05924                }
05925                /* We must store the file first, before copying the message, because
05926                 * ODBC storage does the entire copy with SQL.
05927                 */
05928                if (ast_fileexists(fn, NULL, NULL) > 0) {
05929                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05930                }
05931 
05932                /* Are there to be more recipients of this message? */
05933                while (tmpptr) {
05934                   struct ast_vm_user recipu, *recip;
05935                   char *exten, *cntx;
05936                
05937                   exten = strsep(&tmpptr, "&");
05938                   cntx = strchr(exten, '@');
05939                   if (cntx) {
05940                      *cntx = '\0';
05941                      cntx++;
05942                   }
05943                   if ((recip = find_user(&recipu, cntx, exten))) {
05944                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05945                      free_user(recip);
05946                   }
05947                }
05948 #ifndef IMAP_STORAGE
05949                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05950                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05951                   char sfn[PATH_MAX];
05952                   char dfn[PATH_MAX];
05953                   int x;
05954                   /* It's easier just to try to make it than to check for its existence */
05955                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05956                   x = last_message_index(vmu, urgdir) + 1;
05957                   make_file(sfn, sizeof(sfn), dir, msgnum);
05958                   make_file(dfn, sizeof(dfn), urgdir, x);
05959                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05960                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05961                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05962                   ast_copy_string(fn, dfn, sizeof(fn));
05963                   msgnum = x;
05964                }
05965 #endif
05966                /* Notification needs to happen after the copy, though. */
05967                if (ast_fileexists(fn, NULL, NULL)) {
05968 #ifdef IMAP_STORAGE
05969                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
05970                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05971                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05972                      flag);
05973 #else
05974                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
05975                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05976                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05977                      flag);
05978 #endif
05979                }
05980 
05981                /* Disposal needs to happen after the optional move and copy */
05982                if (ast_fileexists(fn, NULL, NULL)) {
05983                   DISPOSE(dir, msgnum);
05984                }
05985             }
05986          }
05987       } else {
05988          inprocess_count(vmu->mailbox, vmu->context, -1);
05989       }
05990       if (res == '0') {
05991          goto transfer;
05992       } else if (res > 0 && res != 't')
05993          res = 0;
05994 
05995       if (duration < vmu->minsecs)
05996          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05997          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05998       else
05999          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06000    } else
06001       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06002 leave_vm_out:
06003    free_user(vmu);
06004 
06005 #ifdef IMAP_STORAGE
06006    /* expunge message - use UID Expunge if supported on IMAP server*/
06007    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06008    if (expungeonhangup == 1) {
06009       ast_mutex_lock(&vms->lock);
06010 #ifdef HAVE_IMAP_TK2006
06011       if (LEVELUIDPLUS (vms->mailstream)) {
06012          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06013       } else 
06014 #endif
06015          mail_expunge(vms->mailstream);
06016       ast_mutex_unlock(&vms->lock);
06017    }
06018 #endif
06019 
06020    ast_free(tmp);
06021    return res;
06022 }
06023 
06024 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06025 {
06026    int d;
06027    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06028    return d;
06029 }
06030 
06031 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06032 {
06033 #ifdef IMAP_STORAGE
06034    /* we must use mbox(x) folder names, and copy the message there */
06035    /* simple. huh? */
06036    char sequence[10];
06037    char mailbox[256];
06038    int res;
06039 
06040    /* get the real IMAP message number for this message */
06041    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06042    
06043    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06044    ast_mutex_lock(&vms->lock);
06045    /* if save to Old folder, put in INBOX as read */
06046    if (box == OLD_FOLDER) {
06047       mail_setflag(vms->mailstream, sequence, "\\Seen");
06048       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06049    } else if (box == NEW_FOLDER) {
06050       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06051       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06052    }
06053    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06054       ast_mutex_unlock(&vms->lock);
06055       return 0;
06056    }
06057    /* Create the folder if it don't exist */
06058    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06059    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06060    if (mail_create(vms->mailstream, mailbox) == NIL) 
06061       ast_debug(5, "Folder exists.\n");
06062    else
06063       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06064    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06065    ast_mutex_unlock(&vms->lock);
06066    return res;
06067 #else
06068    char *dir = vms->curdir;
06069    char *username = vms->username;
06070    char *context = vmu->context;
06071    char sfn[PATH_MAX];
06072    char dfn[PATH_MAX];
06073    char ddir[PATH_MAX];
06074    const char *dbox = mbox(vmu, box);
06075    int x, i;
06076    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06077 
06078    if (vm_lock_path(ddir))
06079       return ERROR_LOCK_PATH;
06080 
06081    x = last_message_index(vmu, ddir) + 1;
06082 
06083    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06084       x--;
06085       for (i = 1; i <= x; i++) {
06086          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06087          make_file(sfn, sizeof(sfn), ddir, i);
06088          make_file(dfn, sizeof(dfn), ddir, i - 1);
06089          if (EXISTS(ddir, i, sfn, NULL)) {
06090             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06091          } else
06092             break;
06093       }
06094    } else {
06095       if (x >= vmu->maxmsg) {
06096          ast_unlock_path(ddir);
06097          return -1;
06098       }
06099    }
06100    make_file(sfn, sizeof(sfn), dir, msg);
06101    make_file(dfn, sizeof(dfn), ddir, x);
06102    if (strcmp(sfn, dfn)) {
06103       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06104    }
06105    ast_unlock_path(ddir);
06106 #endif
06107    return 0;
06108 }
06109 
06110 static int adsi_logo(unsigned char *buf)
06111 {
06112    int bytes = 0;
06113    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06114    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06115    return bytes;
06116 }
06117 
06118 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06119 {
06120    unsigned char buf[256];
06121    int bytes = 0;
06122    int x;
06123    char num[5];
06124 
06125    *useadsi = 0;
06126    bytes += ast_adsi_data_mode(buf + bytes);
06127    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06128 
06129    bytes = 0;
06130    bytes += adsi_logo(buf);
06131    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06132 #ifdef DISPLAY
06133    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06134 #endif
06135    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06136    bytes += ast_adsi_data_mode(buf + bytes);
06137    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06138 
06139    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06140       bytes = 0;
06141       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06142       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06143       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06144       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06145       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06146       return 0;
06147    }
06148 
06149 #ifdef DISPLAY
06150    /* Add a dot */
06151    bytes = 0;
06152    bytes += ast_adsi_logo(buf);
06153    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06154    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06155    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06156    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06157 #endif
06158    bytes = 0;
06159    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06160    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06161    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06162    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06163    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06164    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06165    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06166 
06167 #ifdef DISPLAY
06168    /* Add another dot */
06169    bytes = 0;
06170    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06171    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06172 
06173    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06174    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06175 #endif
06176 
06177    bytes = 0;
06178    /* These buttons we load but don't use yet */
06179    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06180    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06181    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06182    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06183    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06184    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06185    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06186 
06187 #ifdef DISPLAY
06188    /* Add another dot */
06189    bytes = 0;
06190    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06191    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06192    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06193 #endif
06194 
06195    bytes = 0;
06196    for (x = 0; x < 5; x++) {
06197       snprintf(num, sizeof(num), "%d", x);
06198       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06199    }
06200    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06201    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06202 
06203 #ifdef DISPLAY
06204    /* Add another dot */
06205    bytes = 0;
06206    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06207    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06208    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06209 #endif
06210 
06211    if (ast_adsi_end_download(chan)) {
06212       bytes = 0;
06213       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06214       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06215       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06216       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06217       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06218       return 0;
06219    }
06220    bytes = 0;
06221    bytes += ast_adsi_download_disconnect(buf + bytes);
06222    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06223    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06224 
06225    ast_debug(1, "Done downloading scripts...\n");
06226 
06227 #ifdef DISPLAY
06228    /* Add last dot */
06229    bytes = 0;
06230    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06231    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06232 #endif
06233    ast_debug(1, "Restarting session...\n");
06234 
06235    bytes = 0;
06236    /* Load the session now */
06237    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06238       *useadsi = 1;
06239       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06240    } else
06241       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06242 
06243    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06244    return 0;
06245 }
06246 
06247 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06248 {
06249    int x;
06250    if (!ast_adsi_available(chan))
06251       return;
06252    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06253    if (x < 0)
06254       return;
06255    if (!x) {
06256       if (adsi_load_vmail(chan, useadsi)) {
06257          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06258          return;
06259       }
06260    } else
06261       *useadsi = 1;
06262 }
06263 
06264 static void adsi_login(struct ast_channel *chan)
06265 {
06266    unsigned char buf[256];
06267    int bytes = 0;
06268    unsigned char keys[8];
06269    int x;
06270    if (!ast_adsi_available(chan))
06271       return;
06272 
06273    for (x = 0; x < 8; x++)
06274       keys[x] = 0;
06275    /* Set one key for next */
06276    keys[3] = ADSI_KEY_APPS + 3;
06277 
06278    bytes += adsi_logo(buf + bytes);
06279    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06280    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06281    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06282    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06283    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06284    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06285    bytes += ast_adsi_set_keys(buf + bytes, keys);
06286    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06287    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06288 }
06289 
06290 static void adsi_password(struct ast_channel *chan)
06291 {
06292    unsigned char buf[256];
06293    int bytes = 0;
06294    unsigned char keys[8];
06295    int x;
06296    if (!ast_adsi_available(chan))
06297       return;
06298 
06299    for (x = 0; x < 8; x++)
06300       keys[x] = 0;
06301    /* Set one key for next */
06302    keys[3] = ADSI_KEY_APPS + 3;
06303 
06304    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06305    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06306    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06307    bytes += ast_adsi_set_keys(buf + bytes, keys);
06308    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06309    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06310 }
06311 
06312 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06313 {
06314    unsigned char buf[256];
06315    int bytes = 0;
06316    unsigned char keys[8];
06317    int x, y;
06318 
06319    if (!ast_adsi_available(chan))
06320       return;
06321 
06322    for (x = 0; x < 5; x++) {
06323       y = ADSI_KEY_APPS + 12 + start + x;
06324       if (y > ADSI_KEY_APPS + 12 + 4)
06325          y = 0;
06326       keys[x] = ADSI_KEY_SKT | y;
06327    }
06328    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06329    keys[6] = 0;
06330    keys[7] = 0;
06331 
06332    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06333    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06334    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06335    bytes += ast_adsi_set_keys(buf + bytes, keys);
06336    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06337 
06338    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06339 }
06340 
06341 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06342 {
06343    int bytes = 0;
06344    unsigned char buf[256]; 
06345    char buf1[256], buf2[256];
06346    char fn2[PATH_MAX];
06347 
06348    char cid[256] = "";
06349    char *val;
06350    char *name, *num;
06351    char datetime[21] = "";
06352    FILE *f;
06353 
06354    unsigned char keys[8];
06355 
06356    int x;
06357 
06358    if (!ast_adsi_available(chan))
06359       return;
06360 
06361    /* Retrieve important info */
06362    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06363    f = fopen(fn2, "r");
06364    if (f) {
06365       while (!feof(f)) {   
06366          if (!fgets((char *) buf, sizeof(buf), f)) {
06367             continue;
06368          }
06369          if (!feof(f)) {
06370             char *stringp = NULL;
06371             stringp = (char *) buf;
06372             strsep(&stringp, "=");
06373             val = strsep(&stringp, "=");
06374             if (!ast_strlen_zero(val)) {
06375                if (!strcmp((char *) buf, "callerid"))
06376                   ast_copy_string(cid, val, sizeof(cid));
06377                if (!strcmp((char *) buf, "origdate"))
06378                   ast_copy_string(datetime, val, sizeof(datetime));
06379             }
06380          }
06381       }
06382       fclose(f);
06383    }
06384    /* New meaning for keys */
06385    for (x = 0; x < 5; x++)
06386       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06387    keys[6] = 0x0;
06388    keys[7] = 0x0;
06389 
06390    if (!vms->curmsg) {
06391       /* No prev key, provide "Folder" instead */
06392       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06393    }
06394    if (vms->curmsg >= vms->lastmsg) {
06395       /* If last message ... */
06396       if (vms->curmsg) {
06397          /* but not only message, provide "Folder" instead */
06398          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06399          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06400 
06401       } else {
06402          /* Otherwise if only message, leave blank */
06403          keys[3] = 1;
06404       }
06405    }
06406 
06407    if (!ast_strlen_zero(cid)) {
06408       ast_callerid_parse(cid, &name, &num);
06409       if (!name)
06410          name = num;
06411    } else
06412       name = "Unknown Caller";
06413 
06414    /* If deleted, show "undeleted" */
06415 
06416    if (vms->deleted[vms->curmsg])
06417       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06418 
06419    /* Except "Exit" */
06420    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06421    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06422       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06423    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06424 
06425    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06426    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06427    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06428    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06429    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06430    bytes += ast_adsi_set_keys(buf + bytes, keys);
06431    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06432 
06433    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06434 }
06435 
06436 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06437 {
06438    int bytes = 0;
06439    unsigned char buf[256];
06440    unsigned char keys[8];
06441 
06442    int x;
06443 
06444    if (!ast_adsi_available(chan))
06445       return;
06446 
06447    /* New meaning for keys */
06448    for (x = 0; x < 5; x++)
06449       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06450 
06451    keys[6] = 0x0;
06452    keys[7] = 0x0;
06453 
06454    if (!vms->curmsg) {
06455       /* No prev key, provide "Folder" instead */
06456       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06457    }
06458    if (vms->curmsg >= vms->lastmsg) {
06459       /* If last message ... */
06460       if (vms->curmsg) {
06461          /* but not only message, provide "Folder" instead */
06462          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06463       } else {
06464          /* Otherwise if only message, leave blank */
06465          keys[3] = 1;
06466       }
06467    }
06468 
06469    /* If deleted, show "undeleted" */
06470    if (vms->deleted[vms->curmsg]) 
06471       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06472 
06473    /* Except "Exit" */
06474    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06475    bytes += ast_adsi_set_keys(buf + bytes, keys);
06476    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06477 
06478    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06479 }
06480 
06481 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06482 {
06483    unsigned char buf[256] = "";
06484    char buf1[256] = "", buf2[256] = "";
06485    int bytes = 0;
06486    unsigned char keys[8];
06487    int x;
06488 
06489    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06490    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06491    if (!ast_adsi_available(chan))
06492       return;
06493    if (vms->newmessages) {
06494       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06495       if (vms->oldmessages) {
06496          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06497          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06498       } else {
06499          snprintf(buf2, sizeof(buf2), "%s.", newm);
06500       }
06501    } else if (vms->oldmessages) {
06502       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06503       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06504    } else {
06505       strcpy(buf1, "You have no messages.");
06506       buf2[0] = ' ';
06507       buf2[1] = '\0';
06508    }
06509    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06510    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06511    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06512 
06513    for (x = 0; x < 6; x++)
06514       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06515    keys[6] = 0;
06516    keys[7] = 0;
06517 
06518    /* Don't let them listen if there are none */
06519    if (vms->lastmsg < 0)
06520       keys[0] = 1;
06521    bytes += ast_adsi_set_keys(buf + bytes, keys);
06522 
06523    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06524 
06525    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06526 }
06527 
06528 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06529 {
06530    unsigned char buf[256] = "";
06531    char buf1[256] = "", buf2[256] = "";
06532    int bytes = 0;
06533    unsigned char keys[8];
06534    int x;
06535 
06536    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06537 
06538    if (!ast_adsi_available(chan))
06539       return;
06540 
06541    /* Original command keys */
06542    for (x = 0; x < 6; x++)
06543       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06544 
06545    keys[6] = 0;
06546    keys[7] = 0;
06547 
06548    if ((vms->lastmsg + 1) < 1)
06549       keys[0] = 0;
06550 
06551    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06552       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06553 
06554    if (vms->lastmsg + 1)
06555       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06556    else
06557       strcpy(buf2, "no messages.");
06558    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06559    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06560    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06561    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06562    bytes += ast_adsi_set_keys(buf + bytes, keys);
06563 
06564    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06565 
06566    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06567    
06568 }
06569 
06570 /*
06571 static void adsi_clear(struct ast_channel *chan)
06572 {
06573    char buf[256];
06574    int bytes=0;
06575    if (!ast_adsi_available(chan))
06576       return;
06577    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06578    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06579 
06580    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06581 }
06582 */
06583 
06584 static void adsi_goodbye(struct ast_channel *chan)
06585 {
06586    unsigned char buf[256];
06587    int bytes = 0;
06588 
06589    if (!ast_adsi_available(chan))
06590       return;
06591    bytes += adsi_logo(buf + bytes);
06592    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06593    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06594    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06595    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06596 
06597    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06598 }
06599 
06600 /*!\brief get_folder: Folder menu
06601  * Plays "press 1 for INBOX messages" etc.
06602  * Should possibly be internationalized
06603  */
06604 static int get_folder(struct ast_channel *chan, int start)
06605 {
06606    int x;
06607    int d;
06608    char fn[PATH_MAX];
06609    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06610    if (d)
06611       return d;
06612    for (x = start; x < 5; x++) { /* For all folders */
06613       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06614          return d;
06615       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06616       if (d)
06617          return d;
06618       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06619       d = vm_play_folder_name(chan, fn);
06620       if (d)
06621          return d;
06622       d = ast_waitfordigit(chan, 500);
06623       if (d)
06624          return d;
06625    }
06626    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06627    if (d)
06628       return d;
06629    d = ast_waitfordigit(chan, 4000);
06630    return d;
06631 }
06632 
06633 /*!
06634  * \brief plays a prompt and waits for a keypress.
06635  * \param chan
06636  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06637  * \param start Does not appear to be used at this time.
06638  *
06639  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06640  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06641  * prompting for the number inputs that correspond to the available folders.
06642  * 
06643  * \return zero on success, or -1 on error.
06644  */
06645 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06646 {
06647    int res = 0;
06648    int loops = 0;
06649    res = ast_play_and_wait(chan, fn);  /* Folder name */
06650    while (((res < '0') || (res > '9')) &&
06651          (res != '#') && (res >= 0) &&
06652          loops < 4) {
06653       res = get_folder(chan, 0);
06654       loops++;
06655    }
06656    if (loops == 4) { /* give up */
06657       return '#';
06658    }
06659    return res;
06660 }
06661 
06662 /*!
06663  * \brief presents the option to prepend to an existing message when forwarding it.
06664  * \param chan
06665  * \param vmu
06666  * \param curdir
06667  * \param curmsg
06668  * \param vm_fmts
06669  * \param context
06670  * \param record_gain
06671  * \param duration
06672  * \param vms
06673  * \param flag 
06674  *
06675  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06676  *
06677  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06678  * \return zero on success, -1 on error.
06679  */
06680 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06681          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06682 {
06683 #ifdef IMAP_STORAGE
06684    int res;
06685 #endif
06686    int cmd = 0;
06687    int retries = 0, prepend_duration = 0, already_recorded = 0;
06688    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06689    char textfile[PATH_MAX];
06690    struct ast_config *msg_cfg;
06691    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06692 #ifndef IMAP_STORAGE
06693    signed char zero_gain = 0;
06694 #endif
06695    const char *duration_str;
06696 
06697    /* Must always populate duration correctly */
06698    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06699    strcpy(textfile, msgfile);
06700    strcpy(backup, msgfile);
06701    strcpy(backup_textfile, msgfile);
06702    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06703    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06704    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06705 
06706    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06707       *duration = atoi(duration_str);
06708    } else {
06709       *duration = 0;
06710    }
06711 
06712    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06713       if (cmd)
06714          retries = 0;
06715       switch (cmd) {
06716       case '1': 
06717 
06718 #ifdef IMAP_STORAGE
06719          /* Record new intro file */
06720          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06721          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06722          res = ast_play_and_wait(chan, INTRO);
06723          res = ast_play_and_wait(chan, "beep");
06724          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06725          cmd = 't';
06726 #else
06727 
06728          /* prepend a message to the current message, update the metadata and return */
06729 
06730          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06731          strcpy(textfile, msgfile);
06732          strncat(textfile, ".txt", sizeof(textfile) - 1);
06733          *duration = 0;
06734 
06735          /* if we can't read the message metadata, stop now */
06736          if (!msg_cfg) {
06737             cmd = 0;
06738             break;
06739          }
06740          
06741          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06742          if (already_recorded) {
06743             ast_filecopy(backup, msgfile, NULL);
06744             copy(backup_textfile, textfile);
06745          }
06746          else {
06747             ast_filecopy(msgfile, backup, NULL);
06748             copy(textfile,backup_textfile);
06749          }
06750          already_recorded = 1;
06751 
06752          if (record_gain)
06753             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06754 
06755          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06756          if (cmd == 'S') {
06757             ast_filerename(backup, msgfile, NULL);
06758          }
06759 
06760          if (record_gain)
06761             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06762 
06763          
06764          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06765             *duration = atoi(duration_str);
06766 
06767          if (prepend_duration) {
06768             struct ast_category *msg_cat;
06769             /* need enough space for a maximum-length message duration */
06770             char duration_buf[12];
06771 
06772             *duration += prepend_duration;
06773             msg_cat = ast_category_get(msg_cfg, "message");
06774             snprintf(duration_buf, 11, "%ld", *duration);
06775             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06776                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06777             }
06778          }
06779 
06780 #endif
06781          break;
06782       case '2': 
06783          /* NULL out introfile so we know there is no intro! */
06784 #ifdef IMAP_STORAGE
06785          *vms->introfn = '\0';
06786 #endif
06787          cmd = 't';
06788          break;
06789       case '*':
06790          cmd = '*';
06791          break;
06792       default: 
06793          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06794             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06795          if (!cmd)
06796             cmd = ast_play_and_wait(chan, "vm-starmain");
06797             /* "press star to return to the main menu" */
06798          if (!cmd)
06799             cmd = ast_waitfordigit(chan, 6000);
06800          if (!cmd)
06801             retries++;
06802          if (retries > 3)
06803             cmd = 't';
06804       }
06805    }
06806 
06807    if (msg_cfg)
06808       ast_config_destroy(msg_cfg);
06809    if (prepend_duration)
06810       *duration = prepend_duration;
06811 
06812    if (already_recorded && cmd == -1) {
06813       /* restore original message if prepention cancelled */
06814       ast_filerename(backup, msgfile, NULL);
06815       rename(backup_textfile, textfile);
06816    }
06817 
06818    if (cmd == 't' || cmd == 'S')
06819       cmd = 0;
06820    return cmd;
06821 }
06822 
06823 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06824 {
06825    struct ast_event *event;
06826    char *mailbox, *context;
06827 
06828    /* Strip off @default */
06829    context = mailbox = ast_strdupa(box);
06830    strsep(&context, "@");
06831    if (ast_strlen_zero(context))
06832       context = "default";
06833 
06834    if (!(event = ast_event_new(AST_EVENT_MWI,
06835          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06836          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06837          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06838          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06839          AST_EVENT_IE_END))) {
06840       return;
06841    }
06842 
06843    ast_event_queue_and_cache(event);
06844 }
06845 
06846 /*!
06847  * \brief Sends email notification that a user has a new voicemail waiting for them.
06848  * \param chan
06849  * \param vmu
06850  * \param vms
06851  * \param msgnum
06852  * \param duration
06853  * \param fmt
06854  * \param cidnum The Caller ID phone number value.
06855  * \param cidname The Caller ID name value.
06856  * \param flag
06857  *
06858  * \return zero on success, -1 on error.
06859  */
06860 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)
06861 {
06862    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06863    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06864    const char *category;
06865    char *myserveremail = serveremail;
06866 
06867    ast_channel_lock(chan);
06868    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06869       category = ast_strdupa(category);
06870    }
06871    ast_channel_unlock(chan);
06872 
06873 #ifndef IMAP_STORAGE
06874    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06875 #else
06876    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06877 #endif
06878    make_file(fn, sizeof(fn), todir, msgnum);
06879    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06880 
06881    if (!ast_strlen_zero(vmu->attachfmt)) {
06882       if (strstr(fmt, vmu->attachfmt))
06883          fmt = vmu->attachfmt;
06884       else
06885          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);
06886    }
06887 
06888    /* Attach only the first format */
06889    fmt = ast_strdupa(fmt);
06890    stringp = fmt;
06891    strsep(&stringp, "|");
06892 
06893    if (!ast_strlen_zero(vmu->serveremail))
06894       myserveremail = vmu->serveremail;
06895 
06896    if (!ast_strlen_zero(vmu->email)) {
06897       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06898 
06899       if (attach_user_voicemail)
06900          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06901 
06902       /* XXX possible imap issue, should category be NULL XXX */
06903       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06904 
06905       if (attach_user_voicemail)
06906          DISPOSE(todir, msgnum);
06907    }
06908 
06909    if (!ast_strlen_zero(vmu->pager)) {
06910       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
06911    }
06912 
06913    if (ast_test_flag(vmu, VM_DELETE))
06914       DELETE(todir, msgnum, fn, vmu);
06915 
06916    /* Leave voicemail for someone */
06917    if (ast_app_has_voicemail(ext_context, NULL)) 
06918       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06919 
06920    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06921 
06922    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);
06923    run_externnotify(vmu->context, vmu->mailbox, flag);
06924 
06925 #ifdef IMAP_STORAGE
06926    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06927    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06928       vm_imap_delete(NULL, vms->curmsg, vmu);
06929       vms->newmessages--;  /* Fix new message count */
06930    }
06931 #endif
06932 
06933    return 0;
06934 }
06935 
06936 /*!
06937  * \brief Sends a voicemail message to a mailbox recipient.
06938  * \param chan
06939  * \param context
06940  * \param vms
06941  * \param sender
06942  * \param fmt
06943  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06944  *             Will be 0 when called to forward an existing message (option 8)
06945  *             Will be 1 when called to leave a message (option 3->5)
06946  * \param record_gain 
06947  * \param urgent
06948  *
06949  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06950  * 
06951  * When in the leave message mode (is_new_message == 1):
06952  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06953  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06954  *
06955  * When in the forward message mode (is_new_message == 0):
06956  *   - retreives the current message to be forwarded
06957  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06958  *   - determines the target mailbox and folders
06959  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06960  *
06961  * \return zero on success, -1 on error.
06962  */
06963 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)
06964 {
06965 #ifdef IMAP_STORAGE
06966    int todircount = 0;
06967    struct vm_state *dstvms;
06968 #endif
06969    char username[70]="";
06970    char fn[PATH_MAX]; /* for playback of name greeting */
06971    char ecodes[16] = "#";
06972    int res = 0, cmd = 0;
06973    struct ast_vm_user *receiver = NULL, *vmtmp;
06974    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06975    char *stringp;
06976    const char *s;
06977    int saved_messages = 0;
06978    int valid_extensions = 0;
06979    char *dir;
06980    int curmsg;
06981    char urgent_str[7] = "";
06982    char tmptxtfile[PATH_MAX];
06983    int prompt_played = 0;
06984 #ifndef IMAP_STORAGE
06985    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06986 #endif
06987    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06988       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06989    }
06990 
06991    if (vms == NULL) return -1;
06992    dir = vms->curdir;
06993    curmsg = vms->curmsg;
06994 
06995    tmptxtfile[0] = '\0';
06996    while (!res && !valid_extensions) {
06997       int use_directory = 0;
06998       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06999          int done = 0;
07000          int retries = 0;
07001          cmd = 0;
07002          while ((cmd >= 0) && !done ){
07003             if (cmd)
07004                retries = 0;
07005             switch (cmd) {
07006             case '1': 
07007                use_directory = 0;
07008                done = 1;
07009                break;
07010             case '2': 
07011                use_directory = 1;
07012                done = 1;
07013                break;
07014             case '*': 
07015                cmd = 't';
07016                done = 1;
07017                break;
07018             default: 
07019                /* Press 1 to enter an extension press 2 to use the directory */
07020                cmd = ast_play_and_wait(chan, "vm-forward");
07021                if (!cmd)
07022                   cmd = ast_waitfordigit(chan, 3000);
07023                if (!cmd)
07024                   retries++;
07025                if (retries > 3) {
07026                   cmd = 't';
07027                   done = 1;
07028                }
07029                
07030             }
07031          }
07032          if (cmd < 0 || cmd == 't')
07033             break;
07034       }
07035       
07036       if (use_directory) {
07037          /* use app_directory */
07038          
07039          char old_context[sizeof(chan->context)];
07040          char old_exten[sizeof(chan->exten)];
07041          int old_priority;
07042          struct ast_app* directory_app;
07043 
07044          directory_app = pbx_findapp("Directory");
07045          if (directory_app) {
07046             char vmcontext[256];
07047             /* make backup copies */
07048             memcpy(old_context, chan->context, sizeof(chan->context));
07049             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07050             old_priority = chan->priority;
07051             
07052             /* call the the Directory, changes the channel */
07053             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07054             res = pbx_exec(chan, directory_app, vmcontext);
07055             
07056             ast_copy_string(username, chan->exten, sizeof(username));
07057             
07058             /* restore the old context, exten, and priority */
07059             memcpy(chan->context, old_context, sizeof(chan->context));
07060             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07061             chan->priority = old_priority;
07062          } else {
07063             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07064             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07065          }
07066       } else {
07067          /* Ask for an extension */
07068          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07069          prompt_played++;
07070          if (res || prompt_played > 4)
07071             break;
07072          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07073             break;
07074       }
07075       
07076       /* start all over if no username */
07077       if (ast_strlen_zero(username))
07078          continue;
07079       stringp = username;
07080       s = strsep(&stringp, "*");
07081       /* start optimistic */
07082       valid_extensions = 1;
07083       while (s) {
07084          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07085             int oldmsgs;
07086             int newmsgs;
07087             int capacity;
07088             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07089                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07090                /* Shouldn't happen, but allow trying another extension if it does */
07091                res = ast_play_and_wait(chan, "pbx-invalid");
07092                valid_extensions = 0;
07093                break;
07094             }
07095             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07096             if ((newmsgs + oldmsgs) >= capacity) {
07097                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07098                res = ast_play_and_wait(chan, "vm-mailboxfull");
07099                valid_extensions = 0;
07100                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07101                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07102                   free_user(vmtmp);
07103                }
07104                inprocess_count(receiver->mailbox, receiver->context, -1);
07105                break;
07106             }
07107             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07108          } else {
07109             /* XXX Optimization for the future.  When we encounter a single bad extension,
07110              * bailing out on all of the extensions may not be the way to go.  We should
07111              * probably just bail on that single extension, then allow the user to enter
07112              * several more. XXX
07113              */
07114             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07115                free_user(receiver);
07116             }
07117             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07118             /* "I am sorry, that's not a valid extension.  Please try again." */
07119             res = ast_play_and_wait(chan, "pbx-invalid");
07120             valid_extensions = 0;
07121             break;
07122          }
07123 
07124          /* play name if available, else play extension number */
07125          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07126          RETRIEVE(fn, -1, s, receiver->context);
07127          if (ast_fileexists(fn, NULL, NULL) > 0) {
07128             res = ast_stream_and_wait(chan, fn, ecodes);
07129             if (res) {
07130                DISPOSE(fn, -1);
07131                return res;
07132             }
07133          } else {
07134             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07135          }
07136          DISPOSE(fn, -1);
07137 
07138          s = strsep(&stringp, "*");
07139       }
07140       /* break from the loop of reading the extensions */
07141       if (valid_extensions)
07142          break;
07143    }
07144    /* check if we're clear to proceed */
07145    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07146       return res;
07147    if (is_new_message == 1) {
07148       struct leave_vm_options leave_options;
07149       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07150       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07151 
07152       /* Send VoiceMail */
07153       memset(&leave_options, 0, sizeof(leave_options));
07154       leave_options.record_gain = record_gain;
07155       cmd = leave_voicemail(chan, mailbox, &leave_options);
07156    } else {
07157       /* Forward VoiceMail */
07158       long duration = 0;
07159       struct vm_state vmstmp;
07160       int copy_msg_result = 0;
07161       memcpy(&vmstmp, vms, sizeof(vmstmp));
07162 
07163       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07164 
07165       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07166       if (!cmd) {
07167          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07168 #ifdef IMAP_STORAGE
07169             int attach_user_voicemail;
07170             char *myserveremail = serveremail;
07171             
07172             /* get destination mailbox */
07173             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07174             if (!dstvms) {
07175                dstvms = create_vm_state_from_user(vmtmp);
07176             }
07177             if (dstvms) {
07178                init_mailstream(dstvms, 0);
07179                if (!dstvms->mailstream) {
07180                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07181                } else {
07182                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07183                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07184                }
07185             } else {
07186                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07187             }
07188             if (!ast_strlen_zero(vmtmp->serveremail))
07189                myserveremail = vmtmp->serveremail;
07190             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07191             /* NULL category for IMAP storage */
07192             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07193                dstvms->curbox,
07194                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07195                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07196                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07197                NULL, urgent_str);
07198 #else
07199             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07200 #endif
07201             saved_messages++;
07202             AST_LIST_REMOVE_CURRENT(list);
07203             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07204             free_user(vmtmp);
07205             if (res)
07206                break;
07207          }
07208          AST_LIST_TRAVERSE_SAFE_END;
07209          if (saved_messages > 0 && !copy_msg_result) {
07210             /* give confirmation that the message was saved */
07211             /* commented out since we can't forward batches yet
07212             if (saved_messages == 1)
07213                res = ast_play_and_wait(chan, "vm-message");
07214             else
07215                res = ast_play_and_wait(chan, "vm-messages");
07216             if (!res)
07217                res = ast_play_and_wait(chan, "vm-saved"); */
07218 #ifdef IMAP_STORAGE
07219             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07220             if (ast_strlen_zero(vmstmp.introfn))
07221 #endif
07222             res = ast_play_and_wait(chan, "vm-msgsaved");
07223          }
07224 #ifndef IMAP_STORAGE
07225          else {
07226             /* with IMAP, mailbox full warning played by imap_check_limits */
07227             res = ast_play_and_wait(chan, "vm-mailboxfull");
07228          }
07229          /* Restore original message without prepended message if backup exists */
07230          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07231          strcpy(textfile, msgfile);
07232          strcpy(backup, msgfile);
07233          strcpy(backup_textfile, msgfile);
07234          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07235          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07236          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07237          if (ast_fileexists(backup, NULL, NULL) > 0) {
07238             ast_filerename(backup, msgfile, NULL);
07239             rename(backup_textfile, textfile);
07240          }
07241 #endif
07242       }
07243       DISPOSE(dir, curmsg);
07244 #ifndef IMAP_STORAGE
07245       if (cmd) { /* assuming hangup, cleanup backup file */
07246          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07247          strcpy(textfile, msgfile);
07248          strcpy(backup_textfile, msgfile);
07249          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07250          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07251          rename(backup_textfile, textfile);
07252       }
07253 #endif
07254    }
07255 
07256    /* If anything failed above, we still have this list to free */
07257    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07258       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07259       free_user(vmtmp);
07260    }
07261    return res ? res : cmd;
07262 }
07263 
07264 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07265 {
07266    int res;
07267    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07268       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07269    return res;
07270 }
07271 
07272 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07273 {
07274    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);
07275 }
07276 
07277 static int play_message_category(struct ast_channel *chan, const char *category)
07278 {
07279    int res = 0;
07280 
07281    if (!ast_strlen_zero(category))
07282       res = ast_play_and_wait(chan, category);
07283 
07284    if (res) {
07285       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07286       res = 0;
07287    }
07288 
07289    return res;
07290 }
07291 
07292 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07293 {
07294    int res = 0;
07295    struct vm_zone *the_zone = NULL;
07296    time_t t;
07297 
07298    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07299       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07300       return 0;
07301    }
07302 
07303    /* Does this user have a timezone specified? */
07304    if (!ast_strlen_zero(vmu->zonetag)) {
07305       /* Find the zone in the list */
07306       struct vm_zone *z;
07307       AST_LIST_LOCK(&zones);
07308       AST_LIST_TRAVERSE(&zones, z, list) {
07309          if (!strcmp(z->name, vmu->zonetag)) {
07310             the_zone = z;
07311             break;
07312          }
07313       }
07314       AST_LIST_UNLOCK(&zones);
07315    }
07316 
07317 /* No internal variable parsing for now, so we'll comment it out for the time being */
07318 #if 0
07319    /* Set the DIFF_* variables */
07320    ast_localtime(&t, &time_now, NULL);
07321    tv_now = ast_tvnow();
07322    ast_localtime(&tv_now, &time_then, NULL);
07323 
07324    /* Day difference */
07325    if (time_now.tm_year == time_then.tm_year)
07326       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07327    else
07328       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07329    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07330 
07331    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07332 #endif
07333    if (the_zone) {
07334       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07335    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07336       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07337    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07338       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07339    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07340       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);
07341    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07342       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07343    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07344       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07345    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07346       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07347    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07348       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);
07349    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07350       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07351    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07352       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07353    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07354       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);
07355    } else {
07356       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07357    }
07358 #if 0
07359    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07360 #endif
07361    return res;
07362 }
07363 
07364 
07365 
07366 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07367 {
07368    int res = 0;
07369    int i;
07370    char *callerid, *name;
07371    char prefile[PATH_MAX] = "";
07372    
07373 
07374    /* If voicemail cid is not enabled, or we didn't get cid or context from
07375     * the attribute file, leave now.
07376     *
07377     * TODO Still need to change this so that if this function is called by the
07378     * message envelope (and someone is explicitly requesting to hear the CID),
07379     * it does not check to see if CID is enabled in the config file.
07380     */
07381    if ((cid == NULL)||(context == NULL))
07382       return res;
07383 
07384    /* Strip off caller ID number from name */
07385    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07386    ast_callerid_parse(cid, &name, &callerid);
07387    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07388       /* Check for internal contexts and only */
07389       /* say extension when the call didn't come from an internal context in the list */
07390       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07391          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07392          if ((strcmp(cidinternalcontexts[i], context) == 0))
07393             break;
07394       }
07395       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07396          if (!res) {
07397             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07398             if (!ast_strlen_zero(prefile)) {
07399             /* See if we can find a recorded name for this person instead of their extension number */
07400                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07401                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07402                   if (!callback)
07403                      res = wait_file2(chan, vms, "vm-from");
07404                   res = ast_stream_and_wait(chan, prefile, "");
07405                } else {
07406                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07407                   /* Say "from extension" as one saying to sound smoother */
07408                   if (!callback)
07409                      res = wait_file2(chan, vms, "vm-from-extension");
07410                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07411                }
07412             }
07413          }
07414       } else if (!res) {
07415          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07416          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07417          if (!callback)
07418             res = wait_file2(chan, vms, "vm-from-phonenumber");
07419          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07420       }
07421    } else {
07422       /* Number unknown */
07423       ast_debug(1, "VM-CID: From an unknown number\n");
07424       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07425       res = wait_file2(chan, vms, "vm-unknown-caller");
07426    }
07427    return res;
07428 }
07429 
07430 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07431 {
07432    int res = 0;
07433    int durationm;
07434    int durations;
07435    /* Verify that we have a duration for the message */
07436    if (duration == NULL)
07437       return res;
07438 
07439    /* Convert from seconds to minutes */
07440    durations = atoi(duration);
07441    durationm = (durations / 60);
07442 
07443    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07444 
07445    if ((!res) && (durationm >= minduration)) {
07446       res = wait_file2(chan, vms, "vm-duration");
07447 
07448       /* POLISH syntax */
07449       if (!strncasecmp(chan->language, "pl", 2)) {
07450          div_t num = div(durationm, 10);
07451 
07452          if (durationm == 1) {
07453             res = ast_play_and_wait(chan, "digits/1z");
07454             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07455          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07456             if (num.rem == 2) {
07457                if (!num.quot) {
07458                   res = ast_play_and_wait(chan, "digits/2-ie");
07459                } else {
07460                   res = say_and_wait(chan, durationm - 2 , chan->language);
07461                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07462                }
07463             } else {
07464                res = say_and_wait(chan, durationm, chan->language);
07465             }
07466             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07467          } else {
07468             res = say_and_wait(chan, durationm, chan->language);
07469             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07470          }
07471       /* DEFAULT syntax */
07472       } else {
07473          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07474          res = wait_file2(chan, vms, "vm-minutes");
07475       }
07476    }
07477    return res;
07478 }
07479 
07480 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07481 {
07482    int res = 0;
07483    char filename[256], *cid;
07484    const char *origtime, *context, *category, *duration, *flag;
07485    struct ast_config *msg_cfg;
07486    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07487 
07488    vms->starting = 0;
07489    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07490    adsi_message(chan, vms);
07491    if (!vms->curmsg)
07492       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07493    else if (vms->curmsg == vms->lastmsg)
07494       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07495 
07496    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07497    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07498    msg_cfg = ast_config_load(filename, config_flags);
07499    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07500       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07501       return 0;
07502    }
07503    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07504 
07505    /* Play the word urgent if we are listening to urgent messages */
07506    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07507       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07508    }
07509 
07510    if (!res) {
07511       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07512       /* POLISH syntax */
07513       if (!strncasecmp(chan->language, "pl", 2)) {
07514          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07515             int ten, one;
07516             char nextmsg[256];
07517             ten = (vms->curmsg + 1) / 10;
07518             one = (vms->curmsg + 1) % 10;
07519 
07520             if (vms->curmsg < 20) {
07521                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07522                res = wait_file2(chan, vms, nextmsg);
07523             } else {
07524                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07525                res = wait_file2(chan, vms, nextmsg);
07526                if (one > 0) {
07527                   if (!res) {
07528                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07529                      res = wait_file2(chan, vms, nextmsg);
07530                   }
07531                }
07532             }
07533          }
07534          if (!res)
07535             res = wait_file2(chan, vms, "vm-message");
07536       /* HEBREW syntax */
07537       } else if (!strncasecmp(chan->language, "he", 2)) {
07538          if (!vms->curmsg) {
07539             res = wait_file2(chan, vms, "vm-message");
07540             res = wait_file2(chan, vms, "vm-first");
07541          } else if (vms->curmsg == vms->lastmsg) {
07542             res = wait_file2(chan, vms, "vm-message");
07543             res = wait_file2(chan, vms, "vm-last");
07544          } else {
07545             res = wait_file2(chan, vms, "vm-message");
07546             res = wait_file2(chan, vms, "vm-number");
07547             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07548          }
07549       /* VIETNAMESE syntax */
07550       } else if (!strncasecmp(chan->language, "vi", 2)) {
07551          if (!vms->curmsg) {
07552             res = wait_file2(chan, vms, "vm-message");
07553             res = wait_file2(chan, vms, "vm-first");
07554          } else if (vms->curmsg == vms->lastmsg) {
07555             res = wait_file2(chan, vms, "vm-message");
07556             res = wait_file2(chan, vms, "vm-last");
07557          } else {
07558             res = wait_file2(chan, vms, "vm-message");
07559             res = wait_file2(chan, vms, "vm-number");
07560             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07561          }
07562       } else {
07563          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07564             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07565          } else { /* DEFAULT syntax */
07566             res = wait_file2(chan, vms, "vm-message");
07567          }
07568          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07569             if (!res) {
07570                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07571             }
07572          }
07573       }
07574    }
07575 
07576    if (!msg_cfg) {
07577       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07578       return 0;
07579    }
07580 
07581    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07582       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07583       DISPOSE(vms->curdir, vms->curmsg);
07584       ast_config_destroy(msg_cfg);
07585       return 0;
07586    }
07587 
07588    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07589    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07590    category = ast_variable_retrieve(msg_cfg, "message", "category");
07591 
07592    context = ast_variable_retrieve(msg_cfg, "message", "context");
07593    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07594       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07595    if (!res) {
07596       res = play_message_category(chan, category);
07597    }
07598    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07599       res = play_message_datetime(chan, vmu, origtime, filename);
07600    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07601       res = play_message_callerid(chan, vms, cid, context, 0);
07602    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07603       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07604    /* Allow pressing '1' to skip envelope / callerid */
07605    if (res == '1')
07606       res = 0;
07607    ast_config_destroy(msg_cfg);
07608 
07609    if (!res) {
07610       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07611       vms->heard[vms->curmsg] = 1;
07612 #ifdef IMAP_STORAGE
07613       /*IMAP storage stores any prepended message from a forward
07614        * as a separate file from the rest of the message
07615        */
07616       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07617          wait_file(chan, vms, vms->introfn);
07618       }
07619 #endif
07620       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07621          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07622          res = 0;
07623       }
07624    }
07625    DISPOSE(vms->curdir, vms->curmsg);
07626    return res;
07627 }
07628 
07629 #ifdef IMAP_STORAGE
07630 static int imap_remove_file(char *dir, int msgnum)
07631 {
07632    char fn[PATH_MAX];
07633    char full_fn[PATH_MAX];
07634    char intro[PATH_MAX] = {0,};
07635    
07636    if (msgnum > -1) {
07637       make_file(fn, sizeof(fn), dir, msgnum);
07638       snprintf(intro, sizeof(intro), "%sintro", fn);
07639    } else
07640       ast_copy_string(fn, dir, sizeof(fn));
07641    
07642    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07643       ast_filedelete(fn, NULL);
07644       if (!ast_strlen_zero(intro)) {
07645          ast_filedelete(intro, NULL);
07646       }
07647       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07648       unlink(full_fn);
07649    }
07650    return 0;
07651 }
07652 
07653 
07654 
07655 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07656 {
07657    char *file, *filename;
07658    char *attachment;
07659    char arg[10];
07660    int i;
07661    BODY* body;
07662 
07663    file = strrchr(ast_strdupa(dir), '/');
07664    if (file) {
07665       *file++ = '\0';
07666    } else {
07667       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07668       return -1;
07669    }
07670 
07671    ast_mutex_lock(&vms->lock);
07672    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07673       mail_fetchstructure(vms->mailstream, i + 1, &body);
07674       /* We have the body, now we extract the file name of the first attachment. */
07675       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07676          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07677       } else {
07678          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07679          ast_mutex_unlock(&vms->lock);
07680          return -1;
07681       }
07682       filename = strsep(&attachment, ".");
07683       if (!strcmp(filename, file)) {
07684          sprintf(arg, "%d", i + 1);
07685          mail_setflag(vms->mailstream, arg, "\\DELETED");
07686       }
07687    }
07688    mail_expunge(vms->mailstream);
07689    ast_mutex_unlock(&vms->lock);
07690    return 0;
07691 }
07692 
07693 #elif !defined(IMAP_STORAGE)
07694 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07695 {
07696    int count_msg, last_msg;
07697 
07698    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07699 
07700    /* Rename the member vmbox HERE so that we don't try to return before
07701     * we know what's going on.
07702     */
07703    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07704 
07705    /* Faster to make the directory than to check if it exists. */
07706    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07707 
07708    /* traverses directory using readdir (or select query for ODBC) */
07709    count_msg = count_messages(vmu, vms->curdir);
07710    if (count_msg < 0) {
07711       return count_msg;
07712    } else {
07713       vms->lastmsg = count_msg - 1;
07714    }
07715 
07716    if (vm_allocate_dh(vms, vmu, count_msg)) {
07717       return -1;
07718    }
07719 
07720    /*
07721    The following test is needed in case sequencing gets messed up.
07722    There appears to be more than one way to mess up sequence, so
07723    we will not try to find all of the root causes--just fix it when
07724    detected.
07725    */
07726 
07727    if (vm_lock_path(vms->curdir)) {
07728       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07729       return ERROR_LOCK_PATH;
07730    }
07731 
07732    /* for local storage, checks directory for messages up to maxmsg limit */
07733    last_msg = last_message_index(vmu, vms->curdir);
07734    ast_unlock_path(vms->curdir);
07735 
07736    if (last_msg < -1) {
07737       return last_msg;
07738    } else if (vms->lastmsg != last_msg) {
07739       ast_log(LOG_NOTICE, "Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07740    }
07741 
07742    return 0;
07743 }
07744 #endif
07745 
07746 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07747 {
07748    int x = 0;
07749 #ifndef IMAP_STORAGE
07750    int res = 0, nummsg;
07751    char fn2[PATH_MAX];
07752 #endif
07753 
07754    if (vms->lastmsg <= -1) {
07755       goto done;
07756    }
07757 
07758    vms->curmsg = -1;
07759 #ifndef IMAP_STORAGE
07760    /* Get the deleted messages fixed */
07761    if (vm_lock_path(vms->curdir)) {
07762       return ERROR_LOCK_PATH;
07763    }
07764 
07765    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07766    for (x = 0; x < vms->lastmsg + 1; x++) {
07767       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07768          /* Save this message.  It's not in INBOX or hasn't been heard */
07769          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07770          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07771             break;
07772          }
07773          vms->curmsg++;
07774          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07775          if (strcmp(vms->fn, fn2)) {
07776             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07777          }
07778       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07779          /* Move to old folder before deleting */
07780          res = save_to_folder(vmu, vms, x, 1);
07781          if (res == ERROR_LOCK_PATH) {
07782             /* If save failed do not delete the message */
07783             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07784             vms->deleted[x] = 0;
07785             vms->heard[x] = 0;
07786             --x;
07787          }
07788       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07789          /* Move to deleted folder */
07790          res = save_to_folder(vmu, vms, x, 10);
07791          if (res == ERROR_LOCK_PATH) {
07792             /* If save failed do not delete the message */
07793             vms->deleted[x] = 0;
07794             vms->heard[x] = 0;
07795             --x;
07796          }
07797       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07798          /* If realtime storage enabled - we should explicitly delete this message,
07799          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07800          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07801          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07802             DELETE(vms->curdir, x, vms->fn, vmu);
07803          }
07804       }
07805    }
07806 
07807    /* Delete ALL remaining messages */
07808    nummsg = x - 1;
07809    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07810       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07811       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07812          DELETE(vms->curdir, x, vms->fn, vmu);
07813       }
07814    }
07815    ast_unlock_path(vms->curdir);
07816 #else /* defined(IMAP_STORAGE) */
07817    if (vms->deleted) {
07818       /* Since we now expunge after each delete, deleting in reverse order
07819        * ensures that no reordering occurs between each step. */
07820       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07821          if (vms->deleted[x]) {
07822             ast_debug(3, "IMAP delete of %d\n", x);
07823             DELETE(vms->curdir, x, vms->fn, vmu);
07824          }
07825       }
07826    }
07827 #endif
07828 
07829 done:
07830    if (vms->deleted && vmu->maxmsg) {
07831       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07832    }
07833    if (vms->heard && vmu->maxmsg) {
07834       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07835    }
07836 
07837    return 0;
07838 }
07839 
07840 /* In Greek even though we CAN use a syntax like "friends messages"
07841  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07842  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07843  * syntax for the above three categories which is more elegant.
07844  */
07845 
07846 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07847 {
07848    int cmd;
07849    char *buf;
07850 
07851    buf = alloca(strlen(box) + 2);
07852    strcpy(buf, box);
07853    strcat(buf, "s");
07854 
07855    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07856       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07857       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07858    } else {
07859       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07860       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07861    }
07862 }
07863 
07864 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07865 {
07866    int cmd;
07867 
07868    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07869       if (!strcasecmp(box, "vm-INBOX"))
07870          cmd = ast_play_and_wait(chan, "vm-new-e");
07871       else
07872          cmd = ast_play_and_wait(chan, "vm-old-e");
07873       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07874    } else {
07875       cmd = ast_play_and_wait(chan, "vm-messages");
07876       return cmd ? cmd : ast_play_and_wait(chan, box);
07877    }
07878 }
07879 
07880 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07881 {
07882    int cmd;
07883 
07884    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07885       cmd = ast_play_and_wait(chan, "vm-messages");
07886       return cmd ? cmd : ast_play_and_wait(chan, box);
07887    } else {
07888       cmd = ast_play_and_wait(chan, box);
07889       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07890    }
07891 }
07892 
07893 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07894 {
07895    int cmd;
07896 
07897    if (  !strncasecmp(chan->language, "it", 2) ||
07898         !strncasecmp(chan->language, "es", 2) ||
07899         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07900       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07901       return cmd ? cmd : ast_play_and_wait(chan, box);
07902    } else if (!strncasecmp(chan->language, "gr", 2)) {
07903       return vm_play_folder_name_gr(chan, box);
07904    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07905       return ast_play_and_wait(chan, box);
07906    } else if (!strncasecmp(chan->language, "pl", 2)) {
07907       return vm_play_folder_name_pl(chan, box);
07908    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07909       return vm_play_folder_name_ua(chan, box);
07910    } else if (!strncasecmp(chan->language, "vi", 2)) {
07911       return ast_play_and_wait(chan, box);
07912    } else {  /* Default English */
07913       cmd = ast_play_and_wait(chan, box);
07914       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07915    }
07916 }
07917 
07918 /* GREEK SYNTAX
07919    In greek the plural for old/new is
07920    different so we need the following files
07921    We also need vm-denExeteMynhmata because
07922    this syntax is different.
07923 
07924    -> vm-Olds.wav : "Palia"
07925    -> vm-INBOXs.wav : "Nea"
07926    -> vm-denExeteMynhmata : "den exete mynhmata"
07927 */
07928 
07929 
07930 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07931 {
07932    int res = 0;
07933 
07934    if (vms->newmessages) {
07935       res = ast_play_and_wait(chan, "vm-youhave");
07936       if (!res) 
07937          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07938       if (!res) {
07939          if ((vms->newmessages == 1)) {
07940             res = ast_play_and_wait(chan, "vm-INBOX");
07941             if (!res)
07942                res = ast_play_and_wait(chan, "vm-message");
07943          } else {
07944             res = ast_play_and_wait(chan, "vm-INBOXs");
07945             if (!res)
07946                res = ast_play_and_wait(chan, "vm-messages");
07947          }
07948       }
07949    } else if (vms->oldmessages){
07950       res = ast_play_and_wait(chan, "vm-youhave");
07951       if (!res)
07952          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07953       if ((vms->oldmessages == 1)){
07954          res = ast_play_and_wait(chan, "vm-Old");
07955          if (!res)
07956             res = ast_play_and_wait(chan, "vm-message");
07957       } else {
07958          res = ast_play_and_wait(chan, "vm-Olds");
07959          if (!res)
07960             res = ast_play_and_wait(chan, "vm-messages");
07961       }
07962    } else if (!vms->oldmessages && !vms->newmessages) 
07963       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07964    return res;
07965 }
07966 
07967 /* Version of vm_intro() designed to work for many languages.
07968  *
07969  * It is hoped that this function can prevent the proliferation of 
07970  * language-specific vm_intro() functions and in time replace the language-
07971  * specific functions which already exist.  An examination of the language-
07972  * specific functions revealed that they all corrected the same deficiencies
07973  * in vm_intro_en() (which was the default function). Namely:
07974  *
07975  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07976  *     wording of the voicemail greeting hides this problem.  For example,
07977  *     vm-INBOX contains only the word "new".  This means that both of these
07978  *     sequences produce valid utterances:
07979  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07980  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07981  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07982  *     in many languages) the first utterance becomes "you have 1 the new message".
07983  *  2) The function contains hardcoded rules for pluralizing the word "message".
07984  *     These rules are correct for English, but not for many other languages.
07985  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07986  *     required in many languages.
07987  *  4) The gender of the word for "message" is not specified. This is a problem
07988  *     because in many languages the gender of the number in phrases such
07989  *     as "you have one new message" must match the gender of the word
07990  *     meaning "message".
07991  *
07992  * Fixing these problems for each new language has meant duplication of effort.
07993  * This new function solves the problems in the following general ways:
07994  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07995  *     and vm-Old respectively for those languages where it makes sense.
07996  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07997  *     on vm-message.
07998  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07999  *     prefix on vm-new and vm-old (none for English).
08000  *  4) Pass the gender of the language's word for "message" as an agument to
08001  *     this function which is can in turn pass on to the functions which 
08002  *     say numbers and put endings on nounds and adjectives.
08003  *
08004  * All languages require these messages:
08005  *  vm-youhave    "You have..."
08006  *  vm-and     "and"
08007  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08008  *
08009  * To use it for English, you will need these additional sound files:
08010  *  vm-new     "new"
08011  *  vm-message    "message", singular
08012  *  vm-messages      "messages", plural
08013  *
08014  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08015  *
08016  *  vm-newn    "novoye" (singular, neuter)
08017  *  vm-newx    "novikh" (counting plural form, genative plural)
08018  *  vm-message    "sobsheniye" (singular form)
08019  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08020  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08021  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08022  *  digits/2n     "dva" (neuter singular)
08023  */
08024 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08025 {
08026    int res;
08027    int lastnum = 0;
08028 
08029    res = ast_play_and_wait(chan, "vm-youhave");
08030 
08031    if (!res && vms->newmessages) {
08032       lastnum = vms->newmessages;
08033 
08034       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08035          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08036       }
08037 
08038       if (!res && vms->oldmessages) {
08039          res = ast_play_and_wait(chan, "vm-and");
08040       }
08041    }
08042 
08043    if (!res && vms->oldmessages) {
08044       lastnum = vms->oldmessages;
08045 
08046       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08047          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08048       }
08049    }
08050 
08051    if (!res) {
08052       if (lastnum == 0) {
08053          res = ast_play_and_wait(chan, "vm-no");
08054       }
08055       if (!res) {
08056          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08057       }
08058    }
08059 
08060    return res;
08061 }
08062 
08063 /* Default Hebrew syntax */
08064 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08065 {
08066    int res = 0;
08067 
08068    /* Introduce messages they have */
08069    if (!res) {
08070       if ((vms->newmessages) || (vms->oldmessages)) {
08071          res = ast_play_and_wait(chan, "vm-youhave");
08072       }
08073       /*
08074        * The word "shtei" refers to the number 2 in hebrew when performing a count
08075        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08076        * an element, this is one of them.
08077        */
08078       if (vms->newmessages) {
08079          if (!res) {
08080             if (vms->newmessages == 1) {
08081                res = ast_play_and_wait(chan, "vm-INBOX1");
08082             } else {
08083                if (vms->newmessages == 2) {
08084                   res = ast_play_and_wait(chan, "vm-shtei");
08085                } else {
08086                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08087                }
08088                res = ast_play_and_wait(chan, "vm-INBOX");
08089             }
08090          }
08091          if (vms->oldmessages && !res) {
08092             res = ast_play_and_wait(chan, "vm-and");
08093             if (vms->oldmessages == 1) {
08094                res = ast_play_and_wait(chan, "vm-Old1");
08095             } else {
08096                if (vms->oldmessages == 2) {
08097                   res = ast_play_and_wait(chan, "vm-shtei");
08098                } else {
08099                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08100                }
08101                res = ast_play_and_wait(chan, "vm-Old");
08102             }
08103          }
08104       }
08105       if (!res && vms->oldmessages && !vms->newmessages) {
08106          if (!res) {
08107             if (vms->oldmessages == 1) {
08108                res = ast_play_and_wait(chan, "vm-Old1");
08109             } else {
08110                if (vms->oldmessages == 2) {
08111                   res = ast_play_and_wait(chan, "vm-shtei");
08112                } else {
08113                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08114                }
08115                res = ast_play_and_wait(chan, "vm-Old");
08116             }
08117          }
08118       }
08119       if (!res) {
08120          if (!vms->oldmessages && !vms->newmessages) {
08121             if (!res) {
08122                res = ast_play_and_wait(chan, "vm-nomessages");
08123             }
08124          }
08125       }
08126    }
08127    return res;
08128 }
08129    
08130 /* Default English syntax */
08131 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08132 {
08133    int res;
08134 
08135    /* Introduce messages they have */
08136    res = ast_play_and_wait(chan, "vm-youhave");
08137    if (!res) {
08138       if (vms->urgentmessages) {
08139          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08140          if (!res)
08141             res = ast_play_and_wait(chan, "vm-Urgent");
08142          if ((vms->oldmessages || vms->newmessages) && !res) {
08143             res = ast_play_and_wait(chan, "vm-and");
08144          } else if (!res) {
08145             if ((vms->urgentmessages == 1))
08146                res = ast_play_and_wait(chan, "vm-message");
08147             else
08148                res = ast_play_and_wait(chan, "vm-messages");
08149          }
08150       }
08151       if (vms->newmessages) {
08152          res = say_and_wait(chan, vms->newmessages, chan->language);
08153          if (!res)
08154             res = ast_play_and_wait(chan, "vm-INBOX");
08155          if (vms->oldmessages && !res)
08156             res = ast_play_and_wait(chan, "vm-and");
08157          else if (!res) {
08158             if ((vms->newmessages == 1))
08159                res = ast_play_and_wait(chan, "vm-message");
08160             else
08161                res = ast_play_and_wait(chan, "vm-messages");
08162          }
08163             
08164       }
08165       if (!res && vms->oldmessages) {
08166          res = say_and_wait(chan, vms->oldmessages, chan->language);
08167          if (!res)
08168             res = ast_play_and_wait(chan, "vm-Old");
08169          if (!res) {
08170             if (vms->oldmessages == 1)
08171                res = ast_play_and_wait(chan, "vm-message");
08172             else
08173                res = ast_play_and_wait(chan, "vm-messages");
08174          }
08175       }
08176       if (!res) {
08177          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08178             res = ast_play_and_wait(chan, "vm-no");
08179             if (!res)
08180                res = ast_play_and_wait(chan, "vm-messages");
08181          }
08182       }
08183    }
08184    return res;
08185 }
08186 
08187 /* ITALIAN syntax */
08188 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08189 {
08190    /* Introduce messages they have */
08191    int res;
08192    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08193       res = ast_play_and_wait(chan, "vm-no") ||
08194          ast_play_and_wait(chan, "vm-message");
08195    else
08196       res = ast_play_and_wait(chan, "vm-youhave");
08197    if (!res && vms->newmessages) {
08198       res = (vms->newmessages == 1) ?
08199          ast_play_and_wait(chan, "digits/un") ||
08200          ast_play_and_wait(chan, "vm-nuovo") ||
08201          ast_play_and_wait(chan, "vm-message") :
08202          /* 2 or more new messages */
08203          say_and_wait(chan, vms->newmessages, chan->language) ||
08204          ast_play_and_wait(chan, "vm-nuovi") ||
08205          ast_play_and_wait(chan, "vm-messages");
08206       if (!res && vms->oldmessages)
08207          res = ast_play_and_wait(chan, "vm-and");
08208    }
08209    if (!res && vms->oldmessages) {
08210       res = (vms->oldmessages == 1) ?
08211          ast_play_and_wait(chan, "digits/un") ||
08212          ast_play_and_wait(chan, "vm-vecchio") ||
08213          ast_play_and_wait(chan, "vm-message") :
08214          /* 2 or more old messages */
08215          say_and_wait(chan, vms->oldmessages, chan->language) ||
08216          ast_play_and_wait(chan, "vm-vecchi") ||
08217          ast_play_and_wait(chan, "vm-messages");
08218    }
08219    return res;
08220 }
08221 
08222 /* POLISH syntax */
08223 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08224 {
08225    /* Introduce messages they have */
08226    int res;
08227    div_t num;
08228 
08229    if (!vms->oldmessages && !vms->newmessages) {
08230       res = ast_play_and_wait(chan, "vm-no");
08231       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08232       return res;
08233    } else {
08234       res = ast_play_and_wait(chan, "vm-youhave");
08235    }
08236 
08237    if (vms->newmessages) {
08238       num = div(vms->newmessages, 10);
08239       if (vms->newmessages == 1) {
08240          res = ast_play_and_wait(chan, "digits/1-a");
08241          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08242          res = res ? res : ast_play_and_wait(chan, "vm-message");
08243       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08244          if (num.rem == 2) {
08245             if (!num.quot) {
08246                res = ast_play_and_wait(chan, "digits/2-ie");
08247             } else {
08248                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08249                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08250             }
08251          } else {
08252             res = say_and_wait(chan, vms->newmessages, chan->language);
08253          }
08254          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08255          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08256       } else {
08257          res = say_and_wait(chan, vms->newmessages, chan->language);
08258          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08259          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08260       }
08261       if (!res && vms->oldmessages)
08262          res = ast_play_and_wait(chan, "vm-and");
08263    }
08264    if (!res && vms->oldmessages) {
08265       num = div(vms->oldmessages, 10);
08266       if (vms->oldmessages == 1) {
08267          res = ast_play_and_wait(chan, "digits/1-a");
08268          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08269          res = res ? res : ast_play_and_wait(chan, "vm-message");
08270       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08271          if (num.rem == 2) {
08272             if (!num.quot) {
08273                res = ast_play_and_wait(chan, "digits/2-ie");
08274             } else {
08275                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08276                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08277             }
08278          } else {
08279             res = say_and_wait(chan, vms->oldmessages, chan->language);
08280          }
08281          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08282          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08283       } else {
08284          res = say_and_wait(chan, vms->oldmessages, chan->language);
08285          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08286          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08287       }
08288    }
08289 
08290    return res;
08291 }
08292 
08293 /* SWEDISH syntax */
08294 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08295 {
08296    /* Introduce messages they have */
08297    int res;
08298 
08299    res = ast_play_and_wait(chan, "vm-youhave");
08300    if (res)
08301       return res;
08302 
08303    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08304       res = ast_play_and_wait(chan, "vm-no");
08305       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08306       return res;
08307    }
08308 
08309    if (vms->newmessages) {
08310       if ((vms->newmessages == 1)) {
08311          res = ast_play_and_wait(chan, "digits/ett");
08312          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08313          res = res ? res : ast_play_and_wait(chan, "vm-message");
08314       } else {
08315          res = say_and_wait(chan, vms->newmessages, chan->language);
08316          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08317          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08318       }
08319       if (!res && vms->oldmessages)
08320          res = ast_play_and_wait(chan, "vm-and");
08321    }
08322    if (!res && vms->oldmessages) {
08323       if (vms->oldmessages == 1) {
08324          res = ast_play_and_wait(chan, "digits/ett");
08325          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08326          res = res ? res : ast_play_and_wait(chan, "vm-message");
08327       } else {
08328          res = say_and_wait(chan, vms->oldmessages, chan->language);
08329          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08330          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08331       }
08332    }
08333 
08334    return res;
08335 }
08336 
08337 /* NORWEGIAN syntax */
08338 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08339 {
08340    /* Introduce messages they have */
08341    int res;
08342 
08343    res = ast_play_and_wait(chan, "vm-youhave");
08344    if (res)
08345       return res;
08346 
08347    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08348       res = ast_play_and_wait(chan, "vm-no");
08349       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08350       return res;
08351    }
08352 
08353    if (vms->newmessages) {
08354       if ((vms->newmessages == 1)) {
08355          res = ast_play_and_wait(chan, "digits/1");
08356          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08357          res = res ? res : ast_play_and_wait(chan, "vm-message");
08358       } else {
08359          res = say_and_wait(chan, vms->newmessages, chan->language);
08360          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08361          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08362       }
08363       if (!res && vms->oldmessages)
08364          res = ast_play_and_wait(chan, "vm-and");
08365    }
08366    if (!res && vms->oldmessages) {
08367       if (vms->oldmessages == 1) {
08368          res = ast_play_and_wait(chan, "digits/1");
08369          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08370          res = res ? res : ast_play_and_wait(chan, "vm-message");
08371       } else {
08372          res = say_and_wait(chan, vms->oldmessages, chan->language);
08373          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08374          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08375       }
08376    }
08377 
08378    return res;
08379 }
08380 
08381 /* GERMAN syntax */
08382 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08383 {
08384    /* Introduce messages they have */
08385    int res;
08386    res = ast_play_and_wait(chan, "vm-youhave");
08387    if (!res) {
08388       if (vms->newmessages) {
08389          if ((vms->newmessages == 1))
08390             res = ast_play_and_wait(chan, "digits/1F");
08391          else
08392             res = say_and_wait(chan, vms->newmessages, chan->language);
08393          if (!res)
08394             res = ast_play_and_wait(chan, "vm-INBOX");
08395          if (vms->oldmessages && !res)
08396             res = ast_play_and_wait(chan, "vm-and");
08397          else if (!res) {
08398             if ((vms->newmessages == 1))
08399                res = ast_play_and_wait(chan, "vm-message");
08400             else
08401                res = ast_play_and_wait(chan, "vm-messages");
08402          }
08403             
08404       }
08405       if (!res && vms->oldmessages) {
08406          if (vms->oldmessages == 1)
08407             res = ast_play_and_wait(chan, "digits/1F");
08408          else
08409             res = say_and_wait(chan, vms->oldmessages, chan->language);
08410          if (!res)
08411             res = ast_play_and_wait(chan, "vm-Old");
08412          if (!res) {
08413             if (vms->oldmessages == 1)
08414                res = ast_play_and_wait(chan, "vm-message");
08415             else
08416                res = ast_play_and_wait(chan, "vm-messages");
08417          }
08418       }
08419       if (!res) {
08420          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08421             res = ast_play_and_wait(chan, "vm-no");
08422             if (!res)
08423                res = ast_play_and_wait(chan, "vm-messages");
08424          }
08425       }
08426    }
08427    return res;
08428 }
08429 
08430 /* SPANISH syntax */
08431 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08432 {
08433    /* Introduce messages they have */
08434    int res;
08435    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08436       res = ast_play_and_wait(chan, "vm-youhaveno");
08437       if (!res)
08438          res = ast_play_and_wait(chan, "vm-messages");
08439    } else {
08440       res = ast_play_and_wait(chan, "vm-youhave");
08441    }
08442    if (!res) {
08443       if (vms->newmessages) {
08444          if (!res) {
08445             if ((vms->newmessages == 1)) {
08446                res = ast_play_and_wait(chan, "digits/1M");
08447                if (!res)
08448                   res = ast_play_and_wait(chan, "vm-message");
08449                if (!res)
08450                   res = ast_play_and_wait(chan, "vm-INBOXs");
08451             } else {
08452                res = say_and_wait(chan, vms->newmessages, chan->language);
08453                if (!res)
08454                   res = ast_play_and_wait(chan, "vm-messages");
08455                if (!res)
08456                   res = ast_play_and_wait(chan, "vm-INBOX");
08457             }
08458          }
08459          if (vms->oldmessages && !res)
08460             res = ast_play_and_wait(chan, "vm-and");
08461       }
08462       if (vms->oldmessages) {
08463          if (!res) {
08464             if (vms->oldmessages == 1) {
08465                res = ast_play_and_wait(chan, "digits/1M");
08466                if (!res)
08467                   res = ast_play_and_wait(chan, "vm-message");
08468                if (!res)
08469                   res = ast_play_and_wait(chan, "vm-Olds");
08470             } else {
08471                res = say_and_wait(chan, vms->oldmessages, chan->language);
08472                if (!res)
08473                   res = ast_play_and_wait(chan, "vm-messages");
08474                if (!res)
08475                   res = ast_play_and_wait(chan, "vm-Old");
08476             }
08477          }
08478       }
08479    }
08480 return res;
08481 }
08482 
08483 /* BRAZILIAN PORTUGUESE syntax */
08484 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08485    /* Introduce messages they have */
08486    int res;
08487    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08488       res = ast_play_and_wait(chan, "vm-nomessages");
08489       return res;
08490    } else {
08491       res = ast_play_and_wait(chan, "vm-youhave");
08492    }
08493    if (vms->newmessages) {
08494       if (!res)
08495          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08496       if ((vms->newmessages == 1)) {
08497          if (!res)
08498             res = ast_play_and_wait(chan, "vm-message");
08499          if (!res)
08500             res = ast_play_and_wait(chan, "vm-INBOXs");
08501       } else {
08502          if (!res)
08503             res = ast_play_and_wait(chan, "vm-messages");
08504          if (!res)
08505             res = ast_play_and_wait(chan, "vm-INBOX");
08506       }
08507       if (vms->oldmessages && !res)
08508          res = ast_play_and_wait(chan, "vm-and");
08509    }
08510    if (vms->oldmessages) {
08511       if (!res)
08512          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08513       if (vms->oldmessages == 1) {
08514          if (!res)
08515             res = ast_play_and_wait(chan, "vm-message");
08516          if (!res)
08517             res = ast_play_and_wait(chan, "vm-Olds");
08518       } else {
08519          if (!res)
08520             res = ast_play_and_wait(chan, "vm-messages");
08521          if (!res)
08522             res = ast_play_and_wait(chan, "vm-Old");
08523       }
08524    }
08525    return res;
08526 }
08527 
08528 /* FRENCH syntax */
08529 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08530 {
08531    /* Introduce messages they have */
08532    int res;
08533    res = ast_play_and_wait(chan, "vm-youhave");
08534    if (!res) {
08535       if (vms->newmessages) {
08536          res = say_and_wait(chan, vms->newmessages, chan->language);
08537          if (!res)
08538             res = ast_play_and_wait(chan, "vm-INBOX");
08539          if (vms->oldmessages && !res)
08540             res = ast_play_and_wait(chan, "vm-and");
08541          else if (!res) {
08542             if ((vms->newmessages == 1))
08543                res = ast_play_and_wait(chan, "vm-message");
08544             else
08545                res = ast_play_and_wait(chan, "vm-messages");
08546          }
08547             
08548       }
08549       if (!res && vms->oldmessages) {
08550          res = say_and_wait(chan, vms->oldmessages, chan->language);
08551          if (!res)
08552             res = ast_play_and_wait(chan, "vm-Old");
08553          if (!res) {
08554             if (vms->oldmessages == 1)
08555                res = ast_play_and_wait(chan, "vm-message");
08556             else
08557                res = ast_play_and_wait(chan, "vm-messages");
08558          }
08559       }
08560       if (!res) {
08561          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08562             res = ast_play_and_wait(chan, "vm-no");
08563             if (!res)
08564                res = ast_play_and_wait(chan, "vm-messages");
08565          }
08566       }
08567    }
08568    return res;
08569 }
08570 
08571 /* DUTCH syntax */
08572 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08573 {
08574    /* Introduce messages they have */
08575    int res;
08576    res = ast_play_and_wait(chan, "vm-youhave");
08577    if (!res) {
08578       if (vms->newmessages) {
08579          res = say_and_wait(chan, vms->newmessages, chan->language);
08580          if (!res) {
08581             if (vms->newmessages == 1)
08582                res = ast_play_and_wait(chan, "vm-INBOXs");
08583             else
08584                res = ast_play_and_wait(chan, "vm-INBOX");
08585          }
08586          if (vms->oldmessages && !res)
08587             res = ast_play_and_wait(chan, "vm-and");
08588          else if (!res) {
08589             if ((vms->newmessages == 1))
08590                res = ast_play_and_wait(chan, "vm-message");
08591             else
08592                res = ast_play_and_wait(chan, "vm-messages");
08593          }
08594             
08595       }
08596       if (!res && vms->oldmessages) {
08597          res = say_and_wait(chan, vms->oldmessages, chan->language);
08598          if (!res) {
08599             if (vms->oldmessages == 1)
08600                res = ast_play_and_wait(chan, "vm-Olds");
08601             else
08602                res = ast_play_and_wait(chan, "vm-Old");
08603          }
08604          if (!res) {
08605             if (vms->oldmessages == 1)
08606                res = ast_play_and_wait(chan, "vm-message");
08607             else
08608                res = ast_play_and_wait(chan, "vm-messages");
08609          }
08610       }
08611       if (!res) {
08612          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08613             res = ast_play_and_wait(chan, "vm-no");
08614             if (!res)
08615                res = ast_play_and_wait(chan, "vm-messages");
08616          }
08617       }
08618    }
08619    return res;
08620 }
08621 
08622 /* PORTUGUESE syntax */
08623 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08624 {
08625    /* Introduce messages they have */
08626    int res;
08627    res = ast_play_and_wait(chan, "vm-youhave");
08628    if (!res) {
08629       if (vms->newmessages) {
08630          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08631          if (!res) {
08632             if ((vms->newmessages == 1)) {
08633                res = ast_play_and_wait(chan, "vm-message");
08634                if (!res)
08635                   res = ast_play_and_wait(chan, "vm-INBOXs");
08636             } else {
08637                res = ast_play_and_wait(chan, "vm-messages");
08638                if (!res)
08639                   res = ast_play_and_wait(chan, "vm-INBOX");
08640             }
08641          }
08642          if (vms->oldmessages && !res)
08643             res = ast_play_and_wait(chan, "vm-and");
08644       }
08645       if (!res && vms->oldmessages) {
08646          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08647          if (!res) {
08648             if (vms->oldmessages == 1) {
08649                res = ast_play_and_wait(chan, "vm-message");
08650                if (!res)
08651                   res = ast_play_and_wait(chan, "vm-Olds");
08652             } else {
08653                res = ast_play_and_wait(chan, "vm-messages");
08654                if (!res)
08655                   res = ast_play_and_wait(chan, "vm-Old");
08656             }
08657          }
08658       }
08659       if (!res) {
08660          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08661             res = ast_play_and_wait(chan, "vm-no");
08662             if (!res)
08663                res = ast_play_and_wait(chan, "vm-messages");
08664          }
08665       }
08666    }
08667    return res;
08668 }
08669 
08670 
08671 /* CZECH syntax */
08672 /* in czech there must be declension of word new and message
08673  * czech        : english        : czech      : english
08674  * --------------------------------------------------------
08675  * vm-youhave   : you have 
08676  * vm-novou     : one new        : vm-zpravu  : message
08677  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08678  * vm-novych    : 5-infinite new : vm-zprav   : messages
08679  * vm-starou   : one old
08680  * vm-stare     : 2-4 old 
08681  * vm-starych   : 5-infinite old
08682  * jednu        : one   - falling 4. 
08683  * vm-no        : no  ( no messages )
08684  */
08685 
08686 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08687 {
08688    int res;
08689    res = ast_play_and_wait(chan, "vm-youhave");
08690    if (!res) {
08691       if (vms->newmessages) {
08692          if (vms->newmessages == 1) {
08693             res = ast_play_and_wait(chan, "digits/jednu");
08694          } else {
08695             res = say_and_wait(chan, vms->newmessages, chan->language);
08696          }
08697          if (!res) {
08698             if ((vms->newmessages == 1))
08699                res = ast_play_and_wait(chan, "vm-novou");
08700             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08701                res = ast_play_and_wait(chan, "vm-nove");
08702             if (vms->newmessages > 4)
08703                res = ast_play_and_wait(chan, "vm-novych");
08704          }
08705          if (vms->oldmessages && !res)
08706             res = ast_play_and_wait(chan, "vm-and");
08707          else if (!res) {
08708             if ((vms->newmessages == 1))
08709                res = ast_play_and_wait(chan, "vm-zpravu");
08710             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08711                res = ast_play_and_wait(chan, "vm-zpravy");
08712             if (vms->newmessages > 4)
08713                res = ast_play_and_wait(chan, "vm-zprav");
08714          }
08715       }
08716       if (!res && vms->oldmessages) {
08717          res = say_and_wait(chan, vms->oldmessages, chan->language);
08718          if (!res) {
08719             if ((vms->oldmessages == 1))
08720                res = ast_play_and_wait(chan, "vm-starou");
08721             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08722                res = ast_play_and_wait(chan, "vm-stare");
08723             if (vms->oldmessages > 4)
08724                res = ast_play_and_wait(chan, "vm-starych");
08725          }
08726          if (!res) {
08727             if ((vms->oldmessages == 1))
08728                res = ast_play_and_wait(chan, "vm-zpravu");
08729             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08730                res = ast_play_and_wait(chan, "vm-zpravy");
08731             if (vms->oldmessages > 4)
08732                res = ast_play_and_wait(chan, "vm-zprav");
08733          }
08734       }
08735       if (!res) {
08736          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08737             res = ast_play_and_wait(chan, "vm-no");
08738             if (!res)
08739                res = ast_play_and_wait(chan, "vm-zpravy");
08740          }
08741       }
08742    }
08743    return res;
08744 }
08745 
08746 /* CHINESE (Taiwan) syntax */
08747 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08748 {
08749    int res;
08750    /* Introduce messages they have */
08751    res = ast_play_and_wait(chan, "vm-you");
08752 
08753    if (!res && vms->newmessages) {
08754       res = ast_play_and_wait(chan, "vm-have");
08755       if (!res)
08756          res = say_and_wait(chan, vms->newmessages, chan->language);
08757       if (!res)
08758          res = ast_play_and_wait(chan, "vm-tong");
08759       if (!res)
08760          res = ast_play_and_wait(chan, "vm-INBOX");
08761       if (vms->oldmessages && !res)
08762          res = ast_play_and_wait(chan, "vm-and");
08763       else if (!res) 
08764          res = ast_play_and_wait(chan, "vm-messages");
08765    }
08766    if (!res && vms->oldmessages) {
08767       res = ast_play_and_wait(chan, "vm-have");
08768       if (!res)
08769          res = say_and_wait(chan, vms->oldmessages, chan->language);
08770       if (!res)
08771          res = ast_play_and_wait(chan, "vm-tong");
08772       if (!res)
08773          res = ast_play_and_wait(chan, "vm-Old");
08774       if (!res)
08775          res = ast_play_and_wait(chan, "vm-messages");
08776    }
08777    if (!res && !vms->oldmessages && !vms->newmessages) {
08778       res = ast_play_and_wait(chan, "vm-haveno");
08779       if (!res)
08780          res = ast_play_and_wait(chan, "vm-messages");
08781    }
08782    return res;
08783 }
08784 
08785 /* Vietnamese syntax */
08786 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08787 {
08788    int res;
08789 
08790    /* Introduce messages they have */
08791    res = ast_play_and_wait(chan, "vm-youhave");
08792    if (!res) {
08793       if (vms->newmessages) {
08794          res = say_and_wait(chan, vms->newmessages, chan->language);
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       }
08800       if (!res && vms->oldmessages) {
08801          res = say_and_wait(chan, vms->oldmessages, chan->language);
08802          if (!res)
08803             res = ast_play_and_wait(chan, "vm-Old");        
08804       }
08805       if (!res) {
08806          if (!vms->oldmessages && !vms->newmessages) {
08807             res = ast_play_and_wait(chan, "vm-no");
08808             if (!res)
08809                res = ast_play_and_wait(chan, "vm-message");
08810          }
08811       }
08812    }
08813    return res;
08814 }
08815 
08816 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08817 {
08818    char prefile[256];
08819    
08820    /* Notify the user that the temp greeting is set and give them the option to remove it */
08821    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08822    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08823       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08824       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08825          ast_play_and_wait(chan, "vm-tempgreetactive");
08826       }
08827       DISPOSE(prefile, -1);
08828    }
08829 
08830    /* Play voicemail intro - syntax is different for different languages */
08831    if (0) {
08832       return 0;
08833    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08834       return vm_intro_cs(chan, vms);
08835    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08836       static int deprecation_warning = 0;
08837       if (deprecation_warning++ % 10 == 0) {
08838          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08839       }
08840       return vm_intro_cs(chan, vms);
08841    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08842       return vm_intro_de(chan, vms);
08843    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08844       return vm_intro_es(chan, vms);
08845    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08846       return vm_intro_fr(chan, vms);
08847    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08848       return vm_intro_gr(chan, vms);
08849    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08850       return vm_intro_he(chan, vms);
08851    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08852       return vm_intro_it(chan, vms);
08853    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08854       return vm_intro_nl(chan, vms);
08855    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08856       return vm_intro_no(chan, vms);
08857    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08858       return vm_intro_pl(chan, vms);
08859    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08860       return vm_intro_pt_BR(chan, vms);
08861    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08862       return vm_intro_pt(chan, vms);
08863    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08864       return vm_intro_multilang(chan, vms, "n");
08865    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08866       return vm_intro_se(chan, vms);
08867    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08868       return vm_intro_multilang(chan, vms, "n");
08869    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
08870       return vm_intro_vi(chan, vms);
08871    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08872       return vm_intro_zh(chan, vms);
08873    } else {                                             /* Default to ENGLISH */
08874       return vm_intro_en(chan, vms);
08875    }
08876 }
08877 
08878 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08879 {
08880    int res = 0;
08881    /* Play instructions and wait for new command */
08882    while (!res) {
08883       if (vms->starting) {
08884          if (vms->lastmsg > -1) {
08885             if (skipadvanced)
08886                res = ast_play_and_wait(chan, "vm-onefor-full");
08887             else
08888                res = ast_play_and_wait(chan, "vm-onefor");
08889             if (!res)
08890                res = vm_play_folder_name(chan, vms->vmbox);
08891          }
08892          if (!res) {
08893             if (skipadvanced)
08894                res = ast_play_and_wait(chan, "vm-opts-full");
08895             else
08896                res = ast_play_and_wait(chan, "vm-opts");
08897          }
08898       } else {
08899          /* Added for additional help */
08900          if (skipadvanced) {
08901             res = ast_play_and_wait(chan, "vm-onefor-full");
08902             if (!res)
08903                res = vm_play_folder_name(chan, vms->vmbox);
08904             res = ast_play_and_wait(chan, "vm-opts-full");
08905          }
08906          /* Logic:
08907           * If the current message is not the first OR
08908           * if we're listening to the first new message and there are
08909           * also urgent messages, then prompt for navigation to the
08910           * previous message
08911           */
08912          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08913             res = ast_play_and_wait(chan, "vm-prev");
08914          }
08915          if (!res && !skipadvanced)
08916             res = ast_play_and_wait(chan, "vm-advopts");
08917          if (!res)
08918             res = ast_play_and_wait(chan, "vm-repeat");
08919          /* Logic:
08920           * If we're not listening to the last message OR
08921           * we're listening to the last urgent message and there are
08922           * also new non-urgent messages, then prompt for navigation
08923           * to the next message
08924           */
08925          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08926             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08927             res = ast_play_and_wait(chan, "vm-next");
08928          }
08929          if (!res) {
08930             if (!vms->deleted[vms->curmsg])
08931                res = ast_play_and_wait(chan, "vm-delete");
08932             else
08933                res = ast_play_and_wait(chan, "vm-undelete");
08934             if (!res)
08935                res = ast_play_and_wait(chan, "vm-toforward");
08936             if (!res)
08937                res = ast_play_and_wait(chan, "vm-savemessage");
08938          }
08939       }
08940       if (!res) {
08941          res = ast_play_and_wait(chan, "vm-helpexit");
08942       }
08943       if (!res)
08944          res = ast_waitfordigit(chan, 6000);
08945       if (!res) {
08946          vms->repeats++;
08947          if (vms->repeats > 2) {
08948             res = 't';
08949          }
08950       }
08951    }
08952    return res;
08953 }
08954 
08955 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08956 {
08957    int res = 0;
08958    /* Play instructions and wait for new command */
08959    while (!res) {
08960       if (vms->lastmsg > -1) {
08961          res = ast_play_and_wait(chan, "vm-listen");
08962          if (!res)
08963             res = vm_play_folder_name(chan, vms->vmbox);
08964          if (!res)
08965             res = ast_play_and_wait(chan, "press");
08966          if (!res)
08967             res = ast_play_and_wait(chan, "digits/1");
08968       }
08969       if (!res)
08970          res = ast_play_and_wait(chan, "vm-opts");
08971       if (!res) {
08972          vms->starting = 0;
08973          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08974       }
08975    }
08976    return res;
08977 }
08978 
08979 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08980 {
08981    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08982       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08983    } else {             /* Default to ENGLISH */
08984       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08985    }
08986 }
08987 
08988 
08989 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08990 {
08991    int cmd = 0;
08992    int duration = 0;
08993    int tries = 0;
08994    char newpassword[80] = "";
08995    char newpassword2[80] = "";
08996    char prefile[PATH_MAX] = "";
08997    unsigned char buf[256];
08998    int bytes = 0;
08999 
09000    if (ast_adsi_available(chan)) {
09001       bytes += adsi_logo(buf + bytes);
09002       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09003       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09004       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09005       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09006       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09007    }
09008 
09009    /* First, have the user change their password 
09010       so they won't get here again */
09011    for (;;) {
09012       newpassword[1] = '\0';
09013       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09014       if (cmd == '#')
09015          newpassword[0] = '\0';
09016       if (cmd < 0 || cmd == 't' || cmd == '#')
09017          return cmd;
09018       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09019       if (cmd < 0 || cmd == 't' || cmd == '#')
09020          return cmd;
09021       cmd = check_password(vmu, newpassword); /* perform password validation */
09022       if (cmd != 0) {
09023          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09024          cmd = ast_play_and_wait(chan, vm_invalid_password);
09025       } else {
09026          newpassword2[1] = '\0';
09027          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09028          if (cmd == '#')
09029             newpassword2[0] = '\0';
09030          if (cmd < 0 || cmd == 't' || cmd == '#')
09031             return cmd;
09032          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09033          if (cmd < 0 || cmd == 't' || cmd == '#')
09034             return cmd;
09035          if (!strcmp(newpassword, newpassword2))
09036             break;
09037          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09038          cmd = ast_play_and_wait(chan, vm_mismatch);
09039       }
09040       if (++tries == 3)
09041          return -1;
09042       if (cmd != 0) {
09043          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09044       }
09045    }
09046    if (pwdchange & PWDCHANGE_INTERNAL)
09047       vm_change_password(vmu, newpassword);
09048    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09049       vm_change_password_shell(vmu, newpassword);
09050 
09051    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09052    cmd = ast_play_and_wait(chan, vm_passchanged);
09053 
09054    /* If forcename is set, have the user record their name */  
09055    if (ast_test_flag(vmu, VM_FORCENAME)) {
09056       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09057       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09058          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09059          if (cmd < 0 || cmd == 't' || cmd == '#')
09060             return cmd;
09061       }
09062    }
09063 
09064    /* If forcegreetings is set, have the user record their greetings */
09065    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09066       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09067       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09068          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09069          if (cmd < 0 || cmd == 't' || cmd == '#')
09070             return cmd;
09071       }
09072 
09073       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09074       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09075          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09076          if (cmd < 0 || cmd == 't' || cmd == '#')
09077             return cmd;
09078       }
09079    }
09080 
09081    return cmd;
09082 }
09083 
09084 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09085 {
09086    int cmd = 0;
09087    int retries = 0;
09088    int duration = 0;
09089    char newpassword[80] = "";
09090    char newpassword2[80] = "";
09091    char prefile[PATH_MAX] = "";
09092    unsigned char buf[256];
09093    int bytes = 0;
09094 
09095    if (ast_adsi_available(chan)) {
09096       bytes += adsi_logo(buf + bytes);
09097       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09098       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09099       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09100       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09101       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09102    }
09103    while ((cmd >= 0) && (cmd != 't')) {
09104       if (cmd)
09105          retries = 0;
09106       switch (cmd) {
09107       case '1': /* Record your unavailable message */
09108          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09109          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09110          break;
09111       case '2':  /* Record your busy message */
09112          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09113          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09114          break;
09115       case '3': /* Record greeting */
09116          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09117          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09118          break;
09119       case '4':  /* manage the temporary greeting */
09120          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09121          break;
09122       case '5': /* change password */
09123          if (vmu->password[0] == '-') {
09124             cmd = ast_play_and_wait(chan, "vm-no");
09125             break;
09126          }
09127          newpassword[1] = '\0';
09128          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09129          if (cmd == '#')
09130             newpassword[0] = '\0';
09131          else {
09132             if (cmd < 0)
09133                break;
09134             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09135                break;
09136             }
09137          }
09138          cmd = check_password(vmu, newpassword); /* perform password validation */
09139          if (cmd != 0) {
09140             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09141             cmd = ast_play_and_wait(chan, vm_invalid_password);
09142             if (!cmd) {
09143                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09144             }
09145             break;
09146          }
09147          newpassword2[1] = '\0';
09148          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09149          if (cmd == '#')
09150             newpassword2[0] = '\0';
09151          else {
09152             if (cmd < 0)
09153                break;
09154 
09155             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09156                break;
09157             }
09158          }
09159          if (strcmp(newpassword, newpassword2)) {
09160             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09161             cmd = ast_play_and_wait(chan, vm_mismatch);
09162             if (!cmd) {
09163                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09164             }
09165             break;
09166          }
09167          if (pwdchange & PWDCHANGE_INTERNAL)
09168             vm_change_password(vmu, newpassword);
09169          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09170             vm_change_password_shell(vmu, newpassword);
09171 
09172          ast_debug(1, "User %s set password to %s of length %d\n",
09173             vms->username, newpassword, (int) strlen(newpassword));
09174          cmd = ast_play_and_wait(chan, vm_passchanged);
09175          break;
09176       case '*': 
09177          cmd = 't';
09178          break;
09179       default: 
09180          cmd = 0;
09181          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09182          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09183          if (ast_fileexists(prefile, NULL, NULL)) {
09184             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09185          }
09186          DISPOSE(prefile, -1);
09187          if (!cmd) {
09188             cmd = ast_play_and_wait(chan, "vm-options");
09189          }
09190          if (!cmd) {
09191             cmd = ast_waitfordigit(chan, 6000);
09192          }
09193          if (!cmd) {
09194             retries++;
09195          }
09196          if (retries > 3) {
09197             cmd = 't';
09198          }
09199       }
09200    }
09201    if (cmd == 't')
09202       cmd = 0;
09203    return cmd;
09204 }
09205 
09206 /*!
09207  * \brief The handler for 'record a temporary greeting'. 
09208  * \param chan
09209  * \param vmu
09210  * \param vms
09211  * \param fmtc
09212  * \param record_gain
09213  *
09214  * This is option 4 from the mailbox options menu.
09215  * This function manages the following promptings:
09216  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09217  * 2: remove (delete) the temporary greeting.
09218  * *: return to the main menu.
09219  *
09220  * \return zero on success, -1 on error.
09221  */
09222 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09223 {
09224    int cmd = 0;
09225    int retries = 0;
09226    int duration = 0;
09227    char prefile[PATH_MAX] = "";
09228    unsigned char buf[256];
09229    int bytes = 0;
09230 
09231    if (ast_adsi_available(chan)) {
09232       bytes += adsi_logo(buf + bytes);
09233       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09234       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09235       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09236       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09237       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09238    }
09239 
09240    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09241    while ((cmd >= 0) && (cmd != 't')) {
09242       if (cmd)
09243          retries = 0;
09244       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09245       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09246          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09247          cmd = 't';  
09248       } else {
09249          switch (cmd) {
09250          case '1':
09251             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09252             break;
09253          case '2':
09254             DELETE(prefile, -1, prefile, vmu);
09255             ast_play_and_wait(chan, "vm-tempremoved");
09256             cmd = 't';  
09257             break;
09258          case '*': 
09259             cmd = 't';
09260             break;
09261          default:
09262             cmd = ast_play_and_wait(chan,
09263                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09264                   "vm-tempgreeting2" : "vm-tempgreeting");
09265             if (!cmd)
09266                cmd = ast_waitfordigit(chan, 6000);
09267             if (!cmd)
09268                retries++;
09269             if (retries > 3)
09270                cmd = 't';
09271          }
09272       }
09273       DISPOSE(prefile, -1);
09274    }
09275    if (cmd == 't')
09276       cmd = 0;
09277    return cmd;
09278 }
09279 
09280 /*!
09281  * \brief Greek syntax for 'You have N messages' greeting.
09282  * \param chan
09283  * \param vms
09284  * \param vmu
09285  *
09286  * \return zero on success, -1 on error.
09287  */   
09288 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09289 {
09290    int cmd = 0;
09291 
09292    if (vms->lastmsg > -1) {
09293       cmd = play_message(chan, vmu, vms);
09294    } else {
09295       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09296       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09297          if (!cmd) {
09298             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09299             cmd = ast_play_and_wait(chan, vms->fn);
09300          }
09301          if (!cmd)
09302             cmd = ast_play_and_wait(chan, "vm-messages");
09303       } else {
09304          if (!cmd)
09305             cmd = ast_play_and_wait(chan, "vm-messages");
09306          if (!cmd) {
09307             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09308             cmd = ast_play_and_wait(chan, vms->fn);
09309          }
09310       }
09311    } 
09312    return cmd;
09313 }
09314 
09315 /* Hebrew Syntax */
09316 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09317 {
09318    int cmd = 0;
09319 
09320    if (vms->lastmsg > -1) {
09321       cmd = play_message(chan, vmu, vms);
09322    } else {
09323       if (!strcasecmp(vms->fn, "INBOX")) {
09324          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09325       } else {
09326          cmd = ast_play_and_wait(chan, "vm-nomessages");
09327       }
09328    }
09329    return cmd;
09330 }
09331 
09332 /*! 
09333  * \brief Default English syntax for 'You have N messages' greeting.
09334  * \param chan
09335  * \param vms
09336  * \param vmu
09337  *
09338  * \return zero on success, -1 on error.
09339  */
09340 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09341 {
09342    int cmd = 0;
09343 
09344    if (vms->lastmsg > -1) {
09345       cmd = play_message(chan, vmu, vms);
09346    } else {
09347       cmd = ast_play_and_wait(chan, "vm-youhave");
09348       if (!cmd) 
09349          cmd = ast_play_and_wait(chan, "vm-no");
09350       if (!cmd) {
09351          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09352          cmd = ast_play_and_wait(chan, vms->fn);
09353       }
09354       if (!cmd)
09355          cmd = ast_play_and_wait(chan, "vm-messages");
09356    }
09357    return cmd;
09358 }
09359 
09360 /*! 
09361  *\brief Italian syntax for 'You have N messages' greeting.
09362  * \param chan
09363  * \param vms
09364  * \param vmu
09365  *
09366  * \return zero on success, -1 on error.
09367  */
09368 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09369 {
09370    int cmd;
09371 
09372    if (vms->lastmsg > -1) {
09373       cmd = play_message(chan, vmu, vms);
09374    } else {
09375       cmd = ast_play_and_wait(chan, "vm-no");
09376       if (!cmd)
09377          cmd = ast_play_and_wait(chan, "vm-message");
09378       if (!cmd) {
09379          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09380          cmd = ast_play_and_wait(chan, vms->fn);
09381       }
09382    }
09383    return cmd;
09384 }
09385 
09386 /*! 
09387  * \brief Spanish syntax for 'You have N messages' greeting.
09388  * \param chan
09389  * \param vms
09390  * \param vmu
09391  *
09392  * \return zero on success, -1 on error.
09393  */
09394 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09395 {
09396    int cmd;
09397 
09398    if (vms->lastmsg > -1) {
09399       cmd = play_message(chan, vmu, vms);
09400    } else {
09401       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09402       if (!cmd)
09403          cmd = ast_play_and_wait(chan, "vm-messages");
09404       if (!cmd) {
09405          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09406          cmd = ast_play_and_wait(chan, vms->fn);
09407       }
09408    }
09409    return cmd;
09410 }
09411 
09412 /*! 
09413  * \brief Portuguese syntax for 'You have N messages' greeting.
09414  * \param chan
09415  * \param vms
09416  * \param vmu
09417  *
09418  * \return zero on success, -1 on error.
09419  */
09420 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09421 {
09422    int cmd;
09423 
09424    if (vms->lastmsg > -1) {
09425       cmd = play_message(chan, vmu, vms);
09426    } else {
09427       cmd = ast_play_and_wait(chan, "vm-no");
09428       if (!cmd) {
09429          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09430          cmd = ast_play_and_wait(chan, vms->fn);
09431       }
09432       if (!cmd)
09433          cmd = ast_play_and_wait(chan, "vm-messages");
09434    }
09435    return cmd;
09436 }
09437 
09438 /*! 
09439  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09440  * \param chan
09441  * \param vms
09442  * \param vmu
09443  *
09444  * \return zero on success, -1 on error.
09445  */
09446 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09447 {
09448    int cmd;
09449 
09450    if (vms->lastmsg > -1) {
09451       cmd = play_message(chan, vmu, vms);
09452    } else {
09453       cmd = ast_play_and_wait(chan, "vm-you");
09454       if (!cmd) 
09455          cmd = ast_play_and_wait(chan, "vm-haveno");
09456       if (!cmd)
09457          cmd = ast_play_and_wait(chan, "vm-messages");
09458       if (!cmd) {
09459          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09460          cmd = ast_play_and_wait(chan, vms->fn);
09461       }
09462    }
09463    return cmd;
09464 }
09465 
09466 /*! 
09467  * \brief Vietnamese syntax for 'You have N messages' greeting.
09468  * \param chan
09469  * \param vms
09470  * \param vmu
09471  *
09472  * \return zero on success, -1 on error.
09473  */
09474 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09475 {
09476    int cmd = 0;
09477 
09478    if (vms->lastmsg > -1) {
09479       cmd = play_message(chan, vmu, vms);
09480    } else {
09481       cmd = ast_play_and_wait(chan, "vm-no");
09482       if (!cmd) {
09483          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09484          cmd = ast_play_and_wait(chan, vms->fn);
09485       }
09486    }
09487    return cmd;
09488 }
09489 
09490 /*!
09491  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09492  * \param chan The channel for the current user. We read the language property from this.
09493  * \param vms passed into the language-specific vm_browse_messages function.
09494  * \param vmu passed into the language-specific vm_browse_messages function.
09495  * 
09496  * The method to be invoked is determined by the value of language code property in the user's channel.
09497  * The default (when unable to match) is to use english.
09498  *
09499  * \return zero on success, -1 on error.
09500  */
09501 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09502 {
09503    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09504       return vm_browse_messages_es(chan, vms, vmu);
09505    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09506       return vm_browse_messages_gr(chan, vms, vmu);
09507    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09508       return vm_browse_messages_he(chan, vms, vmu);
09509    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09510       return vm_browse_messages_it(chan, vms, vmu);
09511    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09512       return vm_browse_messages_pt(chan, vms, vmu);
09513    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09514       return vm_browse_messages_vi(chan, vms, vmu);
09515    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09516       return vm_browse_messages_zh(chan, vms, vmu);
09517    } else {                                             /* Default to English syntax */
09518       return vm_browse_messages_en(chan, vms, vmu);
09519    }
09520 }
09521 
09522 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09523          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09524          int skipuser, int max_logins, int silent)
09525 {
09526    int useadsi = 0, valid = 0, logretries = 0;
09527    char password[AST_MAX_EXTENSION]="", *passptr;
09528    struct ast_vm_user vmus, *vmu = NULL;
09529 
09530    /* If ADSI is supported, setup login screen */
09531    adsi_begin(chan, &useadsi);
09532    if (!skipuser && useadsi)
09533       adsi_login(chan);
09534    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09535       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09536       return -1;
09537    }
09538    
09539    /* Authenticate them and get their mailbox/password */
09540    
09541    while (!valid && (logretries < max_logins)) {
09542       /* Prompt for, and read in the username */
09543       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09544          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09545          return -1;
09546       }
09547       if (ast_strlen_zero(mailbox)) {
09548          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09549             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09550          } else {
09551             ast_verb(3, "Username not entered\n"); 
09552             return -1;
09553          }
09554       } else if (mailbox[0] == '*') {
09555          /* user entered '*' */
09556          if (ast_exists_extension(chan, chan->context, "a", 1,
09557             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09558             return -1;
09559          }
09560          mailbox[0] = '\0';
09561       }
09562 
09563       if (useadsi)
09564          adsi_password(chan);
09565 
09566       if (!ast_strlen_zero(prefix)) {
09567          char fullusername[80] = "";
09568          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09569          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09570          ast_copy_string(mailbox, fullusername, mailbox_size);
09571       }
09572 
09573       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09574       vmu = find_user(&vmus, context, mailbox);
09575       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09576          /* saved password is blank, so don't bother asking */
09577          password[0] = '\0';
09578       } else {
09579          if (ast_streamfile(chan, vm_password, chan->language)) {
09580             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09581             return -1;
09582          }
09583          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09584             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09585             return -1;
09586          } else if (password[0] == '*') {
09587             /* user entered '*' */
09588             if (ast_exists_extension(chan, chan->context, "a", 1,
09589                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09590                mailbox[0] = '*';
09591                return -1;
09592             }
09593             mailbox[0] = '\0';
09594          }
09595       }
09596 
09597       if (vmu) {
09598          passptr = vmu->password;
09599          if (passptr[0] == '-') passptr++;
09600       }
09601       if (vmu && !strcmp(passptr, password))
09602          valid++;
09603       else {
09604          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09605          if (!ast_strlen_zero(prefix))
09606             mailbox[0] = '\0';
09607       }
09608       logretries++;
09609       if (!valid) {
09610          if (skipuser || logretries >= max_logins) {
09611             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09612                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09613                return -1;
09614             }
09615          } else {
09616             if (useadsi)
09617                adsi_login(chan);
09618             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09619                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09620                return -1;
09621             }
09622          }
09623          if (ast_waitstream(chan, "")) /* Channel is hung up */
09624             return -1;
09625       }
09626    }
09627    if (!valid && (logretries >= max_logins)) {
09628       ast_stopstream(chan);
09629       ast_play_and_wait(chan, "vm-goodbye");
09630       return -1;
09631    }
09632    if (vmu && !skipuser) {
09633       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09634    }
09635    return 0;
09636 }
09637 
09638 static int vm_execmain(struct ast_channel *chan, const char *data)
09639 {
09640    /* XXX This is, admittedly, some pretty horrendous code.  For some
09641       reason it just seemed a lot easier to do with GOTO's.  I feel
09642       like I'm back in my GWBASIC days. XXX */
09643    int res = -1;
09644    int cmd = 0;
09645    int valid = 0;
09646    char prefixstr[80] ="";
09647    char ext_context[256]="";
09648    int box;
09649    int useadsi = 0;
09650    int skipuser = 0;
09651    struct vm_state vms;
09652    struct ast_vm_user *vmu = NULL, vmus;
09653    char *context = NULL;
09654    int silentexit = 0;
09655    struct ast_flags flags = { 0 };
09656    signed char record_gain = 0;
09657    int play_auto = 0;
09658    int play_folder = 0;
09659    int in_urgent = 0;
09660 #ifdef IMAP_STORAGE
09661    int deleted = 0;
09662 #endif
09663 
09664    /* Add the vm_state to the active list and keep it active */
09665    memset(&vms, 0, sizeof(vms));
09666 
09667    vms.lastmsg = -1;
09668 
09669    memset(&vmus, 0, sizeof(vmus));
09670 
09671    if (chan->_state != AST_STATE_UP) {
09672       ast_debug(1, "Before ast_answer\n");
09673       ast_answer(chan);
09674    }
09675 
09676    if (!ast_strlen_zero(data)) {
09677       char *opts[OPT_ARG_ARRAY_SIZE];
09678       char *parse;
09679       AST_DECLARE_APP_ARGS(args,
09680          AST_APP_ARG(argv0);
09681          AST_APP_ARG(argv1);
09682       );
09683 
09684       parse = ast_strdupa(data);
09685 
09686       AST_STANDARD_APP_ARGS(args, parse);
09687 
09688       if (args.argc == 2) {
09689          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09690             return -1;
09691          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09692             int gain;
09693             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09694                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09695                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09696                   return -1;
09697                } else {
09698                   record_gain = (signed char) gain;
09699                }
09700             } else {
09701                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09702             }
09703          }
09704          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09705             play_auto = 1;
09706             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09707                /* See if it is a folder name first */
09708                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09709                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09710                      play_folder = -1;
09711                   }
09712                } else {
09713                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09714                }
09715             } else {
09716                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09717             }
09718             if (play_folder > 9 || play_folder < 0) {
09719                ast_log(AST_LOG_WARNING,
09720                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09721                   opts[OPT_ARG_PLAYFOLDER]);
09722                play_folder = 0;
09723             }
09724          }
09725       } else {
09726          /* old style options parsing */
09727          while (*(args.argv0)) {
09728             if (*(args.argv0) == 's')
09729                ast_set_flag(&flags, OPT_SILENT);
09730             else if (*(args.argv0) == 'p')
09731                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09732             else 
09733                break;
09734             (args.argv0)++;
09735          }
09736 
09737       }
09738 
09739       valid = ast_test_flag(&flags, OPT_SILENT);
09740 
09741       if ((context = strchr(args.argv0, '@')))
09742          *context++ = '\0';
09743 
09744       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09745          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09746       else
09747          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09748 
09749       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09750          skipuser++;
09751       else
09752          valid = 0;
09753    }
09754 
09755    if (!valid)
09756       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09757 
09758    ast_debug(1, "After vm_authenticate\n");
09759 
09760    if (vms.username[0] == '*') {
09761       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09762 
09763       /* user entered '*' */
09764       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09765          res = 0; /* prevent hangup */
09766          goto out;
09767       }
09768    }
09769 
09770    if (!res) {
09771       valid = 1;
09772       if (!skipuser)
09773          vmu = &vmus;
09774    } else {
09775       res = 0;
09776    }
09777 
09778    /* If ADSI is supported, setup login screen */
09779    adsi_begin(chan, &useadsi);
09780 
09781    if (!valid) {
09782       goto out;
09783    }
09784 
09785 #ifdef IMAP_STORAGE
09786    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09787    pthread_setspecific(ts_vmstate.key, &vms);
09788 
09789    vms.interactive = 1;
09790    vms.updated = 1;
09791    if (vmu)
09792       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09793    vmstate_insert(&vms);
09794    init_vm_state(&vms);
09795 #endif
09796    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09797    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09798       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09799       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09800       return -1;
09801    }
09802    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09803       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09804       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09805       return -1;
09806    }
09807    
09808    /* Set language from config to override channel language */
09809    if (!ast_strlen_zero(vmu->language))
09810       ast_string_field_set(chan, language, vmu->language);
09811 
09812    /* Retrieve urgent, old and new message counts */
09813    ast_debug(1, "Before open_mailbox\n");
09814    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09815    if (res < 0)
09816       goto out;
09817    vms.oldmessages = vms.lastmsg + 1;
09818    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
09819    /* check INBOX */
09820    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09821    if (res < 0)
09822       goto out;
09823    vms.newmessages = vms.lastmsg + 1;
09824    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
09825    /* Start in Urgent */
09826    in_urgent = 1;
09827    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09828    if (res < 0)
09829       goto out;
09830    vms.urgentmessages = vms.lastmsg + 1;
09831    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
09832 
09833    /* Select proper mailbox FIRST!! */
09834    if (play_auto) {
09835       if (vms.urgentmessages) {
09836          in_urgent = 1;
09837          res = open_mailbox(&vms, vmu, 11);
09838       } else {
09839          in_urgent = 0;
09840          res = open_mailbox(&vms, vmu, play_folder);
09841       }
09842       if (res < 0)
09843          goto out;
09844 
09845       /* If there are no new messages, inform the user and hangup */
09846       if (vms.lastmsg == -1) {
09847          in_urgent = 0;
09848          cmd = vm_browse_messages(chan, &vms, vmu);
09849          res = 0;
09850          goto out;
09851       }
09852    } else {
09853       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09854          /* If we only have old messages start here */
09855          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09856          in_urgent = 0;
09857          play_folder = 1;
09858          if (res < 0)
09859             goto out;
09860       } else if (!vms.urgentmessages && vms.newmessages) {
09861          /* If we have new messages but none are urgent */
09862          in_urgent = 0;
09863          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09864          if (res < 0)
09865             goto out;
09866       }
09867    }
09868 
09869    if (useadsi)
09870       adsi_status(chan, &vms);
09871    res = 0;
09872 
09873    /* Check to see if this is a new user */
09874    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09875       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09876       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09877          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09878       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09879       if ((cmd == 't') || (cmd == '#')) {
09880          /* Timeout */
09881          res = 0;
09882          goto out;
09883       } else if (cmd < 0) {
09884          /* Hangup */
09885          res = -1;
09886          goto out;
09887       }
09888    }
09889 #ifdef IMAP_STORAGE
09890       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
09891       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09892          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09893          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09894       }
09895       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09896       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09897          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09898          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09899       }
09900 #endif
09901    if (play_auto) {
09902       cmd = '1';
09903    } else {
09904       cmd = vm_intro(chan, vmu, &vms);
09905    }
09906 
09907    vms.repeats = 0;
09908    vms.starting = 1;
09909    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09910       /* Run main menu */
09911       switch (cmd) {
09912       case '1': /* First message */
09913          vms.curmsg = 0;
09914          /* Fall through */
09915       case '5': /* Play current message */
09916          cmd = vm_browse_messages(chan, &vms, vmu);
09917          break;
09918       case '2': /* Change folders */
09919          if (useadsi)
09920             adsi_folders(chan, 0, "Change to folder...");
09921          cmd = get_folder2(chan, "vm-changeto", 0);
09922          if (cmd == '#') {
09923             cmd = 0;
09924          } else if (cmd > 0) {
09925             cmd = cmd - '0';
09926             res = close_mailbox(&vms, vmu);
09927             if (res == ERROR_LOCK_PATH)
09928                goto out;
09929             /* If folder is not urgent, set in_urgent to zero! */
09930             if (cmd != 11) in_urgent = 0;
09931             res = open_mailbox(&vms, vmu, cmd);
09932             if (res < 0)
09933                goto out;
09934             play_folder = cmd;
09935             cmd = 0;
09936          }
09937          if (useadsi)
09938             adsi_status2(chan, &vms);
09939             
09940          if (!cmd)
09941             cmd = vm_play_folder_name(chan, vms.vmbox);
09942 
09943          vms.starting = 1;
09944          break;
09945       case '3': /* Advanced options */
09946          cmd = 0;
09947          vms.repeats = 0;
09948          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09949             switch (cmd) {
09950             case '1': /* Reply */
09951                if (vms.lastmsg > -1 && !vms.starting) {
09952                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09953                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09954                      res = cmd;
09955                      goto out;
09956                   }
09957                } else
09958                   cmd = ast_play_and_wait(chan, "vm-sorry");
09959                cmd = 't';
09960                break;
09961             case '2': /* Callback */
09962                if (!vms.starting)
09963                   ast_verb(3, "Callback Requested\n");
09964                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09965                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09966                   if (cmd == 9) {
09967                      silentexit = 1;
09968                      goto out;
09969                   } else if (cmd == ERROR_LOCK_PATH) {
09970                      res = cmd;
09971                      goto out;
09972                   }
09973                } else 
09974                   cmd = ast_play_and_wait(chan, "vm-sorry");
09975                cmd = 't';
09976                break;
09977             case '3': /* Envelope */
09978                if (vms.lastmsg > -1 && !vms.starting) {
09979                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09980                   if (cmd == ERROR_LOCK_PATH) {
09981                      res = cmd;
09982                      goto out;
09983                   }
09984                } else
09985                   cmd = ast_play_and_wait(chan, "vm-sorry");
09986                cmd = 't';
09987                break;
09988             case '4': /* Dialout */
09989                if (!ast_strlen_zero(vmu->dialout)) {
09990                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09991                   if (cmd == 9) {
09992                      silentexit = 1;
09993                      goto out;
09994                   }
09995                } else 
09996                   cmd = ast_play_and_wait(chan, "vm-sorry");
09997                cmd = 't';
09998                break;
09999 
10000             case '5': /* Leave VoiceMail */
10001                if (ast_test_flag(vmu, VM_SVMAIL)) {
10002                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10003                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10004                      res = cmd;
10005                      goto out;
10006                   }
10007                } else
10008                   cmd = ast_play_and_wait(chan, "vm-sorry");
10009                cmd = 't';
10010                break;
10011                
10012             case '*': /* Return to main menu */
10013                cmd = 't';
10014                break;
10015 
10016             default:
10017                cmd = 0;
10018                if (!vms.starting) {
10019                   cmd = ast_play_and_wait(chan, "vm-toreply");
10020                }
10021                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10022                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10023                }
10024                if (!cmd && !vms.starting) {
10025                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10026                }
10027                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10028                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10029                }
10030                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
10031                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10032                if (!cmd)
10033                   cmd = ast_play_and_wait(chan, "vm-starmain");
10034                if (!cmd)
10035                   cmd = ast_waitfordigit(chan, 6000);
10036                if (!cmd)
10037                   vms.repeats++;
10038                if (vms.repeats > 3)
10039                   cmd = 't';
10040             }
10041          }
10042          if (cmd == 't') {
10043             cmd = 0;
10044             vms.repeats = 0;
10045          }
10046          break;
10047       case '4': /* Go to the previous message */
10048          if (vms.curmsg > 0) {
10049             vms.curmsg--;
10050             cmd = play_message(chan, vmu, &vms);
10051          } else {
10052             /* Check if we were listening to new
10053                messages.  If so, go to Urgent messages
10054                instead of saying "no more messages"
10055             */
10056             if (in_urgent == 0 && vms.urgentmessages > 0) {
10057                /* Check for Urgent messages */
10058                in_urgent = 1;
10059                res = close_mailbox(&vms, vmu);
10060                if (res == ERROR_LOCK_PATH)
10061                   goto out;
10062                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10063                if (res < 0)
10064                   goto out;
10065                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10066                vms.curmsg = vms.lastmsg;
10067                if (vms.lastmsg < 0)
10068                   cmd = ast_play_and_wait(chan, "vm-nomore");
10069             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10070                vms.curmsg = vms.lastmsg;
10071                cmd = play_message(chan, vmu, &vms);
10072             } else {
10073                cmd = ast_play_and_wait(chan, "vm-nomore");
10074             }
10075          }
10076          break;
10077       case '6': /* Go to the next message */
10078          if (vms.curmsg < vms.lastmsg) {
10079             vms.curmsg++;
10080             cmd = play_message(chan, vmu, &vms);
10081          } else {
10082             if (in_urgent && vms.newmessages > 0) {
10083                /* Check if we were listening to urgent
10084                 * messages.  If so, go to regular new messages
10085                 * instead of saying "no more messages"
10086                 */
10087                in_urgent = 0;
10088                res = close_mailbox(&vms, vmu);
10089                if (res == ERROR_LOCK_PATH)
10090                   goto out;
10091                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10092                if (res < 0)
10093                   goto out;
10094                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10095                vms.curmsg = -1;
10096                if (vms.lastmsg < 0) {
10097                   cmd = ast_play_and_wait(chan, "vm-nomore");
10098                }
10099             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10100                vms.curmsg = 0;
10101                cmd = play_message(chan, vmu, &vms);
10102             } else {
10103                cmd = ast_play_and_wait(chan, "vm-nomore");
10104             }
10105          }
10106          break;
10107       case '7': /* Delete the current message */
10108          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10109             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10110             if (useadsi)
10111                adsi_delete(chan, &vms);
10112             if (vms.deleted[vms.curmsg]) {
10113                if (play_folder == 0) {
10114                   if (in_urgent) {
10115                      vms.urgentmessages--;
10116                   } else {
10117                      vms.newmessages--;
10118                   }
10119                }
10120                else if (play_folder == 1)
10121                   vms.oldmessages--;
10122                cmd = ast_play_and_wait(chan, "vm-deleted");
10123             } else {
10124                if (play_folder == 0) {
10125                   if (in_urgent) {
10126                      vms.urgentmessages++;
10127                   } else {
10128                      vms.newmessages++;
10129                   }
10130                }
10131                else if (play_folder == 1)
10132                   vms.oldmessages++;
10133                cmd = ast_play_and_wait(chan, "vm-undeleted");
10134             }
10135             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10136                if (vms.curmsg < vms.lastmsg) {
10137                   vms.curmsg++;
10138                   cmd = play_message(chan, vmu, &vms);
10139                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10140                   vms.curmsg = 0;
10141                   cmd = play_message(chan, vmu, &vms);
10142                } else {
10143                   /* Check if we were listening to urgent
10144                      messages.  If so, go to regular new messages
10145                      instead of saying "no more messages"
10146                   */
10147                   if (in_urgent == 1) {
10148                      /* Check for new messages */
10149                      in_urgent = 0;
10150                      res = close_mailbox(&vms, vmu);
10151                      if (res == ERROR_LOCK_PATH)
10152                         goto out;
10153                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10154                      if (res < 0)
10155                         goto out;
10156                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10157                      vms.curmsg = -1;
10158                      if (vms.lastmsg < 0)
10159                         cmd = ast_play_and_wait(chan, "vm-nomore");
10160                   } else {
10161                      cmd = ast_play_and_wait(chan, "vm-nomore");
10162                   }
10163                }
10164             }
10165          } else /* Delete not valid if we haven't selected a message */
10166             cmd = 0;
10167 #ifdef IMAP_STORAGE
10168          deleted = 1;
10169 #endif
10170          break;
10171    
10172       case '8': /* Forward the current messgae */
10173          if (vms.lastmsg > -1) {
10174             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10175             if (cmd == ERROR_LOCK_PATH) {
10176                res = cmd;
10177                goto out;
10178             }
10179          } else {
10180             /* Check if we were listening to urgent
10181                messages.  If so, go to regular new messages
10182                instead of saying "no more messages"
10183             */
10184             if (in_urgent == 1 && vms.newmessages > 0) {
10185                /* Check for new messages */
10186                in_urgent = 0;
10187                res = close_mailbox(&vms, vmu);
10188                if (res == ERROR_LOCK_PATH)
10189                   goto out;
10190                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10191                if (res < 0)
10192                   goto out;
10193                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10194                vms.curmsg = -1;
10195                if (vms.lastmsg < 0)
10196                   cmd = ast_play_and_wait(chan, "vm-nomore");
10197             } else {
10198                cmd = ast_play_and_wait(chan, "vm-nomore");
10199             }
10200          }
10201          break;
10202       case '9': /* Save message to folder */
10203          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10204             /* No message selected */
10205             cmd = 0;
10206             break;
10207          }
10208          if (useadsi)
10209             adsi_folders(chan, 1, "Save to folder...");
10210          cmd = get_folder2(chan, "vm-savefolder", 1);
10211          box = 0; /* Shut up compiler */
10212          if (cmd == '#') {
10213             cmd = 0;
10214             break;
10215          } else if (cmd > 0) {
10216             box = cmd = cmd - '0';
10217             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10218             if (cmd == ERROR_LOCK_PATH) {
10219                res = cmd;
10220                goto out;
10221 #ifndef IMAP_STORAGE
10222             } else if (!cmd) {
10223                vms.deleted[vms.curmsg] = 1;
10224 #endif
10225             } else {
10226                vms.deleted[vms.curmsg] = 0;
10227                vms.heard[vms.curmsg] = 0;
10228             }
10229          }
10230          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10231          if (useadsi)
10232             adsi_message(chan, &vms);
10233          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10234          if (!cmd) {
10235             cmd = ast_play_and_wait(chan, "vm-message");
10236             if (!cmd) 
10237                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10238             if (!cmd)
10239                cmd = ast_play_and_wait(chan, "vm-savedto");
10240             if (!cmd)
10241                cmd = vm_play_folder_name(chan, vms.fn);
10242          } else {
10243             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10244          }
10245          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10246             if (vms.curmsg < vms.lastmsg) {
10247                vms.curmsg++;
10248                cmd = play_message(chan, vmu, &vms);
10249             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10250                vms.curmsg = 0;
10251                cmd = play_message(chan, vmu, &vms);
10252             } else {
10253                /* Check if we were listening to urgent
10254                   messages.  If so, go to regular new messages
10255                   instead of saying "no more messages"
10256                */
10257                if (in_urgent == 1 && vms.newmessages > 0) {
10258                   /* Check for new messages */
10259                   in_urgent = 0;
10260                   res = close_mailbox(&vms, vmu);
10261                   if (res == ERROR_LOCK_PATH)
10262                      goto out;
10263                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10264                   if (res < 0)
10265                      goto out;
10266                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10267                   vms.curmsg = -1;
10268                   if (vms.lastmsg < 0)
10269                      cmd = ast_play_and_wait(chan, "vm-nomore");
10270                } else {
10271                   cmd = ast_play_and_wait(chan, "vm-nomore");
10272                }
10273             }
10274          }
10275          break;
10276       case '*': /* Help */
10277          if (!vms.starting) {
10278             cmd = ast_play_and_wait(chan, "vm-onefor");
10279             if (!strncasecmp(chan->language, "he", 2)) {
10280                cmd = ast_play_and_wait(chan, "vm-for");
10281             }
10282             if (!cmd)
10283                cmd = vm_play_folder_name(chan, vms.vmbox);
10284             if (!cmd)
10285                cmd = ast_play_and_wait(chan, "vm-opts");
10286             if (!cmd)
10287                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10288          } else
10289             cmd = 0;
10290          break;
10291       case '0': /* Mailbox options */
10292          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10293          if (useadsi)
10294             adsi_status(chan, &vms);
10295          break;
10296       default: /* Nothing */
10297          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10298          break;
10299       }
10300    }
10301    if ((cmd == 't') || (cmd == '#')) {
10302       /* Timeout */
10303       res = 0;
10304    } else {
10305       /* Hangup */
10306       res = -1;
10307    }
10308 
10309 out:
10310    if (res > -1) {
10311       ast_stopstream(chan);
10312       adsi_goodbye(chan);
10313       if (valid && res != OPERATOR_EXIT) {
10314          if (silentexit)
10315             res = ast_play_and_wait(chan, "vm-dialout");
10316          else 
10317             res = ast_play_and_wait(chan, "vm-goodbye");
10318       }
10319       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10320          res = 0;
10321       }
10322       if (useadsi)
10323          ast_adsi_unload_session(chan);
10324    }
10325    if (vmu)
10326       close_mailbox(&vms, vmu);
10327    if (valid) {
10328       int new = 0, old = 0, urgent = 0;
10329       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10330       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10331       /* Urgent flag not passwd to externnotify here */
10332       run_externnotify(vmu->context, vmu->mailbox, NULL);
10333       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10334       queue_mwi_event(ext_context, urgent, new, old);
10335    }
10336 #ifdef IMAP_STORAGE
10337    /* expunge message - use UID Expunge if supported on IMAP server*/
10338    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10339    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10340       ast_mutex_lock(&vms.lock);
10341 #ifdef HAVE_IMAP_TK2006
10342       if (LEVELUIDPLUS (vms.mailstream)) {
10343          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10344       } else 
10345 #endif
10346          mail_expunge(vms.mailstream);
10347       ast_mutex_unlock(&vms.lock);
10348    }
10349    /*  before we delete the state, we should copy pertinent info
10350     *  back to the persistent model */
10351    if (vmu) {
10352       vmstate_delete(&vms);
10353    }
10354 #endif
10355    if (vmu)
10356       free_user(vmu);
10357    if (vms.deleted)
10358       ast_free(vms.deleted);
10359    if (vms.heard)
10360       ast_free(vms.heard);
10361 
10362 #ifdef IMAP_STORAGE
10363    pthread_setspecific(ts_vmstate.key, NULL);
10364 #endif
10365    return res;
10366 }
10367 
10368 static int vm_exec(struct ast_channel *chan, const char *data)
10369 {
10370    int res = 0;
10371    char *tmp;
10372    struct leave_vm_options leave_options;
10373    struct ast_flags flags = { 0 };
10374    char *opts[OPT_ARG_ARRAY_SIZE];
10375    AST_DECLARE_APP_ARGS(args,
10376       AST_APP_ARG(argv0);
10377       AST_APP_ARG(argv1);
10378    );
10379    
10380    memset(&leave_options, 0, sizeof(leave_options));
10381 
10382    if (chan->_state != AST_STATE_UP)
10383       ast_answer(chan);
10384 
10385    if (!ast_strlen_zero(data)) {
10386       tmp = ast_strdupa(data);
10387       AST_STANDARD_APP_ARGS(args, tmp);
10388       if (args.argc == 2) {
10389          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10390             return -1;
10391          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10392          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10393             int gain;
10394 
10395             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10396                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10397                return -1;
10398             } else {
10399                leave_options.record_gain = (signed char) gain;
10400             }
10401          }
10402          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10403             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10404                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10405          }
10406       }
10407    } else {
10408       char temp[256];
10409       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10410       if (res < 0)
10411          return res;
10412       if (ast_strlen_zero(temp))
10413          return 0;
10414       args.argv0 = ast_strdupa(temp);
10415    }
10416 
10417    res = leave_voicemail(chan, args.argv0, &leave_options);
10418    if (res == 't') {
10419       ast_play_and_wait(chan, "vm-goodbye");
10420       res = 0;
10421    }
10422 
10423    if (res == OPERATOR_EXIT) {
10424       res = 0;
10425    }
10426 
10427    if (res == ERROR_LOCK_PATH) {
10428       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10429       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10430       res = 0;
10431    }
10432 
10433    return res;
10434 }
10435 
10436 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10437 {
10438    struct ast_vm_user *vmu;
10439 
10440    AST_LIST_TRAVERSE(&users, vmu, list) {
10441       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10442          if (strcasecmp(vmu->context, context)) {
10443             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10444                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10445                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10446                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10447          }
10448          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10449          return NULL;
10450       }
10451       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10452          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10453          return NULL;
10454       }
10455    }
10456    
10457    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10458       return NULL;
10459    
10460    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10461    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10462 
10463    AST_LIST_INSERT_TAIL(&users, vmu, list);
10464    
10465    return vmu;
10466 }
10467 
10468 static int append_mailbox(const char *context, const char *box, const char *data)
10469 {
10470    /* Assumes lock is already held */
10471    char *tmp;
10472    char *stringp;
10473    char *s;
10474    struct ast_vm_user *vmu;
10475    char *mailbox_full;
10476    int new = 0, old = 0, urgent = 0;
10477    char secretfn[PATH_MAX] = "";
10478 
10479    tmp = ast_strdupa(data);
10480 
10481    if (!(vmu = find_or_create(context, box)))
10482       return -1;
10483 
10484    populate_defaults(vmu);
10485 
10486    stringp = tmp;
10487    if ((s = strsep(&stringp, ","))) {
10488       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10489    }
10490    if (stringp && (s = strsep(&stringp, ","))) {
10491       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10492    }
10493    if (stringp && (s = strsep(&stringp, ","))) {
10494       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10495    }
10496    if (stringp && (s = strsep(&stringp, ","))) {
10497       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10498    }
10499    if (stringp && (s = strsep(&stringp, ","))) {
10500       apply_options(vmu, s);
10501    }
10502 
10503    switch (vmu->passwordlocation) {
10504    case OPT_PWLOC_SPOOLDIR:
10505       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10506       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10507    }
10508 
10509    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10510    strcpy(mailbox_full, box);
10511    strcat(mailbox_full, "@");
10512    strcat(mailbox_full, context);
10513 
10514    inboxcount2(mailbox_full, &urgent, &new, &old);
10515    queue_mwi_event(mailbox_full, urgent, new, old);
10516 
10517    return 0;
10518 }
10519 
10520 AST_TEST_DEFINE(test_voicemail_vmuser)
10521 {
10522    int res = 0;
10523    struct ast_vm_user *vmu;
10524    /* language parameter seems to only be used for display in manager action */
10525    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10526       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10527       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10528       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10529       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10530       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10531       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir";
10532 #ifdef IMAP_STORAGE
10533    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10534       "imapfolder=INBOX|imapvmshareid=6000";
10535 #endif
10536 
10537    switch (cmd) {
10538    case TEST_INIT:
10539       info->name = "vmuser";
10540       info->category = "/apps/app_voicemail/";
10541       info->summary = "Vmuser unit test";
10542       info->description =
10543          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10544       return AST_TEST_NOT_RUN;
10545    case TEST_EXECUTE:
10546       break;
10547    }
10548 
10549    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10550       return AST_TEST_NOT_RUN;
10551    }
10552    ast_set_flag(vmu, VM_ALLOCED);
10553 
10554    apply_options(vmu, options_string);
10555 
10556    if (!ast_test_flag(vmu, VM_ATTACH)) {
10557       ast_test_status_update(test, "Parse failure for attach option\n");
10558       res = 1;
10559    }
10560    if (strcasecmp(vmu->attachfmt, "wav49")) {
10561       ast_test_status_update(test, "Parse failure for attachftm option\n");
10562       res = 1;
10563    }
10564    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10565       ast_test_status_update(test, "Parse failure for serveremail option\n");
10566       res = 1;
10567    }
10568    if (strcasecmp(vmu->zonetag, "central")) {
10569       ast_test_status_update(test, "Parse failure for tz option\n");
10570       res = 1;
10571    }
10572    if (!ast_test_flag(vmu, VM_DELETE)) {
10573       ast_test_status_update(test, "Parse failure for delete option\n");
10574       res = 1;
10575    }
10576    if (!ast_test_flag(vmu, VM_SAYCID)) {
10577       ast_test_status_update(test, "Parse failure for saycid option\n");
10578       res = 1;
10579    }
10580    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10581       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10582       res = 1;
10583    }
10584    if (!ast_test_flag(vmu, VM_REVIEW)) {
10585       ast_test_status_update(test, "Parse failure for review option\n");
10586       res = 1;
10587    }
10588    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10589       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10590       res = 1;
10591    }
10592    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10593       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10594       res = 1;
10595    }
10596    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10597       ast_test_status_update(test, "Parse failure for operator option\n");
10598       res = 1;
10599    }
10600    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10601       ast_test_status_update(test, "Parse failure for envelope option\n");
10602       res = 1;
10603    }
10604    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10605       ast_test_status_update(test, "Parse failure for moveheard option\n");
10606       res = 1;
10607    }
10608    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10609       ast_test_status_update(test, "Parse failure for sayduration option\n");
10610       res = 1;
10611    }
10612    if (vmu->saydurationm != 5) {
10613       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10614       res = 1;
10615    }
10616    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10617       ast_test_status_update(test, "Parse failure for forcename option\n");
10618       res = 1;
10619    }
10620    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10621       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10622       res = 1;
10623    }
10624    if (strcasecmp(vmu->callback, "somecontext")) {
10625       ast_test_status_update(test, "Parse failure for callbacks option\n");
10626       res = 1;
10627    }
10628    if (strcasecmp(vmu->dialout, "somecontext2")) {
10629       ast_test_status_update(test, "Parse failure for dialout option\n");
10630       res = 1;
10631    }
10632    if (strcasecmp(vmu->exit, "somecontext3")) {
10633       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10634       res = 1;
10635    }
10636    if (vmu->minsecs != 10) {
10637       ast_test_status_update(test, "Parse failure for minsecs option\n");
10638       res = 1;
10639    }
10640    if (vmu->maxsecs != 100) {
10641       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10642       res = 1;
10643    }
10644    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10645       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10646       res = 1;
10647    }
10648    if (vmu->maxdeletedmsg != 50) {
10649       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10650       res = 1;
10651    }
10652    if (vmu->volgain != 1.3) {
10653       ast_test_status_update(test, "Parse failure for volgain option\n");
10654       res = 1;
10655    }
10656    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10657       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10658       res = 1;
10659    }
10660 #ifdef IMAP_STORAGE
10661    apply_options(vmu, option_string2);
10662 
10663    if (strcasecmp(vmu->imapuser, "imapuser")) {
10664       ast_test_status_update(test, "Parse failure for imapuser option\n");
10665       res = 1;
10666    }
10667    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10668       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10669       res = 1;
10670    }
10671    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10672       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10673       res = 1;
10674    }
10675    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10676       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10677       res = 1;
10678    }
10679 #endif
10680 
10681    free_user(vmu);
10682    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10683 }
10684 
10685 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10686 {
10687    struct ast_vm_user svm;
10688    char *context, *box;
10689    AST_DECLARE_APP_ARGS(args,
10690       AST_APP_ARG(mbox);
10691       AST_APP_ARG(options);
10692    );
10693    static int dep_warning = 0;
10694 
10695    if (ast_strlen_zero(data)) {
10696       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10697       return -1;
10698    }
10699 
10700    if (!dep_warning) {
10701       dep_warning = 1;
10702       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10703    }
10704 
10705    box = ast_strdupa(data);
10706 
10707    AST_STANDARD_APP_ARGS(args, box);
10708 
10709    if (args.options) {
10710    }
10711 
10712    if ((context = strchr(args.mbox, '@'))) {
10713       *context = '\0';
10714       context++;
10715    }
10716 
10717    if (find_user(&svm, context, args.mbox)) {
10718       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10719    } else
10720       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10721 
10722    return 0;
10723 }
10724 
10725 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10726 {
10727    struct ast_vm_user svm;
10728    AST_DECLARE_APP_ARGS(arg,
10729       AST_APP_ARG(mbox);
10730       AST_APP_ARG(context);
10731    );
10732 
10733    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10734 
10735    if (ast_strlen_zero(arg.mbox)) {
10736       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10737       return -1;
10738    }
10739 
10740    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10741    return 0;
10742 }
10743 
10744 static struct ast_custom_function mailbox_exists_acf = {
10745    .name = "MAILBOX_EXISTS",
10746    .read = acf_mailbox_exists,
10747 };
10748 
10749 static int vmauthenticate(struct ast_channel *chan, const char *data)
10750 {
10751    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
10752    struct ast_vm_user vmus;
10753    char *options = NULL;
10754    int silent = 0, skipuser = 0;
10755    int res = -1;
10756    
10757    if (data) {
10758       s = ast_strdupa(data);
10759       user = strsep(&s, ",");
10760       options = strsep(&s, ",");
10761       if (user) {
10762          s = user;
10763          user = strsep(&s, "@");
10764          context = strsep(&s, "");
10765          if (!ast_strlen_zero(user))
10766             skipuser++;
10767          ast_copy_string(mailbox, user, sizeof(mailbox));
10768       }
10769    }
10770 
10771    if (options) {
10772       silent = (strchr(options, 's')) != NULL;
10773    }
10774 
10775    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10776       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10777       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10778       ast_play_and_wait(chan, "auth-thankyou");
10779       res = 0;
10780    } else if (mailbox[0] == '*') {
10781       /* user entered '*' */
10782       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10783          res = 0; /* prevent hangup */
10784       }
10785    }
10786 
10787    return res;
10788 }
10789 
10790 static char *show_users_realtime(int fd, const char *context)
10791 {
10792    struct ast_config *cfg;
10793    const char *cat = NULL;
10794 
10795    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10796       "context", context, SENTINEL))) {
10797       return CLI_FAILURE;
10798    }
10799 
10800    ast_cli(fd,
10801       "\n"
10802       "=============================================================\n"
10803       "=== Configured Voicemail Users ==============================\n"
10804       "=============================================================\n"
10805       "===\n");
10806 
10807    while ((cat = ast_category_browse(cfg, cat))) {
10808       struct ast_variable *var = NULL;
10809       ast_cli(fd,
10810          "=== Mailbox ...\n"
10811          "===\n");
10812       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10813          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10814       ast_cli(fd,
10815          "===\n"
10816          "=== ---------------------------------------------------------\n"
10817          "===\n");
10818    }
10819 
10820    ast_cli(fd,
10821       "=============================================================\n"
10822       "\n");
10823 
10824    ast_config_destroy(cfg);
10825 
10826    return CLI_SUCCESS;
10827 }
10828 
10829 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10830 {
10831    int which = 0;
10832    int wordlen;
10833    struct ast_vm_user *vmu;
10834    const char *context = "";
10835 
10836    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10837    if (pos > 4)
10838       return NULL;
10839    if (pos == 3)
10840       return (state == 0) ? ast_strdup("for") : NULL;
10841    wordlen = strlen(word);
10842    AST_LIST_TRAVERSE(&users, vmu, list) {
10843       if (!strncasecmp(word, vmu->context, wordlen)) {
10844          if (context && strcmp(context, vmu->context) && ++which > state)
10845             return ast_strdup(vmu->context);
10846          /* ignore repeated contexts ? */
10847          context = vmu->context;
10848       }
10849    }
10850    return NULL;
10851 }
10852 
10853 /*! \brief Show a list of voicemail users in the CLI */
10854 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10855 {
10856    struct ast_vm_user *vmu;
10857 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10858    const char *context = NULL;
10859    int users_counter = 0;
10860 
10861    switch (cmd) {
10862    case CLI_INIT:
10863       e->command = "voicemail show users";
10864       e->usage =
10865          "Usage: voicemail show users [for <context>]\n"
10866          "       Lists all mailboxes currently set up\n";
10867       return NULL;
10868    case CLI_GENERATE:
10869       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10870    }  
10871 
10872    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10873       return CLI_SHOWUSAGE;
10874    if (a->argc == 5) {
10875       if (strcmp(a->argv[3],"for"))
10876          return CLI_SHOWUSAGE;
10877       context = a->argv[4];
10878    }
10879 
10880    if (ast_check_realtime("voicemail")) {
10881       if (!context) {
10882          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10883          return CLI_SHOWUSAGE;
10884       }
10885       return show_users_realtime(a->fd, context);
10886    }
10887 
10888    AST_LIST_LOCK(&users);
10889    if (AST_LIST_EMPTY(&users)) {
10890       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10891       AST_LIST_UNLOCK(&users);
10892       return CLI_FAILURE;
10893    }
10894    if (a->argc == 3)
10895       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10896    else {
10897       int count = 0;
10898       AST_LIST_TRAVERSE(&users, vmu, list) {
10899          if (!strcmp(context, vmu->context))
10900             count++;
10901       }
10902       if (count) {
10903          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10904       } else {
10905          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10906          AST_LIST_UNLOCK(&users);
10907          return CLI_FAILURE;
10908       }
10909    }
10910    AST_LIST_TRAVERSE(&users, vmu, list) {
10911       int newmsgs = 0, oldmsgs = 0;
10912       char count[12], tmp[256] = "";
10913 
10914       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10915          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10916          inboxcount(tmp, &newmsgs, &oldmsgs);
10917          snprintf(count, sizeof(count), "%d", newmsgs);
10918          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10919          users_counter++;
10920       }
10921    }
10922    AST_LIST_UNLOCK(&users);
10923    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10924    return CLI_SUCCESS;
10925 }
10926 
10927 /*! \brief Show a list of voicemail zones in the CLI */
10928 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10929 {
10930    struct vm_zone *zone;
10931 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10932    char *res = CLI_SUCCESS;
10933 
10934    switch (cmd) {
10935    case CLI_INIT:
10936       e->command = "voicemail show zones";
10937       e->usage =
10938          "Usage: voicemail show zones\n"
10939          "       Lists zone message formats\n";
10940       return NULL;
10941    case CLI_GENERATE:
10942       return NULL;
10943    }
10944 
10945    if (a->argc != 3)
10946       return CLI_SHOWUSAGE;
10947 
10948    AST_LIST_LOCK(&zones);
10949    if (!AST_LIST_EMPTY(&zones)) {
10950       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10951       AST_LIST_TRAVERSE(&zones, zone, list) {
10952          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10953       }
10954    } else {
10955       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10956       res = CLI_FAILURE;
10957    }
10958    AST_LIST_UNLOCK(&zones);
10959 
10960    return res;
10961 }
10962 
10963 /*! \brief Reload voicemail configuration from the CLI */
10964 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10965 {
10966    switch (cmd) {
10967    case CLI_INIT:
10968       e->command = "voicemail reload";
10969       e->usage =
10970          "Usage: voicemail reload\n"
10971          "       Reload voicemail configuration\n";
10972       return NULL;
10973    case CLI_GENERATE:
10974       return NULL;
10975    }
10976 
10977    if (a->argc != 2)
10978       return CLI_SHOWUSAGE;
10979 
10980    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10981    load_config(1);
10982    
10983    return CLI_SUCCESS;
10984 }
10985 
10986 static struct ast_cli_entry cli_voicemail[] = {
10987    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10988    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10989    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10990 };
10991 
10992 #ifdef IMAP_STORAGE
10993    #define DATA_EXPORT_VM_USERS(USER)              \
10994       USER(ast_vm_user, context, AST_DATA_STRING)        \
10995       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
10996       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
10997       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
10998       USER(ast_vm_user, email, AST_DATA_STRING)       \
10999       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11000       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11001       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11002       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11003       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11004       USER(ast_vm_user, language, AST_DATA_STRING)       \
11005       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11006       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11007       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11008       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11009       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11010       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11011       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11012       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11013       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11014       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11015       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11016       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11017       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11018       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11019       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11020 #else
11021    #define DATA_EXPORT_VM_USERS(USER)              \
11022       USER(ast_vm_user, context, AST_DATA_STRING)        \
11023       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11024       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11025       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11026       USER(ast_vm_user, email, AST_DATA_STRING)       \
11027       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11028       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11029       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11030       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11031       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11032       USER(ast_vm_user, language, AST_DATA_STRING)       \
11033       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11034       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11035       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11036       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11037       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11038       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11039       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11040       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11041       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11042       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11043       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11044       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11045 #endif
11046 
11047 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11048 
11049 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11050    ZONE(vm_zone, name, AST_DATA_STRING)      \
11051    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11052    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11053 
11054 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11055 
11056 /*!
11057  * \internal
11058  * \brief Add voicemail user to the data_root.
11059  * \param[in] search The search tree.
11060  * \param[in] data_root The main result node.
11061  * \param[in] user The voicemail user.
11062  */
11063 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11064     struct ast_data *data_root, struct ast_vm_user *user)
11065 {
11066    struct ast_data *data_user, *data_zone;
11067    struct ast_data *data_state;
11068    struct vm_zone *zone = NULL;
11069    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11070    char ext_context[256] = "";
11071 
11072    data_user = ast_data_add_node(data_root, "user");
11073    if (!data_user) {
11074       return -1;
11075    }
11076 
11077    ast_data_add_structure(ast_vm_user, data_user, user);
11078 
11079    AST_LIST_LOCK(&zones);
11080    AST_LIST_TRAVERSE(&zones, zone, list) {
11081       if (!strcmp(zone->name, user->zonetag)) {
11082          break;
11083       }
11084    }
11085    AST_LIST_UNLOCK(&zones);
11086 
11087    /* state */
11088    data_state = ast_data_add_node(data_user, "state");
11089    if (!data_state) {
11090       return -1;
11091    }
11092    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11093    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11094    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11095    ast_data_add_int(data_state, "newmsg", newmsg);
11096    ast_data_add_int(data_state, "oldmsg", oldmsg);
11097 
11098    if (zone) {
11099       data_zone = ast_data_add_node(data_user, "zone");
11100       ast_data_add_structure(vm_zone, data_zone, zone);
11101    }
11102 
11103    if (!ast_data_search_match(search, data_user)) {
11104       ast_data_remove_node(data_root, data_user);
11105    }
11106 
11107    return 0;
11108 }
11109 
11110 static int vm_users_data_provider_get(const struct ast_data_search *search,
11111    struct ast_data *data_root)
11112 {
11113    struct ast_vm_user *user;
11114 
11115    AST_LIST_LOCK(&users);
11116    AST_LIST_TRAVERSE(&users, user, list) {
11117       vm_users_data_provider_get_helper(search, data_root, user);
11118    }
11119    AST_LIST_UNLOCK(&users);
11120 
11121    return 0;
11122 }
11123 
11124 static const struct ast_data_handler vm_users_data_provider = {
11125    .version = AST_DATA_HANDLER_VERSION,
11126    .get = vm_users_data_provider_get
11127 };
11128 
11129 static const struct ast_data_entry vm_data_providers[] = {
11130    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11131 };
11132 
11133 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11134 {
11135    int new = 0, old = 0, urgent = 0;
11136 
11137    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11138 
11139    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11140       mwi_sub->old_urgent = urgent;
11141       mwi_sub->old_new = new;
11142       mwi_sub->old_old = old;
11143       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11144       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11145    }
11146 }
11147 
11148 static void poll_subscribed_mailboxes(void)
11149 {
11150    struct mwi_sub *mwi_sub;
11151 
11152    AST_RWLIST_RDLOCK(&mwi_subs);
11153    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11154       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11155          poll_subscribed_mailbox(mwi_sub);
11156       }
11157    }
11158    AST_RWLIST_UNLOCK(&mwi_subs);
11159 }
11160 
11161 static void *mb_poll_thread(void *data)
11162 {
11163    while (poll_thread_run) {
11164       struct timespec ts = { 0, };
11165       struct timeval wait;
11166 
11167       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11168       ts.tv_sec = wait.tv_sec;
11169       ts.tv_nsec = wait.tv_usec * 1000;
11170 
11171       ast_mutex_lock(&poll_lock);
11172       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11173       ast_mutex_unlock(&poll_lock);
11174 
11175       if (!poll_thread_run)
11176          break;
11177 
11178       poll_subscribed_mailboxes();
11179    }
11180 
11181    return NULL;
11182 }
11183 
11184 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11185 {
11186    ast_free(mwi_sub);
11187 }
11188 
11189 static int handle_unsubscribe(void *datap)
11190 {
11191    struct mwi_sub *mwi_sub;
11192    uint32_t *uniqueid = datap;
11193    
11194    AST_RWLIST_WRLOCK(&mwi_subs);
11195    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11196       if (mwi_sub->uniqueid == *uniqueid) {
11197          AST_LIST_REMOVE_CURRENT(entry);
11198          break;
11199       }
11200    }
11201    AST_RWLIST_TRAVERSE_SAFE_END
11202    AST_RWLIST_UNLOCK(&mwi_subs);
11203 
11204    if (mwi_sub)
11205       mwi_sub_destroy(mwi_sub);
11206 
11207    ast_free(uniqueid);  
11208    return 0;
11209 }
11210 
11211 static int handle_subscribe(void *datap)
11212 {
11213    unsigned int len;
11214    struct mwi_sub *mwi_sub;
11215    struct mwi_sub_task *p = datap;
11216 
11217    len = sizeof(*mwi_sub);
11218    if (!ast_strlen_zero(p->mailbox))
11219       len += strlen(p->mailbox);
11220 
11221    if (!ast_strlen_zero(p->context))
11222       len += strlen(p->context) + 1; /* Allow for seperator */
11223 
11224    if (!(mwi_sub = ast_calloc(1, len)))
11225       return -1;
11226 
11227    mwi_sub->uniqueid = p->uniqueid;
11228    if (!ast_strlen_zero(p->mailbox))
11229       strcpy(mwi_sub->mailbox, p->mailbox);
11230 
11231    if (!ast_strlen_zero(p->context)) {
11232       strcat(mwi_sub->mailbox, "@");
11233       strcat(mwi_sub->mailbox, p->context);
11234    }
11235 
11236    AST_RWLIST_WRLOCK(&mwi_subs);
11237    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11238    AST_RWLIST_UNLOCK(&mwi_subs);
11239    ast_free((void *) p->mailbox);
11240    ast_free((void *) p->context);
11241    ast_free(p);
11242    poll_subscribed_mailbox(mwi_sub);
11243    return 0;
11244 }
11245 
11246 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11247 {
11248    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11249    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11250       return;
11251 
11252    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11253       return;
11254 
11255    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11256    *uniqueid = u;
11257    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11258       ast_free(uniqueid);
11259    }
11260 }
11261 
11262 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11263 {
11264    struct mwi_sub_task *mwist;
11265    
11266    if (ast_event_get_type(event) != AST_EVENT_SUB)
11267       return;
11268 
11269    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11270       return;
11271 
11272    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11273       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11274       return;
11275    }
11276    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11277    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11278    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11279    
11280    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11281       ast_free(mwist);
11282    }
11283 }
11284 
11285 static void start_poll_thread(void)
11286 {
11287    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11288       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11289       AST_EVENT_IE_END);
11290 
11291    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11292       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11293       AST_EVENT_IE_END);
11294 
11295    if (mwi_sub_sub)
11296       ast_event_report_subs(mwi_sub_sub);
11297 
11298    poll_thread_run = 1;
11299 
11300    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11301 }
11302 
11303 static void stop_poll_thread(void)
11304 {
11305    poll_thread_run = 0;
11306 
11307    if (mwi_sub_sub) {
11308       ast_event_unsubscribe(mwi_sub_sub);
11309       mwi_sub_sub = NULL;
11310    }
11311 
11312    if (mwi_unsub_sub) {
11313       ast_event_unsubscribe(mwi_unsub_sub);
11314       mwi_unsub_sub = NULL;
11315    }
11316 
11317    ast_mutex_lock(&poll_lock);
11318    ast_cond_signal(&poll_cond);
11319    ast_mutex_unlock(&poll_lock);
11320 
11321    pthread_join(poll_thread, NULL);
11322 
11323    poll_thread = AST_PTHREADT_NULL;
11324 }
11325 
11326 /*! \brief Manager list voicemail users command */
11327 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11328 {
11329    struct ast_vm_user *vmu = NULL;
11330    const char *id = astman_get_header(m, "ActionID");
11331    char actionid[128] = "";
11332 
11333    if (!ast_strlen_zero(id))
11334       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11335 
11336    AST_LIST_LOCK(&users);
11337 
11338    if (AST_LIST_EMPTY(&users)) {
11339       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11340       AST_LIST_UNLOCK(&users);
11341       return RESULT_SUCCESS;
11342    }
11343    
11344    astman_send_ack(s, m, "Voicemail user list will follow");
11345    
11346    AST_LIST_TRAVERSE(&users, vmu, list) {
11347       char dirname[256];
11348 
11349 #ifdef IMAP_STORAGE
11350       int new, old;
11351       inboxcount(vmu->mailbox, &new, &old);
11352 #endif
11353       
11354       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11355       astman_append(s,
11356          "%s"
11357          "Event: VoicemailUserEntry\r\n"
11358          "VMContext: %s\r\n"
11359          "VoiceMailbox: %s\r\n"
11360          "Fullname: %s\r\n"
11361          "Email: %s\r\n"
11362          "Pager: %s\r\n"
11363          "ServerEmail: %s\r\n"
11364          "MailCommand: %s\r\n"
11365          "Language: %s\r\n"
11366          "TimeZone: %s\r\n"
11367          "Callback: %s\r\n"
11368          "Dialout: %s\r\n"
11369          "UniqueID: %s\r\n"
11370          "ExitContext: %s\r\n"
11371          "SayDurationMinimum: %d\r\n"
11372          "SayEnvelope: %s\r\n"
11373          "SayCID: %s\r\n"
11374          "AttachMessage: %s\r\n"
11375          "AttachmentFormat: %s\r\n"
11376          "DeleteMessage: %s\r\n"
11377          "VolumeGain: %.2f\r\n"
11378          "CanReview: %s\r\n"
11379          "CallOperator: %s\r\n"
11380          "MaxMessageCount: %d\r\n"
11381          "MaxMessageLength: %d\r\n"
11382          "NewMessageCount: %d\r\n"
11383 #ifdef IMAP_STORAGE
11384          "OldMessageCount: %d\r\n"
11385          "IMAPUser: %s\r\n"
11386 #endif
11387          "\r\n",
11388          actionid,
11389          vmu->context,
11390          vmu->mailbox,
11391          vmu->fullname,
11392          vmu->email,
11393          vmu->pager,
11394          vmu->serveremail,
11395          vmu->mailcmd,
11396          vmu->language,
11397          vmu->zonetag,
11398          vmu->callback,
11399          vmu->dialout,
11400          vmu->uniqueid,
11401          vmu->exit,
11402          vmu->saydurationm,
11403          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11404          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11405          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11406          vmu->attachfmt,
11407          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11408          vmu->volgain,
11409          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11410          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11411          vmu->maxmsg,
11412          vmu->maxsecs,
11413 #ifdef IMAP_STORAGE
11414          new, old, vmu->imapuser
11415 #else
11416          count_messages(vmu, dirname)
11417 #endif
11418          );
11419    }     
11420    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11421 
11422    AST_LIST_UNLOCK(&users);
11423 
11424    return RESULT_SUCCESS;
11425 }
11426 
11427 /*! \brief Free the users structure. */
11428 static void free_vm_users(void) 
11429 {
11430    struct ast_vm_user *current;
11431    AST_LIST_LOCK(&users);
11432    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11433       ast_set_flag(current, VM_ALLOCED);
11434       free_user(current);
11435    }
11436    AST_LIST_UNLOCK(&users);
11437 }
11438 
11439 /*! \brief Free the zones structure. */
11440 static void free_vm_zones(void)
11441 {
11442    struct vm_zone *zcur;
11443    AST_LIST_LOCK(&zones);
11444    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11445       free_zone(zcur);
11446    AST_LIST_UNLOCK(&zones);
11447 }
11448 
11449 static const char *substitute_escapes(const char *value)
11450 {
11451    char *current;
11452 
11453    /* Add 16 for fudge factor */
11454    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11455 
11456    ast_str_reset(str);
11457    
11458    /* Substitute strings \r, \n, and \t into the appropriate characters */
11459    for (current = (char *) value; *current; current++) {
11460       if (*current == '\\') {
11461          current++;
11462          if (!*current) {
11463             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11464             break;
11465          }
11466          switch (*current) {
11467          case 'r':
11468             ast_str_append(&str, 0, "\r");
11469             break;
11470          case 'n':
11471 #ifdef IMAP_STORAGE
11472             if (!str->used || str->str[str->used - 1] != '\r') {
11473                ast_str_append(&str, 0, "\r");
11474             }
11475 #endif
11476             ast_str_append(&str, 0, "\n");
11477             break;
11478          case 't':
11479             ast_str_append(&str, 0, "\t");
11480             break;
11481          default:
11482             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11483             break;
11484          }
11485       } else {
11486          ast_str_append(&str, 0, "%c", *current);
11487       }
11488    }
11489 
11490    return ast_str_buffer(str);
11491 }
11492 
11493 static int load_config(int reload)
11494 {
11495    struct ast_vm_user *current;
11496    struct ast_config *cfg, *ucfg;
11497    char *cat;
11498    struct ast_variable *var;
11499    const char *val;
11500    char *q, *stringp, *tmp;
11501    int x;
11502    int tmpadsi[4];
11503    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11504    char secretfn[PATH_MAX] = "";
11505 
11506    ast_unload_realtime("voicemail");
11507    ast_unload_realtime("voicemail_data");
11508 
11509    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11510       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11511          return 0;
11512       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11513          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11514          ucfg = NULL;
11515       }
11516       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11517       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11518          ast_config_destroy(ucfg);
11519          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11520          return 0;
11521       }
11522    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11523       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11524       return 0;
11525    } else {
11526       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11527       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11528          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11529          ucfg = NULL;
11530       }
11531    }
11532 #ifdef IMAP_STORAGE
11533    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11534 #endif
11535    /* set audio control prompts */
11536    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11537    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11538    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11539    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11540    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11541 
11542    /* Free all the users structure */  
11543    free_vm_users();
11544 
11545    /* Free all the zones structure */
11546    free_vm_zones();
11547 
11548    AST_LIST_LOCK(&users);  
11549 
11550    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11551    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11552 
11553    if (cfg) {
11554       /* General settings */
11555 
11556       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11557          val = "default";
11558       ast_copy_string(userscontext, val, sizeof(userscontext));
11559       /* Attach voice message to mail message ? */
11560       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11561          val = "yes";
11562       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11563 
11564       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11565          val = "no";
11566       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11567 
11568       volgain = 0.0;
11569       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11570          sscanf(val, "%30lf", &volgain);
11571 
11572 #ifdef ODBC_STORAGE
11573       strcpy(odbc_database, "asterisk");
11574       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11575          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11576       }
11577       strcpy(odbc_table, "voicemessages");
11578       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11579          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11580       }
11581 #endif      
11582       /* Mail command */
11583       strcpy(mailcmd, SENDMAIL);
11584       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11585          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11586 
11587       maxsilence = 0;
11588       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11589          maxsilence = atoi(val);
11590          if (maxsilence > 0)
11591             maxsilence *= 1000;
11592       }
11593       
11594       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11595          maxmsg = MAXMSG;
11596       } else {
11597          maxmsg = atoi(val);
11598          if (maxmsg < 0) {
11599             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11600             maxmsg = MAXMSG;
11601          } else if (maxmsg > MAXMSGLIMIT) {
11602             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11603             maxmsg = MAXMSGLIMIT;
11604          }
11605       }
11606 
11607       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11608          maxdeletedmsg = 0;
11609       } else {
11610          if (sscanf(val, "%30d", &x) == 1)
11611             maxdeletedmsg = x;
11612          else if (ast_true(val))
11613             maxdeletedmsg = MAXMSG;
11614          else
11615             maxdeletedmsg = 0;
11616 
11617          if (maxdeletedmsg < 0) {
11618             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11619             maxdeletedmsg = MAXMSG;
11620          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11621             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11622             maxdeletedmsg = MAXMSGLIMIT;
11623          }
11624       }
11625 
11626       /* Load date format config for voicemail mail */
11627       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11628          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11629       }
11630 
11631       /* Load date format config for voicemail pager mail */
11632       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11633          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11634       }
11635 
11636       /* External password changing command */
11637       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11638          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11639          pwdchange = PWDCHANGE_EXTERNAL;
11640       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11641          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11642          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11643       }
11644  
11645       /* External password validation command */
11646       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11647          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11648          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11649       }
11650 
11651 #ifdef IMAP_STORAGE
11652       /* IMAP server address */
11653       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11654          ast_copy_string(imapserver, val, sizeof(imapserver));
11655       } else {
11656          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11657       }
11658       /* IMAP server port */
11659       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11660          ast_copy_string(imapport, val, sizeof(imapport));
11661       } else {
11662          ast_copy_string(imapport, "143", sizeof(imapport));
11663       }
11664       /* IMAP server flags */
11665       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11666          ast_copy_string(imapflags, val, sizeof(imapflags));
11667       }
11668       /* IMAP server master username */
11669       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11670          ast_copy_string(authuser, val, sizeof(authuser));
11671       }
11672       /* IMAP server master password */
11673       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11674          ast_copy_string(authpassword, val, sizeof(authpassword));
11675       }
11676       /* Expunge on exit */
11677       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11678          if (ast_false(val))
11679             expungeonhangup = 0;
11680          else
11681             expungeonhangup = 1;
11682       } else {
11683          expungeonhangup = 1;
11684       }
11685       /* IMAP voicemail folder */
11686       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11687          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11688       } else {
11689          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11690       }
11691       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11692          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11693       }
11694       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11695          imapgreetings = ast_true(val);
11696       } else {
11697          imapgreetings = 0;
11698       }
11699       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11700          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11701       } else {
11702          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11703       }
11704 
11705       /* There is some very unorthodox casting done here. This is due
11706        * to the way c-client handles the argument passed in. It expects a 
11707        * void pointer and casts the pointer directly to a long without
11708        * first dereferencing it. */
11709       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11710          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11711       } else {
11712          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11713       }
11714 
11715       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11716          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11717       } else {
11718          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11719       }
11720 
11721       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11722          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11723       } else {
11724          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11725       }
11726 
11727       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11728          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11729       } else {
11730          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11731       }
11732 
11733       /* Increment configuration version */
11734       imapversion++;
11735 #endif
11736       /* External voicemail notify application */
11737       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11738          ast_copy_string(externnotify, val, sizeof(externnotify));
11739          ast_debug(1, "found externnotify: %s\n", externnotify);
11740       } else {
11741          externnotify[0] = '\0';
11742       }
11743 
11744       /* SMDI voicemail notification */
11745       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11746          ast_debug(1, "Enabled SMDI voicemail notification\n");
11747          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11748             smdi_iface = ast_smdi_interface_find(val);
11749          } else {
11750             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11751             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
11752          }
11753          if (!smdi_iface) {
11754             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11755          } 
11756       }
11757 
11758       /* Silence treshold */
11759       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11760       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11761          silencethreshold = atoi(val);
11762       
11763       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11764          val = ASTERISK_USERNAME;
11765       ast_copy_string(serveremail, val, sizeof(serveremail));
11766       
11767       vmmaxsecs = 0;
11768       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11769          if (sscanf(val, "%30d", &x) == 1) {
11770             vmmaxsecs = x;
11771          } else {
11772             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11773          }
11774       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11775          static int maxmessage_deprecate = 0;
11776          if (maxmessage_deprecate == 0) {
11777             maxmessage_deprecate = 1;
11778             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11779          }
11780          if (sscanf(val, "%30d", &x) == 1) {
11781             vmmaxsecs = x;
11782          } else {
11783             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11784          }
11785       }
11786 
11787       vmminsecs = 0;
11788       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11789          if (sscanf(val, "%30d", &x) == 1) {
11790             vmminsecs = x;
11791             if (maxsilence / 1000 >= vmminsecs) {
11792                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11793             }
11794          } else {
11795             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11796          }
11797       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11798          static int maxmessage_deprecate = 0;
11799          if (maxmessage_deprecate == 0) {
11800             maxmessage_deprecate = 1;
11801             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11802          }
11803          if (sscanf(val, "%30d", &x) == 1) {
11804             vmminsecs = x;
11805             if (maxsilence / 1000 >= vmminsecs) {
11806                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11807             }
11808          } else {
11809             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11810          }
11811       }
11812 
11813       val = ast_variable_retrieve(cfg, "general", "format");
11814       if (!val) {
11815          val = "wav";   
11816       } else {
11817          tmp = ast_strdupa(val);
11818          val = ast_format_str_reduce(tmp);
11819          if (!val) {
11820             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11821             val = "wav";
11822          }
11823       }
11824       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11825 
11826       skipms = 3000;
11827       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11828          if (sscanf(val, "%30d", &x) == 1) {
11829             maxgreet = x;
11830          } else {
11831             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11832          }
11833       }
11834 
11835       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11836          if (sscanf(val, "%30d", &x) == 1) {
11837             skipms = x;
11838          } else {
11839             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11840          }
11841       }
11842 
11843       maxlogins = 3;
11844       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11845          if (sscanf(val, "%30d", &x) == 1) {
11846             maxlogins = x;
11847          } else {
11848             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11849          }
11850       }
11851 
11852       minpassword = MINPASSWORD;
11853       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11854          if (sscanf(val, "%30d", &x) == 1) {
11855             minpassword = x;
11856          } else {
11857             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11858          }
11859       }
11860 
11861       /* Force new user to record name ? */
11862       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11863          val = "no";
11864       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11865 
11866       /* Force new user to record greetings ? */
11867       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11868          val = "no";
11869       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11870 
11871       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11872          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11873          stringp = ast_strdupa(val);
11874          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11875             if (!ast_strlen_zero(stringp)) {
11876                q = strsep(&stringp, ",");
11877                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11878                   q++;
11879                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11880                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11881             } else {
11882                cidinternalcontexts[x][0] = '\0';
11883             }
11884          }
11885       }
11886       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11887          ast_debug(1, "VM Review Option disabled globally\n");
11888          val = "no";
11889       }
11890       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11891 
11892       /* Temporary greeting reminder */
11893       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11894          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11895          val = "no";
11896       } else {
11897          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11898       }
11899       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11900       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11901          ast_debug(1, "VM next message wrap disabled globally\n");
11902          val = "no";
11903       }
11904       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11905 
11906       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11907          ast_debug(1, "VM Operator break disabled globally\n");
11908          val = "no";
11909       }
11910       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11911 
11912       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11913          ast_debug(1, "VM CID Info before msg disabled globally\n");
11914          val = "no";
11915       } 
11916       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11917 
11918       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
11919          ast_debug(1, "Send Voicemail msg disabled globally\n");
11920          val = "no";
11921       }
11922       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11923    
11924       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11925          ast_debug(1, "ENVELOPE before msg enabled globally\n");
11926          val = "yes";
11927       }
11928       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11929 
11930       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11931          ast_debug(1, "Move Heard enabled globally\n");
11932          val = "yes";
11933       }
11934       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11935 
11936       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11937          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11938          val = "no";
11939       }
11940       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11941 
11942       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11943          ast_debug(1, "Duration info before msg enabled globally\n");
11944          val = "yes";
11945       }
11946       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11947 
11948       saydurationminfo = 2;
11949       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11950          if (sscanf(val, "%30d", &x) == 1) {
11951             saydurationminfo = x;
11952          } else {
11953             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11954          }
11955       }
11956 
11957       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11958          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
11959          val = "no";
11960       }
11961       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11962 
11963       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11964          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11965          ast_debug(1, "found dialout context: %s\n", dialcontext);
11966       } else {
11967          dialcontext[0] = '\0';  
11968       }
11969       
11970       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11971          ast_copy_string(callcontext, val, sizeof(callcontext));
11972          ast_debug(1, "found callback context: %s\n", callcontext);
11973       } else {
11974          callcontext[0] = '\0';
11975       }
11976 
11977       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11978          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11979          ast_debug(1, "found operator context: %s\n", exitcontext);
11980       } else {
11981          exitcontext[0] = '\0';
11982       }
11983       
11984       /* load password sounds configuration */
11985       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11986          ast_copy_string(vm_password, val, sizeof(vm_password));
11987       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11988          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11989       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11990          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11991       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11992          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11993       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11994          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11995       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11996          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11997       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11998          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11999       }
12000       /* load configurable audio prompts */
12001       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12002          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12003       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12004          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12005       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12006          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12007       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12008          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12009       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12010          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12011 
12012       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12013          val = "no";
12014       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12015 
12016       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12017          val = "voicemail.conf";
12018       }
12019       if (!(strcmp(val, "spooldir"))) {
12020          passwordlocation = OPT_PWLOC_SPOOLDIR;
12021       } else {
12022          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12023       }
12024 
12025       poll_freq = DEFAULT_POLL_FREQ;
12026       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12027          if (sscanf(val, "%30u", &poll_freq) != 1) {
12028             poll_freq = DEFAULT_POLL_FREQ;
12029             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12030          }
12031       }
12032 
12033       poll_mailboxes = 0;
12034       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12035          poll_mailboxes = ast_true(val);
12036 
12037       if (ucfg) { 
12038          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12039             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12040                continue;
12041             if ((current = find_or_create(userscontext, cat))) {
12042                populate_defaults(current);
12043                apply_options_full(current, ast_variable_browse(ucfg, cat));
12044                ast_copy_string(current->context, userscontext, sizeof(current->context));
12045                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12046                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12047                }
12048 
12049                switch (current->passwordlocation) {
12050                case OPT_PWLOC_SPOOLDIR:
12051                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12052                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12053                }
12054             }
12055          }
12056          ast_config_destroy(ucfg);
12057       }
12058       cat = ast_category_browse(cfg, NULL);
12059       while (cat) {
12060          if (strcasecmp(cat, "general")) {
12061             var = ast_variable_browse(cfg, cat);
12062             if (strcasecmp(cat, "zonemessages")) {
12063                /* Process mailboxes in this context */
12064                while (var) {
12065                   append_mailbox(cat, var->name, var->value);
12066                   var = var->next;
12067                }
12068             } else {
12069                /* Timezones in this context */
12070                while (var) {
12071                   struct vm_zone *z;
12072                   if ((z = ast_malloc(sizeof(*z)))) {
12073                      char *msg_format, *tzone;
12074                      msg_format = ast_strdupa(var->value);
12075                      tzone = strsep(&msg_format, "|,");
12076                      if (msg_format) {
12077                         ast_copy_string(z->name, var->name, sizeof(z->name));
12078                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12079                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12080                         AST_LIST_LOCK(&zones);
12081                         AST_LIST_INSERT_HEAD(&zones, z, list);
12082                         AST_LIST_UNLOCK(&zones);
12083                      } else {
12084                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12085                         ast_free(z);
12086                      }
12087                   } else {
12088                      AST_LIST_UNLOCK(&users);
12089                      ast_config_destroy(cfg);
12090                      return -1;
12091                   }
12092                   var = var->next;
12093                }
12094             }
12095          }
12096          cat = ast_category_browse(cfg, cat);
12097       }
12098       memset(fromstring, 0, sizeof(fromstring));
12099       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12100       strcpy(charset, "ISO-8859-1");
12101       if (emailbody) {
12102          ast_free(emailbody);
12103          emailbody = NULL;
12104       }
12105       if (emailsubject) {
12106          ast_free(emailsubject);
12107          emailsubject = NULL;
12108       }
12109       if (pagerbody) {
12110          ast_free(pagerbody);
12111          pagerbody = NULL;
12112       }
12113       if (pagersubject) {
12114          ast_free(pagersubject);
12115          pagersubject = NULL;
12116       }
12117       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12118          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12119       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12120          ast_copy_string(fromstring, val, sizeof(fromstring));
12121       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12122          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12123       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12124          ast_copy_string(charset, val, sizeof(charset));
12125       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12126          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12127          for (x = 0; x < 4; x++) {
12128             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12129          }
12130       }
12131       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12132          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12133          for (x = 0; x < 4; x++) {
12134             memcpy(&adsisec[x], &tmpadsi[x], 1);
12135          }
12136       }
12137       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12138          if (atoi(val)) {
12139             adsiver = atoi(val);
12140          }
12141       }
12142       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12143          ast_copy_string(zonetag, val, sizeof(zonetag));
12144       }
12145       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12146          ast_copy_string(locale, val, sizeof(locale));
12147       }
12148       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12149          emailsubject = ast_strdup(val);
12150       }
12151       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12152          emailbody = ast_strdup(substitute_escapes(val));
12153       }
12154       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12155          pagersubject = ast_strdup(val);
12156       }
12157       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12158          pagerbody = ast_strdup(substitute_escapes(val));
12159       }
12160       AST_LIST_UNLOCK(&users);
12161       ast_config_destroy(cfg);
12162 
12163       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12164          start_poll_thread();
12165       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12166          stop_poll_thread();;
12167 
12168       return 0;
12169    } else {
12170       AST_LIST_UNLOCK(&users);
12171       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12172       if (ucfg)
12173          ast_config_destroy(ucfg);
12174       return 0;
12175    }
12176 }
12177 
12178 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12179 {
12180    int res = -1;
12181    char dir[PATH_MAX];
12182    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12183    ast_debug(2, "About to try retrieving name file %s\n", dir);
12184    RETRIEVE(dir, -1, mailbox, context);
12185    if (ast_fileexists(dir, NULL, NULL)) {
12186       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12187    }
12188    DISPOSE(dir, -1);
12189    return res;
12190 }
12191 
12192 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12193    struct ast_config *pwconf;
12194    struct ast_flags config_flags = { 0 };
12195 
12196    pwconf = ast_config_load(secretfn, config_flags);
12197    if (pwconf) {
12198       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12199       if (val) {
12200          ast_copy_string(password, val, passwordlen);
12201          return;
12202       }
12203    }
12204    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12205 }
12206 
12207 static int write_password_to_file(const char *secretfn, const char *password) {
12208    struct ast_config *conf;
12209    struct ast_category *cat;
12210    struct ast_variable *var;
12211 
12212    if (!(conf=ast_config_new())) {
12213       ast_log(LOG_ERROR, "Error creating new config structure\n");
12214       return -1;
12215    }
12216    if (!(cat=ast_category_new("general","",1))) {
12217       ast_log(LOG_ERROR, "Error creating new category structure\n");
12218       return -1;
12219    }
12220    if (!(var=ast_variable_new("password",password,""))) {
12221       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12222       return -1;
12223    }
12224    ast_category_append(conf,cat);
12225    ast_variable_append(cat,var);
12226    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12227       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12228       return -1;
12229    }
12230    return 0;
12231 }
12232 
12233 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12234 {
12235    char *context;
12236    char *args_copy;
12237    int res;
12238 
12239    if (ast_strlen_zero(data)) {
12240       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12241       return -1;
12242    }
12243 
12244    args_copy = ast_strdupa(data);
12245    if ((context = strchr(args_copy, '@'))) {
12246       *context++ = '\0';
12247    } else {
12248       context = "default";
12249    }
12250 
12251    if ((res = sayname(chan, args_copy, context) < 0)) {
12252       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12253       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12254       if (!res) {
12255          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12256       }
12257    }
12258 
12259    return res;
12260 }
12261 
12262 #ifdef TEST_FRAMEWORK
12263 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12264 {
12265    return 0;
12266 }
12267 
12268 static struct ast_frame *fake_read(struct ast_channel *ast)
12269 {
12270    return &ast_null_frame;
12271 }
12272 
12273 AST_TEST_DEFINE(test_voicemail_vmsayname)
12274 {
12275    char dir[PATH_MAX];
12276    char dir2[PATH_MAX];
12277    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12278    static const char TEST_EXTENSION[] = "1234";
12279 
12280    struct ast_channel *test_channel1 = NULL;
12281    int res = -1;
12282 
12283    static const struct ast_channel_tech fake_tech = {
12284       .write = fake_write,
12285       .read = fake_read,
12286    };
12287 
12288    switch (cmd) {
12289    case TEST_INIT:
12290       info->name = "vmsayname_exec";
12291       info->category = "/apps/app_voicemail/";
12292       info->summary = "Vmsayname unit test";
12293       info->description =
12294          "This tests passing various parameters to vmsayname";
12295       return AST_TEST_NOT_RUN;
12296    case TEST_EXECUTE:
12297       break;
12298    }
12299 
12300    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12301         NULL, NULL, 0, 0, "TestChannel1"))) {
12302       goto exit_vmsayname_test;
12303    }
12304 
12305    /* normally this is done in the channel driver */
12306    test_channel1->nativeformats = AST_FORMAT_GSM;
12307    test_channel1->writeformat = AST_FORMAT_GSM;
12308    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12309    test_channel1->readformat = AST_FORMAT_GSM;
12310    test_channel1->rawreadformat = AST_FORMAT_GSM;
12311    test_channel1->tech = &fake_tech;
12312 
12313    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12314    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12315    if (!(res = vmsayname_exec(test_channel1, dir))) {
12316       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12317       if (ast_fileexists(dir, NULL, NULL)) {
12318          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12319          res = -1;
12320          goto exit_vmsayname_test;
12321       } else {
12322          /* no greeting already exists as expected, let's create one to fully test sayname */
12323          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12324             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12325             goto exit_vmsayname_test;
12326          }
12327          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12328          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12329          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12330          if ((res = symlink(dir, dir2))) {
12331             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12332             goto exit_vmsayname_test;
12333          }
12334          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12335          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12336          res = vmsayname_exec(test_channel1, dir);
12337 
12338          /* TODO: there may be a better way to do this */
12339          unlink(dir2);
12340          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12341          rmdir(dir2);
12342          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12343          rmdir(dir2);
12344       }
12345    }
12346 
12347 exit_vmsayname_test:
12348 
12349    if (test_channel1) {
12350       ast_hangup(test_channel1);
12351    }
12352 
12353    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12354 }
12355 
12356 AST_TEST_DEFINE(test_voicemail_msgcount)
12357 {
12358    int i, j, res = AST_TEST_PASS, syserr;
12359    struct ast_vm_user *vmu;
12360    struct vm_state vms;
12361 #ifdef IMAP_STORAGE
12362    struct ast_channel *chan = NULL;
12363 #endif
12364    struct {
12365       char dir[256];
12366       char file[256];
12367       char txtfile[256];
12368    } tmp[3];
12369    char syscmd[256];
12370    const char origweasels[] = "tt-weasels";
12371    const char testcontext[] = "test";
12372    const char testmailbox[] = "00000000";
12373    const char testspec[] = "00000000@test";
12374    FILE *txt;
12375    int new, old, urgent;
12376    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12377    const int folder2mbox[3] = { 1, 11, 0 };
12378    const int expected_results[3][12] = {
12379       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12380       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12381       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12382       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12383    };
12384 
12385    switch (cmd) {
12386    case TEST_INIT:
12387       info->name = "test_voicemail_msgcount";
12388       info->category = "/apps/app_voicemail/";
12389       info->summary = "Test Voicemail status checks";
12390       info->description =
12391          "Verify that message counts are correct when retrieved through the public API";
12392       return AST_TEST_NOT_RUN;
12393    case TEST_EXECUTE:
12394       break;
12395    }
12396 
12397    /* Make sure the original path was completely empty */
12398    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12399    if ((syserr = ast_safe_system(syscmd))) {
12400       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12401          syserr > 0 ? strerror(syserr) : "unable to fork()");
12402       return AST_TEST_FAIL;
12403    }
12404 
12405 #ifdef IMAP_STORAGE
12406    if (!(chan = ast_dummy_channel_alloc())) {
12407       ast_test_status_update(test, "Unable to create dummy channel\n");
12408       return AST_TEST_FAIL;
12409    }
12410 #endif
12411 
12412    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12413       !(vmu = find_or_create(testcontext, testmailbox))) {
12414       ast_test_status_update(test, "Cannot create vmu structure\n");
12415       ast_unreplace_sigchld();
12416       return AST_TEST_FAIL;
12417    }
12418 
12419    populate_defaults(vmu);
12420    memset(&vms, 0, sizeof(vms));
12421 
12422    /* Create temporary voicemail */
12423    for (i = 0; i < 3; i++) {
12424       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12425       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12426       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12427 
12428       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12429          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12430             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12431          if ((syserr = ast_safe_system(syscmd))) {
12432             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12433                syserr > 0 ? strerror(syserr) : "unable to fork()");
12434             ast_unreplace_sigchld();
12435             return AST_TEST_FAIL;
12436          }
12437       }
12438 
12439       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12440          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12441          fclose(txt);
12442       } else {
12443          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12444          res = AST_TEST_FAIL;
12445          break;
12446       }
12447       open_mailbox(&vms, vmu, folder2mbox[i]);
12448       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12449 
12450       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12451       for (j = 0; j < 3; j++) {
12452          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12453          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12454             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12455                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12456             res = AST_TEST_FAIL;
12457          }
12458       }
12459 
12460       new = old = urgent = 0;
12461       if (ast_app_inboxcount(testspec, &new, &old)) {
12462          ast_test_status_update(test, "inboxcount returned failure\n");
12463          res = AST_TEST_FAIL;
12464       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12465          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12466             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12467          res = AST_TEST_FAIL;
12468       }
12469 
12470       new = old = urgent = 0;
12471       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12472          ast_test_status_update(test, "inboxcount2 returned failure\n");
12473          res = AST_TEST_FAIL;
12474       } else if (old != expected_results[i][6 + 0] ||
12475             urgent != expected_results[i][6 + 1] ||
12476                new != expected_results[i][6 + 2]    ) {
12477          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12478             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12479          res = AST_TEST_FAIL;
12480       }
12481 
12482       new = old = urgent = 0;
12483       for (j = 0; j < 3; j++) {
12484          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12485             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12486                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12487             res = AST_TEST_FAIL;
12488          }
12489       }
12490    }
12491 
12492    for (i = 0; i < 3; i++) {
12493       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12494        * server, in which case, the rm below will not affect the
12495        * voicemails. */
12496       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12497       DISPOSE(tmp[i].dir, 0);
12498    }
12499 
12500    if (vms.deleted) {
12501       ast_free(vms.deleted);
12502    }
12503    if (vms.heard) {
12504       ast_free(vms.heard);
12505    }
12506 
12507 #ifdef IMAP_STORAGE
12508    chan = ast_channel_release(chan);
12509 #endif
12510 
12511    /* And remove test directory */
12512    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12513    if ((syserr = ast_safe_system(syscmd))) {
12514       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12515          syserr > 0 ? strerror(syserr) : "unable to fork()");
12516    }
12517 
12518    return res;
12519 }
12520 
12521 AST_TEST_DEFINE(test_voicemail_notify_endl)
12522 {
12523    int res = AST_TEST_PASS;
12524    char testcontext[] = "test";
12525    char testmailbox[] = "00000000";
12526    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12527    char attach[256], attach2[256];
12528    char buf[256] = ""; /* No line should actually be longer than 80 */
12529    struct ast_channel *chan = NULL;
12530    struct ast_vm_user *vmu, vmus = {
12531       .flags = 0,
12532    };
12533    FILE *file;
12534    struct {
12535       char *name;
12536       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12537       void *location;
12538       union {
12539          int intval;
12540          char *strval;
12541       } u;
12542    } test_items[] = {
12543       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12544       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12545       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12546       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12547       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12548       { "attach2", STRPTR, attach2, .u.strval = "" },
12549       { "attach", STRPTR, attach, .u.strval = "" },
12550    };
12551    int which;
12552 
12553    switch (cmd) {
12554    case TEST_INIT:
12555       info->name = "test_voicemail_notify_endl";
12556       info->category = "/apps/app_voicemail/";
12557       info->summary = "Test Voicemail notification end-of-line";
12558       info->description =
12559          "Verify that notification emails use a consistent end-of-line character";
12560       return AST_TEST_NOT_RUN;
12561    case TEST_EXECUTE:
12562       break;
12563    }
12564 
12565    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12566    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12567 
12568    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12569       !(vmu = find_or_create(testcontext, testmailbox))) {
12570       ast_test_status_update(test, "Cannot create vmu structure\n");
12571       return AST_TEST_NOT_RUN;
12572    }
12573 
12574    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12575       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12576       return AST_TEST_NOT_RUN;
12577    }
12578 
12579    populate_defaults(vmu);
12580    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12581 #ifdef IMAP_STORAGE
12582    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12583 #endif
12584 
12585    file = tmpfile();
12586    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12587       /* Kill previous test, if any */
12588       rewind(file);
12589       if (ftruncate(fileno(file), 0)) {
12590          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12591          res = AST_TEST_FAIL;
12592          break;
12593       }
12594 
12595       /* Make each change, in order, to the test mailbox */
12596       if (test_items[which].type == INT) {
12597          *((int *) test_items[which].location) = test_items[which].u.intval;
12598       } else if (test_items[which].type == FLAGVAL) {
12599          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12600             ast_clear_flag(vmu, test_items[which].u.intval);
12601          } else {
12602             ast_set_flag(vmu, test_items[which].u.intval);
12603          }
12604       } else if (test_items[which].type == STATIC) {
12605          strcpy(test_items[which].location, test_items[which].u.strval);
12606       } else if (test_items[which].type == STRPTR) {
12607          test_items[which].location = test_items[which].u.strval;
12608       }
12609 
12610       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12611       rewind(file);
12612       while (fgets(buf, sizeof(buf), file)) {
12613          if (
12614 #ifdef IMAP_STORAGE
12615          buf[strlen(buf) - 2] != '\r'
12616 #else
12617          buf[strlen(buf) - 2] == '\r'
12618 #endif
12619          || buf[strlen(buf) - 1] != '\n') {
12620             res = AST_TEST_FAIL;
12621          }
12622       }
12623    }
12624    fclose(file);
12625    return res;
12626 }
12627 #endif /* defined(TEST_FRAMEWORK) */
12628 
12629 static int reload(void)
12630 {
12631    return load_config(1);
12632 }
12633 
12634 static int unload_module(void)
12635 {
12636    int res;
12637 
12638    res = ast_unregister_application(app);
12639    res |= ast_unregister_application(app2);
12640    res |= ast_unregister_application(app3);
12641    res |= ast_unregister_application(app4);
12642    res |= ast_unregister_application(sayname_app);
12643    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12644    res |= ast_manager_unregister("VoicemailUsersList");
12645    res |= ast_data_unregister(NULL);
12646 #ifdef TEST_FRAMEWORK
12647    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12648    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12649    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12650    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12651 #endif
12652    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12653    ast_uninstall_vm_functions();
12654    ao2_ref(inprocess_container, -1);
12655 
12656    if (poll_thread != AST_PTHREADT_NULL)
12657       stop_poll_thread();
12658 
12659    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12660    ast_unload_realtime("voicemail");
12661    ast_unload_realtime("voicemail_data");
12662 
12663    free_vm_users();
12664    free_vm_zones();
12665    return res;
12666 }
12667 
12668 static int load_module(void)
12669 {
12670    int res;
12671    my_umask = umask(0);
12672    umask(my_umask);
12673 
12674    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12675       return AST_MODULE_LOAD_DECLINE;
12676    }
12677 
12678    /* compute the location of the voicemail spool directory */
12679    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12680    
12681    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12682       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12683    }
12684 
12685    if ((res = load_config(0)))
12686       return res;
12687 
12688    res = ast_register_application_xml(app, vm_exec);
12689    res |= ast_register_application_xml(app2, vm_execmain);
12690    res |= ast_register_application_xml(app3, vm_box_exists);
12691    res |= ast_register_application_xml(app4, vmauthenticate);
12692    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12693    res |= ast_custom_function_register(&mailbox_exists_acf);
12694    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12695 #ifdef TEST_FRAMEWORK
12696    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12697    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12698    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12699    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12700 #endif
12701 
12702    if (res)
12703       return res;
12704 
12705    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12706    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12707 
12708    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12709    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12710    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12711 
12712    return res;
12713 }
12714 
12715 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
12716 {
12717    int cmd = 0;
12718    char destination[80] = "";
12719    int retries = 0;
12720 
12721    if (!num) {
12722       ast_verb(3, "Destination number will be entered manually\n");
12723       while (retries < 3 && cmd != 't') {
12724          destination[1] = '\0';
12725          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
12726          if (!cmd)
12727             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
12728          if (!cmd)
12729             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
12730          if (!cmd) {
12731             cmd = ast_waitfordigit(chan, 6000);
12732             if (cmd)
12733                destination[0] = cmd;
12734          }
12735          if (!cmd) {
12736             retries++;
12737          } else {
12738 
12739             if (cmd < 0)
12740                return 0;
12741             if (cmd == '*') {
12742                ast_verb(3, "User hit '*' to cancel outgoing call\n");
12743                return 0;
12744             }
12745             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
12746                retries++;
12747             else
12748                cmd = 't';
12749          }
12750       }
12751       if (retries >= 3) {
12752          return 0;
12753       }
12754       
12755    } else {
12756       if (option_verbose > 2)
12757          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
12758       ast_copy_string(destination, num, sizeof(destination));
12759    }
12760 
12761    if (!ast_strlen_zero(destination)) {
12762       if (destination[strlen(destination) -1 ] == '*')
12763          return 0; 
12764       if (option_verbose > 2)
12765          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
12766       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
12767       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
12768       chan->priority = 0;
12769       return 9;
12770    }
12771    return 0;
12772 }
12773 
12774 /*!
12775  * \brief The advanced options within a message.
12776  * \param chan
12777  * \param vmu 
12778  * \param vms
12779  * \param msg
12780  * \param option
12781  * \param record_gain
12782  *
12783  * Provides handling for the play message envelope, call the person back, or reply to message. 
12784  *
12785  * \return zero on success, -1 on error.
12786  */
12787 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)
12788 {
12789    int res = 0;
12790    char filename[PATH_MAX];
12791    struct ast_config *msg_cfg = NULL;
12792    const char *origtime, *context;
12793    char *name, *num;
12794    int retries = 0;
12795    char *cid;
12796    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
12797 
12798    vms->starting = 0; 
12799 
12800    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12801 
12802    /* Retrieve info from VM attribute file */
12803    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
12804    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
12805    msg_cfg = ast_config_load(filename, config_flags);
12806    DISPOSE(vms->curdir, vms->curmsg);
12807    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
12808       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
12809       return 0;
12810    }
12811 
12812    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
12813       ast_config_destroy(msg_cfg);
12814       return 0;
12815    }
12816 
12817    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
12818 
12819    context = ast_variable_retrieve(msg_cfg, "message", "context");
12820    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
12821       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
12822    switch (option) {
12823    case 3: /* Play message envelope */
12824       if (!res)
12825          res = play_message_datetime(chan, vmu, origtime, filename);
12826       if (!res)
12827          res = play_message_callerid(chan, vms, cid, context, 0);
12828 
12829       res = 't';
12830       break;
12831 
12832    case 2:  /* Call back */
12833 
12834       if (ast_strlen_zero(cid))
12835          break;
12836 
12837       ast_callerid_parse(cid, &name, &num);
12838       while ((res > -1) && (res != 't')) {
12839          switch (res) {
12840          case '1':
12841             if (num) {
12842                /* Dial the CID number */
12843                res = dialout(chan, vmu, num, vmu->callback);
12844                if (res) {
12845                   ast_config_destroy(msg_cfg);
12846                   return 9;
12847                }
12848             } else {
12849                res = '2';
12850             }
12851             break;
12852 
12853          case '2':
12854             /* Want to enter a different number, can only do this if there's a dialout context for this user */
12855             if (!ast_strlen_zero(vmu->dialout)) {
12856                res = dialout(chan, vmu, NULL, vmu->dialout);
12857                if (res) {
12858                   ast_config_destroy(msg_cfg);
12859                   return 9;
12860                }
12861             } else {
12862                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
12863                res = ast_play_and_wait(chan, "vm-sorry");
12864             }
12865             ast_config_destroy(msg_cfg);
12866             return res;
12867          case '*':
12868             res = 't';
12869             break;
12870          case '3':
12871          case '4':
12872          case '5':
12873          case '6':
12874          case '7':
12875          case '8':
12876          case '9':
12877          case '0':
12878 
12879             res = ast_play_and_wait(chan, "vm-sorry");
12880             retries++;
12881             break;
12882          default:
12883             if (num) {
12884                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
12885                res = ast_play_and_wait(chan, "vm-num-i-have");
12886                if (!res)
12887                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
12888                if (!res)
12889                   res = ast_play_and_wait(chan, "vm-tocallnum");
12890                /* Only prompt for a caller-specified number if there is a dialout context specified */
12891                if (!ast_strlen_zero(vmu->dialout)) {
12892                   if (!res)
12893                      res = ast_play_and_wait(chan, "vm-calldiffnum");
12894                }
12895             } else {
12896                res = ast_play_and_wait(chan, "vm-nonumber");
12897                if (!ast_strlen_zero(vmu->dialout)) {
12898                   if (!res)
12899                      res = ast_play_and_wait(chan, "vm-toenternumber");
12900                }
12901             }
12902             if (!res)
12903                res = ast_play_and_wait(chan, "vm-star-cancel");
12904             if (!res)
12905                res = ast_waitfordigit(chan, 6000);
12906             if (!res) {
12907                retries++;
12908                if (retries > 3)
12909                   res = 't';
12910             }
12911             break; 
12912             
12913          }
12914          if (res == 't')
12915             res = 0;
12916          else if (res == '*')
12917             res = -1;
12918       }
12919       break;
12920       
12921    case 1:  /* Reply */
12922       /* Send reply directly to sender */
12923       if (ast_strlen_zero(cid))
12924          break;
12925 
12926       ast_callerid_parse(cid, &name, &num);
12927       if (!num) {
12928          ast_verb(3, "No CID number available, no reply sent\n");
12929          if (!res)
12930             res = ast_play_and_wait(chan, "vm-nonumber");
12931          ast_config_destroy(msg_cfg);
12932          return res;
12933       } else {
12934          struct ast_vm_user vmu2;
12935          if (find_user(&vmu2, vmu->context, num)) {
12936             struct leave_vm_options leave_options;
12937             char mailbox[AST_MAX_EXTENSION * 2 + 2];
12938             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
12939 
12940             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
12941             
12942             memset(&leave_options, 0, sizeof(leave_options));
12943             leave_options.record_gain = record_gain;
12944             res = leave_voicemail(chan, mailbox, &leave_options);
12945             if (!res)
12946                res = 't';
12947             ast_config_destroy(msg_cfg);
12948             return res;
12949          } else {
12950             /* Sender has no mailbox, can't reply */
12951             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
12952             ast_play_and_wait(chan, "vm-nobox");
12953             res = 't';
12954             ast_config_destroy(msg_cfg);
12955             return res;
12956          }
12957       } 
12958       res = 0;
12959 
12960       break;
12961    }
12962 
12963 #ifndef IMAP_STORAGE
12964    ast_config_destroy(msg_cfg);
12965 
12966    if (!res) {
12967       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12968       vms->heard[msg] = 1;
12969       res = wait_file(chan, vms, vms->fn);
12970    }
12971 #endif
12972    return res;
12973 }
12974 
12975 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
12976          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
12977          signed char record_gain, struct vm_state *vms, char *flag)
12978 {
12979    /* Record message & let caller review or re-record it, or set options if applicable */
12980    int res = 0;
12981    int cmd = 0;
12982    int max_attempts = 3;
12983    int attempts = 0;
12984    int recorded = 0;
12985    int msg_exists = 0;
12986    signed char zero_gain = 0;
12987    char tempfile[PATH_MAX];
12988    char *acceptdtmf = "#";
12989    char *canceldtmf = "";
12990    int canceleddtmf = 0;
12991 
12992    /* Note that urgent and private are for flagging messages as such in the future */
12993 
12994    /* barf if no pointer passed to store duration in */
12995    if (duration == NULL) {
12996       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
12997       return -1;
12998    }
12999 
13000    if (!outsidecaller)
13001       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13002    else
13003       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13004 
13005    cmd = '3';  /* Want to start by recording */
13006 
13007    while ((cmd >= 0) && (cmd != 't')) {
13008       switch (cmd) {
13009       case '1':
13010          if (!msg_exists) {
13011             /* In this case, 1 is to record a message */
13012             cmd = '3';
13013             break;
13014          } else {
13015             /* Otherwise 1 is to save the existing message */
13016             ast_verb(3, "Saving message as is\n");
13017             if (!outsidecaller) 
13018                ast_filerename(tempfile, recordfile, NULL);
13019             ast_stream_and_wait(chan, "vm-msgsaved", "");
13020             if (!outsidecaller) {
13021                /* Saves to IMAP server only if imapgreeting=yes */
13022                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13023                DISPOSE(recordfile, -1);
13024             }
13025             cmd = 't';
13026             return res;
13027          }
13028       case '2':
13029          /* Review */
13030          ast_verb(3, "Reviewing the message\n");
13031          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13032          break;
13033       case '3':
13034          msg_exists = 0;
13035          /* Record */
13036          if (recorded == 1) 
13037             ast_verb(3, "Re-recording the message\n");
13038          else  
13039             ast_verb(3, "Recording the message\n");
13040          
13041          if (recorded && outsidecaller) {
13042             cmd = ast_play_and_wait(chan, INTRO);
13043             cmd = ast_play_and_wait(chan, "beep");
13044          }
13045          recorded = 1;
13046          /* 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 */
13047          if (record_gain)
13048             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13049          if (ast_test_flag(vmu, VM_OPERATOR))
13050             canceldtmf = "0";
13051          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13052          if (strchr(canceldtmf, cmd)) {
13053          /* need this flag here to distinguish between pressing '0' during message recording or after */
13054             canceleddtmf = 1;
13055          }
13056          if (record_gain)
13057             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13058          if (cmd == -1) {
13059             /* User has hung up, no options to give */
13060             if (!outsidecaller) {
13061                /* user was recording a greeting and they hung up, so let's delete the recording. */
13062                ast_filedelete(tempfile, NULL);
13063             }     
13064             return cmd;
13065          }
13066          if (cmd == '0') {
13067             break;
13068          } else if (cmd == '*') {
13069             break;
13070 #if 0
13071          } else if (vmu->review && (*duration < 5)) {
13072             /* Message is too short */
13073             ast_verb(3, "Message too short\n");
13074             cmd = ast_play_and_wait(chan, "vm-tooshort");
13075             cmd = ast_filedelete(tempfile, NULL);
13076             break;
13077          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13078             /* Message is all silence */
13079             ast_verb(3, "Nothing recorded\n");
13080             cmd = ast_filedelete(tempfile, NULL);
13081             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13082             if (!cmd)
13083                cmd = ast_play_and_wait(chan, "vm-speakup");
13084             break;
13085 #endif
13086          } else {
13087             /* If all is well, a message exists */
13088             msg_exists = 1;
13089             cmd = 0;
13090          }
13091          break;
13092       case '4':
13093          if (outsidecaller) {  /* only mark vm messages */
13094             /* Mark Urgent */
13095             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13096                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13097                res = ast_play_and_wait(chan, "vm-marked-urgent");
13098                strcpy(flag, "Urgent");
13099             } else if (flag) {
13100                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13101                res = ast_play_and_wait(chan, "vm-urgent-removed");
13102                strcpy(flag, "");
13103             } else {
13104                ast_play_and_wait(chan, "vm-sorry");
13105             }
13106             cmd = 0;
13107          } else {
13108             cmd = ast_play_and_wait(chan, "vm-sorry");
13109          }
13110          break;
13111       case '5':
13112       case '6':
13113       case '7':
13114       case '8':
13115       case '9':
13116       case '*':
13117       case '#':
13118          cmd = ast_play_and_wait(chan, "vm-sorry");
13119          break;
13120 #if 0 
13121 /*  XXX Commented out for the moment because of the dangers of deleting
13122     a message while recording (can put the message numbers out of sync) */
13123       case '*':
13124          /* Cancel recording, delete message, offer to take another message*/
13125          cmd = ast_play_and_wait(chan, "vm-deleted");
13126          cmd = ast_filedelete(tempfile, NULL);
13127          if (outsidecaller) {
13128             res = vm_exec(chan, NULL);
13129             return res;
13130          }
13131          else
13132             return 1;
13133 #endif
13134       case '0':
13135          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13136             cmd = ast_play_and_wait(chan, "vm-sorry");
13137             break;
13138          }
13139          if (msg_exists || recorded) {
13140             cmd = ast_play_and_wait(chan, "vm-saveoper");
13141             if (!cmd)
13142                cmd = ast_waitfordigit(chan, 3000);
13143             if (cmd == '1') {
13144                ast_filerename(tempfile, recordfile, NULL);
13145                ast_play_and_wait(chan, "vm-msgsaved");
13146                cmd = '0';
13147             } else if (cmd == '4') {
13148                if (flag) {
13149                   ast_play_and_wait(chan, "vm-marked-urgent");
13150                   strcpy(flag, "Urgent");
13151                }
13152                ast_play_and_wait(chan, "vm-msgsaved");
13153                cmd = '0';
13154             } else {
13155                ast_play_and_wait(chan, "vm-deleted");
13156                DELETE(tempfile, -1, tempfile, vmu);
13157                cmd = '0';
13158             }
13159          }
13160          return cmd;
13161       default:
13162          /* If the caller is an ouside caller, and the review option is enabled,
13163             allow them to review the message, but let the owner of the box review
13164             their OGM's */
13165          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13166             return cmd;
13167          if (msg_exists) {
13168             cmd = ast_play_and_wait(chan, "vm-review");
13169             if (!cmd && outsidecaller) {
13170                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13171                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13172                } else if (flag) {
13173                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13174                }
13175             }
13176          } else {
13177             cmd = ast_play_and_wait(chan, "vm-torerecord");
13178             if (!cmd)
13179                cmd = ast_waitfordigit(chan, 600);
13180          }
13181          
13182          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13183             cmd = ast_play_and_wait(chan, "vm-reachoper");
13184             if (!cmd)
13185                cmd = ast_waitfordigit(chan, 600);
13186          }
13187 #if 0
13188          if (!cmd)
13189             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13190 #endif
13191          if (!cmd)
13192             cmd = ast_waitfordigit(chan, 6000);
13193          if (!cmd) {
13194             attempts++;
13195          }
13196          if (attempts > max_attempts) {
13197             cmd = 't';
13198          }
13199       }
13200    }
13201    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13202       /* Hang up or timeout, so delete the recording. */
13203       ast_filedelete(tempfile, NULL);
13204    }
13205 
13206    if (cmd != 't' && outsidecaller)
13207       ast_play_and_wait(chan, "vm-goodbye");
13208 
13209    return cmd;
13210 }
13211 
13212 /* This is a workaround so that menuselect displays a proper description
13213  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13214  */
13215 
13216 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13217       .load = load_module,
13218       .unload = unload_module,
13219       .reload = reload,
13220       .nonoptreq = "res_adsi,res_smdi",
13221       );