Fri Jul 15 2011 11:57:12

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 /*! \file
00020  *
00021  * \brief Comedian Mail - Voicemail System
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \extref Unixodbc - http://www.unixodbc.org
00026  * \extref A source distribution of University of Washington's IMAP
00027 c-client (http://www.washington.edu/imap/
00028  * 
00029  * \par See also
00030  * \arg \ref Config_vm
00031  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
00032  * \ingroup applications
00033  * \note This module requires res_adsi to load. This needs to be optional
00034  * during compilation.
00035  *
00036  *
00037  *
00038  * \note  This file is now almost impossible to work with, due to all \#ifdefs.
00039  *        Feels like the database code before realtime. Someone - please come up
00040  *        with a plan to clean this up.
00041  */
00042 
00043 /*** MAKEOPTS
00044 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
00045    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00046       <conflict>ODBC_STORAGE</conflict>
00047       <conflict>IMAP_STORAGE</conflict>
00048       <defaultenabled>yes</defaultenabled>
00049    </member>
00050    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00051       <depend>generic_odbc</depend>
00052       <depend>ltdl</depend>
00053       <conflict>IMAP_STORAGE</conflict>
00054       <conflict>FILE_STORAGE</conflict>
00055       <defaultenabled>no</defaultenabled>
00056    </member>
00057    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00058       <depend>imap_tk</depend>
00059       <conflict>ODBC_STORAGE</conflict>
00060       <conflict>FILE_STORAGE</conflict>
00061       <use>openssl</use>
00062       <defaultenabled>no</defaultenabled>
00063    </member>
00064 </category>
00065  ***/
00066 
00067 #include "asterisk.h"
00068 
00069 #ifdef IMAP_STORAGE
00070 #include <ctype.h>
00071 #include <signal.h>
00072 #include <pwd.h>
00073 #ifdef USE_SYSTEM_IMAP
00074 #include <imap/c-client.h>
00075 #include <imap/imap4r1.h>
00076 #include <imap/linkage.h>
00077 #elif defined (USE_SYSTEM_CCLIENT)
00078 #include <c-client/c-client.h>
00079 #include <c-client/imap4r1.h>
00080 #include <c-client/linkage.h>
00081 #else
00082 #include "c-client.h"
00083 #include "imap4r1.h"
00084 #include "linkage.h"
00085 #endif
00086 #endif
00087 
00088 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 316708 $")
00089 
00090 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00091 #include <sys/time.h>
00092 #include <sys/stat.h>
00093 #include <sys/mman.h>
00094 #include <time.h>
00095 #include <dirent.h>
00096 
00097 #include "asterisk/logger.h"
00098 #include "asterisk/lock.h"
00099 #include "asterisk/file.h"
00100 #include "asterisk/channel.h"
00101 #include "asterisk/pbx.h"
00102 #include "asterisk/config.h"
00103 #include "asterisk/say.h"
00104 #include "asterisk/module.h"
00105 #include "asterisk/adsi.h"
00106 #include "asterisk/app.h"
00107 #include "asterisk/manager.h"
00108 #include "asterisk/dsp.h"
00109 #include "asterisk/localtime.h"
00110 #include "asterisk/cli.h"
00111 #include "asterisk/utils.h"
00112 #include "asterisk/stringfields.h"
00113 #include "asterisk/smdi.h"
00114 #include "asterisk/astobj2.h"
00115 #include "asterisk/event.h"
00116 #include "asterisk/taskprocessor.h"
00117 
00118 #ifdef ODBC_STORAGE
00119 #include "asterisk/res_odbc.h"
00120 #endif
00121 
00122 #ifdef IMAP_STORAGE
00123 #include "asterisk/threadstorage.h"
00124 #endif
00125 
00126 /*** DOCUMENTATION
00127    <application name="VoiceMail" language="en_US">
00128       <synopsis>
00129          Leave a Voicemail message.
00130       </synopsis>
00131       <syntax>
00132          <parameter name="mailboxs" argsep="&amp;" required="true">
00133             <argument name="mailbox1" argsep="@" required="true">
00134                <argument name="mailbox" required="true" />
00135                <argument name="context" />
00136             </argument>
00137             <argument name="mailbox2" argsep="@" multiple="true">
00138                <argument name="mailbox" required="true" />
00139                <argument name="context" />
00140             </argument>
00141          </parameter>
00142          <parameter name="options">
00143             <optionlist>
00144                <option name="b">
00145                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00146                </option>
00147                <option name="d">
00148                   <argument name="c" />
00149                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00150                   if played during the greeting. Context defaults to the current context.</para>
00151                </option>
00152                <option name="g">
00153                   <argument name="#" required="true" />
00154                   <para>Use the specified amount of gain when recording the voicemail
00155                   message. The units are whole-number decibels (dB). Only works on supported
00156                   technologies, which is DAHDI only.</para>
00157                </option>
00158                <option name="s">
00159                   <para>Skip the playback of instructions for leaving a message to the
00160                   calling party.</para>
00161                </option>
00162                <option name="u">
00163                   <para>Play the <literal>unavailable</literal> greeting.</para>
00164                </option>
00165                <option name="U">
00166                   <para>Mark message as <literal>URGENT</literal>.</para>
00167                </option>
00168                <option name="P">
00169                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00170                </option>
00171             </optionlist>
00172          </parameter>
00173       </syntax>
00174       <description>
00175          <para>This application allows the calling party to leave a message for the specified
00176          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00177          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00178          exist.</para>
00179          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00180          <enumlist>
00181             <enum name="0">
00182                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00183             </enum>
00184             <enum name="*">
00185                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00186             </enum>
00187          </enumlist>
00188          <para>This application will set the following channel variable upon completion:</para>
00189          <variablelist>
00190             <variable name="VMSTATUS">
00191                <para>This indicates the status of the execution of the VoiceMail application.</para>
00192                <value name="SUCCESS" />
00193                <value name="USEREXIT" />
00194                <value name="FAILED" />
00195             </variable>
00196          </variablelist>
00197       </description>
00198    </application>
00199    <application name="VoiceMailMain" language="en_US">
00200       <synopsis>
00201          Check Voicemail messages.
00202       </synopsis>
00203       <syntax>
00204          <parameter name="mailbox" required="true" argsep="@">
00205             <argument name="mailbox" />
00206             <argument name="context" />
00207          </parameter>
00208          <parameter name="options">
00209             <optionlist>
00210                <option name="p">
00211                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00212                   the mailbox that is entered by the caller.</para>
00213                </option>
00214                <option name="g">
00215                   <argument name="#" required="true" />
00216                   <para>Use the specified amount of gain when recording a voicemail message.
00217                   The units are whole-number decibels (dB).</para>
00218                </option>
00219                <option name="s">
00220                   <para>Skip checking the passcode for the mailbox.</para>
00221                </option>
00222                <option name="a">
00223                   <argument name="folder" required="true" />
00224                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00225                   Defaults to <literal>0</literal> (INBOX).</para>
00226                   <enumlist>
00227                      <enum name="0"><para>INBOX</para></enum>
00228                      <enum name="1"><para>Old</para></enum>
00229                      <enum name="2"><para>Work</para></enum>
00230                      <enum name="3"><para>Family</para></enum>
00231                      <enum name="4"><para>Friends</para></enum>
00232                      <enum name="5"><para>Cust1</para></enum>
00233                      <enum name="6"><para>Cust2</para></enum>
00234                      <enum name="7"><para>Cust3</para></enum>
00235                      <enum name="8"><para>Cust4</para></enum>
00236                      <enum name="9"><para>Cust5</para></enum>
00237                   </enumlist>
00238                </option>
00239             </optionlist>
00240          </parameter>
00241       </syntax>
00242       <description>
00243          <para>This application allows the calling party to check voicemail messages. A specific
00244          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00245          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00246          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00247          <literal>default</literal> context will be used.</para>
00248       </description>
00249    </application>
00250    <application name="MailboxExists" language="en_US">
00251       <synopsis>
00252          Check to see if Voicemail mailbox exists.
00253       </synopsis>
00254       <syntax>
00255          <parameter name="mailbox" required="true" argsep="@">
00256             <argument name="mailbox" required="true" />
00257             <argument name="context" />
00258          </parameter>
00259          <parameter name="options">
00260             <para>None options.</para>
00261          </parameter>
00262       </syntax>
00263       <description>
00264          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00265          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00266          will be used.</para>
00267          <para>This application will set the following channel variable upon completion:</para>
00268          <variablelist>
00269             <variable name="VMBOXEXISTSSTATUS">
00270                <para>This will contain the status of the execution of the MailboxExists application.
00271                Possible values include:</para>
00272                <value name="SUCCESS" />
00273                <value name="FAILED" />
00274             </variable>
00275          </variablelist>
00276       </description>
00277    </application>
00278    <application name="VMAuthenticate" language="en_US">
00279       <synopsis>
00280          Authenticate with Voicemail passwords.
00281       </synopsis>
00282       <syntax>
00283          <parameter name="mailbox" required="true" argsep="@">
00284             <argument name="mailbox" />
00285             <argument name="context" />
00286          </parameter>
00287          <parameter name="options">
00288             <optionlist>
00289                <option name="s">
00290                   <para>Skip playing the initial prompts.</para>
00291                </option>
00292             </optionlist>
00293          </parameter>
00294       </syntax>
00295       <description>
00296          <para>This application behaves the same way as the Authenticate application, but the passwords
00297          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00298          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00299          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00300          mailbox.</para>
00301       </description>
00302    </application>
00303    <function name="MAILBOX_EXISTS" language="en_US">
00304       <synopsis>
00305          Tell if a mailbox is configured.
00306       </synopsis>
00307       <syntax argsep="@">
00308          <parameter name="mailbox" required="true" />
00309          <parameter name="context" />
00310       </syntax>
00311       <description>
00312          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00313          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00314          context.</para>
00315       </description>
00316    </function>
00317  ***/
00318 
00319 #ifdef IMAP_STORAGE
00320 static char imapserver[48];
00321 static char imapport[8];
00322 static char imapflags[128];
00323 static char imapfolder[64];
00324 static char imapparentfolder[64] = "\0";
00325 static char greetingfolder[64];
00326 static char authuser[32];
00327 static char authpassword[42];
00328 static int imapversion = 1;
00329 
00330 static int expungeonhangup = 1;
00331 static int imapgreetings = 0;
00332 static char delimiter = '\0';
00333 
00334 struct vm_state;
00335 struct ast_vm_user;
00336 
00337 AST_THREADSTORAGE(ts_vmstate);
00338 
00339 /* Forward declarations for IMAP */
00340 static int init_mailstream(struct vm_state *vms, int box);
00341 static void write_file(char *filename, char *buffer, unsigned long len);
00342 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00343 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00344 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00345 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00346 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00347 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00348 static void vmstate_insert(struct vm_state *vms);
00349 static void vmstate_delete(struct vm_state *vms);
00350 static void set_update(MAILSTREAM * stream);
00351 static void init_vm_state(struct vm_state *vms);
00352 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00353 static void get_mailbox_delimiter(MAILSTREAM *stream);
00354 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00355 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00356 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);
00357 static void update_messages_by_imapuser(const char *user, unsigned long number);
00358 static int vm_delete(char *file);
00359 
00360 static int imap_remove_file (char *dir, int msgnum);
00361 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00362 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00363 static void check_quota(struct vm_state *vms, char *mailbox);
00364 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00365 struct vmstate {
00366    struct vm_state *vms;
00367    AST_LIST_ENTRY(vmstate) list;
00368 };
00369 
00370 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00371 
00372 #endif
00373 
00374 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00375 
00376 #define COMMAND_TIMEOUT 5000
00377 /* Don't modify these here; set your umask at runtime instead */
00378 #define  VOICEMAIL_DIR_MODE   0777
00379 #define  VOICEMAIL_FILE_MODE  0666
00380 #define  CHUNKSIZE   65536
00381 
00382 #define VOICEMAIL_CONFIG "voicemail.conf"
00383 #define ASTERISK_USERNAME "asterisk"
00384 
00385 /* Define fast-forward, pause, restart, and reverse keys
00386    while listening to a voicemail message - these are
00387    strings, not characters */
00388 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00389 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00390 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00391 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00392 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00393 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00394 
00395 /* Default mail command to mail voicemail. Change it with the
00396     mailcmd= command in voicemail.conf */
00397 #define SENDMAIL "/usr/sbin/sendmail -t"
00398 
00399 #define INTRO "vm-intro"
00400 
00401 #define MAXMSG 100
00402 #define MAXMSGLIMIT 9999
00403 
00404 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00405 
00406 #define BASELINELEN 72
00407 #define BASEMAXINLINE 256
00408 #ifdef IMAP_STORAGE
00409 #define ENDL "\r\n"
00410 #else
00411 #define ENDL "\n"
00412 #endif
00413 
00414 #define MAX_DATETIME_FORMAT   512
00415 #define MAX_NUM_CID_CONTEXTS 10
00416 
00417 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00418 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00419 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00420 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00421 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00422 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00423 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00424 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00425 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00426 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00427 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00428 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00429 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00430 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00431 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00432 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00433 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00434 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00435 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00436 #define ERROR_LOCK_PATH  -100
00437 #define OPERATOR_EXIT     300
00438 
00439 
00440 enum {
00441    NEW_FOLDER,
00442    OLD_FOLDER,
00443    WORK_FOLDER,
00444    FAMILY_FOLDER,
00445    FRIENDS_FOLDER,
00446    GREETINGS_FOLDER
00447 } vm_box;
00448 
00449 enum {
00450    OPT_SILENT =           (1 << 0),
00451    OPT_BUSY_GREETING =    (1 << 1),
00452    OPT_UNAVAIL_GREETING = (1 << 2),
00453    OPT_RECORDGAIN =       (1 << 3),
00454    OPT_PREPEND_MAILBOX =  (1 << 4),
00455    OPT_AUTOPLAY =         (1 << 6),
00456    OPT_DTMFEXIT =         (1 << 7),
00457    OPT_MESSAGE_Urgent =   (1 << 8),
00458    OPT_MESSAGE_PRIORITY = (1 << 9)
00459 } vm_option_flags;
00460 
00461 enum {
00462    OPT_ARG_RECORDGAIN = 0,
00463    OPT_ARG_PLAYFOLDER = 1,
00464    OPT_ARG_DTMFEXIT   = 2,
00465    /* This *must* be the last value in this enum! */
00466    OPT_ARG_ARRAY_SIZE = 3,
00467 } vm_option_args;
00468 
00469 AST_APP_OPTIONS(vm_app_options, {
00470    AST_APP_OPTION('s', OPT_SILENT),
00471    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00472    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00473    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00474    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00475    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00476    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00477    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00478    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00479 });
00480 
00481 static int load_config(int reload);
00482 
00483 /*! \page vmlang Voicemail Language Syntaxes Supported
00484 
00485    \par Syntaxes supported, not really language codes.
00486    \arg \b en    - English
00487    \arg \b de    - German
00488    \arg \b es    - Spanish
00489    \arg \b fr    - French
00490    \arg \b it    - Italian
00491    \arg \b nl    - Dutch
00492    \arg \b pt    - Portuguese
00493    \arg \b pt_BR - Portuguese (Brazil)
00494    \arg \b gr    - Greek
00495    \arg \b no    - Norwegian
00496    \arg \b se    - Swedish
00497    \arg \b tw    - Chinese (Taiwan)
00498    \arg \b ua - Ukrainian
00499 
00500 German requires the following additional soundfile:
00501 \arg \b 1F  einE (feminine)
00502 
00503 Spanish requires the following additional soundfile:
00504 \arg \b 1M      un (masculine)
00505 
00506 Dutch, Portuguese & Spanish require the following additional soundfiles:
00507 \arg \b vm-INBOXs singular of 'new'
00508 \arg \b vm-Olds      singular of 'old/heard/read'
00509 
00510 NB these are plural:
00511 \arg \b vm-INBOX  nieuwe (nl)
00512 \arg \b vm-Old    oude (nl)
00513 
00514 Polish uses:
00515 \arg \b vm-new-a  'new', feminine singular accusative
00516 \arg \b vm-new-e  'new', feminine plural accusative
00517 \arg \b vm-new-ych   'new', feminine plural genitive
00518 \arg \b vm-old-a  'old', feminine singular accusative
00519 \arg \b vm-old-e  'old', feminine plural accusative
00520 \arg \b vm-old-ych   'old', feminine plural genitive
00521 \arg \b digits/1-a   'one', not always same as 'digits/1'
00522 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00523 
00524 Swedish uses:
00525 \arg \b vm-nytt      singular of 'new'
00526 \arg \b vm-nya    plural of 'new'
00527 \arg \b vm-gammalt   singular of 'old'
00528 \arg \b vm-gamla  plural of 'old'
00529 \arg \b digits/ett   'one', not always same as 'digits/1'
00530 
00531 Norwegian uses:
00532 \arg \b vm-ny     singular of 'new'
00533 \arg \b vm-nye    plural of 'new'
00534 \arg \b vm-gammel singular of 'old'
00535 \arg \b vm-gamle  plural of 'old'
00536 
00537 Dutch also uses:
00538 \arg \b nl-om     'at'?
00539 
00540 Spanish also uses:
00541 \arg \b vm-youhaveno
00542 
00543 Italian requires the following additional soundfile:
00544 
00545 For vm_intro_it:
00546 \arg \b vm-nuovo  new
00547 \arg \b vm-nuovi  new plural
00548 \arg \b vm-vecchio   old
00549 \arg \b vm-vecchi old plural
00550 
00551 Chinese (Taiwan) requires the following additional soundfile:
00552 \arg \b vm-tong      A class-word for call (tong1)
00553 \arg \b vm-ri     A class-word for day (ri4)
00554 \arg \b vm-you    You (ni3)
00555 \arg \b vm-haveno   Have no (mei2 you3)
00556 \arg \b vm-have     Have (you3)
00557 \arg \b vm-listen   To listen (yao4 ting1)
00558 
00559 
00560 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00561 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00562 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00563 
00564 */
00565 
00566 struct baseio {
00567    int iocp;
00568    int iolen;
00569    int linelength;
00570    int ateof;
00571    unsigned char iobuf[BASEMAXINLINE];
00572 };
00573 
00574 /*! Structure for linked list of users 
00575  * Use ast_vm_user_destroy() to free one of these structures. */
00576 struct ast_vm_user {
00577    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00578    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00579    char password[80];               /*!< Secret pin code, numbers only */
00580    char fullname[80];               /*!< Full name, for directory app */
00581    char email[80];                  /*!< E-mail address */
00582    char *emailsubject;              /*!< E-mail subject */
00583    char *emailbody;                 /*!< E-mail body */
00584    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00585    char serveremail[80];            /*!< From: Mail address */
00586    char mailcmd[160];               /*!< Configurable mail command */
00587    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00588    char zonetag[80];                /*!< Time zone */
00589    char callback[80];
00590    char dialout[80];
00591    char uniqueid[80];               /*!< Unique integer identifier */
00592    char exit[80];
00593    char attachfmt[20];              /*!< Attachment format */
00594    unsigned int flags;              /*!< VM_ flags */ 
00595    int saydurationm;
00596    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00597    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00598    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00599 #ifdef IMAP_STORAGE
00600    char imapuser[80];               /*!< IMAP server login */
00601    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00602    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00603    int imapversion;                 /*!< If configuration changes, use the new values */
00604 #endif
00605    double volgain;                  /*!< Volume gain for voicemails sent via email */
00606    AST_LIST_ENTRY(ast_vm_user) list;
00607 };
00608 
00609 /*! Voicemail time zones */
00610 struct vm_zone {
00611    AST_LIST_ENTRY(vm_zone) list;
00612    char name[80];
00613    char timezone[80];
00614    char msg_format[512];
00615 };
00616 
00617 #define VMSTATE_MAX_MSG_ARRAY 256
00618 
00619 /*! Voicemail mailbox state */
00620 struct vm_state {
00621    char curbox[80];
00622    char username[80];
00623    char context[80];
00624    char curdir[PATH_MAX];
00625    char vmbox[PATH_MAX];
00626    char fn[PATH_MAX];
00627    char intro[PATH_MAX];
00628    int *deleted;
00629    int *heard;
00630    int dh_arraysize; /* used for deleted / heard allocation */
00631    int curmsg;
00632    int lastmsg;
00633    int newmessages;
00634    int oldmessages;
00635    int urgentmessages;
00636    int starting;
00637    int repeats;
00638 #ifdef IMAP_STORAGE
00639    ast_mutex_t lock;
00640    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00641    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00642    MAILSTREAM *mailstream;
00643    int vmArrayIndex;
00644    char imapuser[80];                   /*!< IMAP server login */
00645    int imapversion;
00646    int interactive;
00647    char introfn[PATH_MAX];              /*!< Name of prepended file */
00648    unsigned int quota_limit;
00649    unsigned int quota_usage;
00650    struct vm_state *persist_vms;
00651 #endif
00652 };
00653 
00654 #ifdef ODBC_STORAGE
00655 static char odbc_database[80];
00656 static char odbc_table[80];
00657 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00658 #define DISPOSE(a,b) remove_file(a,b)
00659 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00660 #define EXISTS(a,b,c,d) (message_exists(a,b))
00661 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00662 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00663 #define DELETE(a,b,c,d) (delete_file(a,b))
00664 #else
00665 #ifdef IMAP_STORAGE
00666 #define DISPOSE(a,b) (imap_remove_file(a,b))
00667 #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))
00668 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00669 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00670 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00671 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00672 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00673 #else
00674 #define RETRIEVE(a,b,c,d)
00675 #define DISPOSE(a,b)
00676 #define STORE(a,b,c,d,e,f,g,h,i,j)
00677 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00678 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00679 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00680 #define DELETE(a,b,c,d) (vm_delete(c))
00681 #endif
00682 #endif
00683 
00684 static char VM_SPOOL_DIR[PATH_MAX];
00685 
00686 static char ext_pass_cmd[128];
00687 static char ext_pass_check_cmd[128];
00688 
00689 static int my_umask;
00690 
00691 #define PWDCHANGE_INTERNAL (1 << 1)
00692 #define PWDCHANGE_EXTERNAL (1 << 2)
00693 static int pwdchange = PWDCHANGE_INTERNAL;
00694 
00695 #ifdef ODBC_STORAGE
00696 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00697 #else
00698 # ifdef IMAP_STORAGE
00699 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00700 # else
00701 # define tdesc "Comedian Mail (Voicemail System)"
00702 # endif
00703 #endif
00704 
00705 static char userscontext[AST_MAX_EXTENSION] = "default";
00706 
00707 static char *addesc = "Comedian Mail";
00708 
00709 /* Leave a message */
00710 static char *app = "VoiceMail";
00711 
00712 /* Check mail, control, etc */
00713 static char *app2 = "VoiceMailMain";
00714 
00715 static char *app3 = "MailboxExists";
00716 static char *app4 = "VMAuthenticate";
00717 
00718 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00719 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00720 static char zonetag[80];
00721 static int maxsilence;
00722 static int maxmsg;
00723 static int maxdeletedmsg;
00724 static int silencethreshold = 128;
00725 static char serveremail[80];
00726 static char mailcmd[160];  /* Configurable mail cmd */
00727 static char externnotify[160]; 
00728 static struct ast_smdi_interface *smdi_iface = NULL;
00729 static char vmfmts[80];
00730 static double volgain;
00731 static int vmminsecs;
00732 static int vmmaxsecs;
00733 static int maxgreet;
00734 static int skipms;
00735 static int maxlogins;
00736 static int minpassword;
00737 
00738 /*! Poll mailboxes for changes since there is something external to
00739  *  app_voicemail that may change them. */
00740 static unsigned int poll_mailboxes;
00741 
00742 /*! Polling frequency */
00743 static unsigned int poll_freq;
00744 /*! By default, poll every 30 seconds */
00745 #define DEFAULT_POLL_FREQ 30
00746 
00747 AST_MUTEX_DEFINE_STATIC(poll_lock);
00748 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00749 static pthread_t poll_thread = AST_PTHREADT_NULL;
00750 static unsigned char poll_thread_run;
00751 
00752 /*! Subscription to ... MWI event subscriptions */
00753 static struct ast_event_sub *mwi_sub_sub;
00754 /*! Subscription to ... MWI event un-subscriptions */
00755 static struct ast_event_sub *mwi_unsub_sub;
00756 
00757 /*!
00758  * \brief An MWI subscription
00759  *
00760  * This is so we can keep track of which mailboxes are subscribed to.
00761  * This way, we know which mailboxes to poll when the pollmailboxes
00762  * option is being used.
00763  */
00764 struct mwi_sub {
00765    AST_RWLIST_ENTRY(mwi_sub) entry;
00766    int old_urgent;
00767    int old_new;
00768    int old_old;
00769    uint32_t uniqueid;
00770    char mailbox[1];
00771 };
00772 
00773 struct mwi_sub_task {
00774    const char *mailbox;
00775    const char *context;
00776    uint32_t uniqueid;
00777 };
00778 
00779 static struct ast_taskprocessor *mwi_subscription_tps;
00780 
00781 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00782 
00783 /* custom audio control prompts for voicemail playback */
00784 static char listen_control_forward_key[12];
00785 static char listen_control_reverse_key[12];
00786 static char listen_control_pause_key[12];
00787 static char listen_control_restart_key[12];
00788 static char listen_control_stop_key[12];
00789 
00790 /* custom password sounds */
00791 static char vm_password[80] = "vm-password";
00792 static char vm_newpassword[80] = "vm-newpassword";
00793 static char vm_passchanged[80] = "vm-passchanged";
00794 static char vm_reenterpassword[80] = "vm-reenterpassword";
00795 static char vm_mismatch[80] = "vm-mismatch";
00796 static char vm_invalid_password[80] = "vm-invalid-password";
00797 static char vm_pls_try_again[80] = "vm-pls-try-again";
00798 
00799 static struct ast_flags globalflags = {0};
00800 
00801 static int saydurationminfo;
00802 
00803 static char dialcontext[AST_MAX_CONTEXT] = "";
00804 static char callcontext[AST_MAX_CONTEXT] = "";
00805 static char exitcontext[AST_MAX_CONTEXT] = "";
00806 
00807 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00808 
00809 
00810 static char *emailbody = NULL;
00811 static char *emailsubject = NULL;
00812 static char *pagerbody = NULL;
00813 static char *pagersubject = NULL;
00814 static char fromstring[100];
00815 static char pagerfromstring[100];
00816 static char charset[32] = "ISO-8859-1";
00817 
00818 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00819 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00820 static int adsiver = 1;
00821 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00822 
00823 /* Forward declarations - generic */
00824 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00825 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);
00826 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00827 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00828          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00829          signed char record_gain, struct vm_state *vms, char *flag);
00830 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00831 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00832 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);
00833 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);
00834 static void apply_options(struct ast_vm_user *vmu, const char *options);
00835 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);
00836 static int is_valid_dtmf(const char *key);
00837 
00838 struct ao2_container *inprocess_container;
00839 
00840 struct inprocess {
00841    int count;
00842    char *context;
00843    char mailbox[0];
00844 };
00845 
00846 static int inprocess_hash_fn(const void *obj, const int flags)
00847 {
00848    const struct inprocess *i = obj;
00849    return atoi(i->mailbox);
00850 }
00851 
00852 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00853 {
00854    struct inprocess *i = obj, *j = arg;
00855    if (strcmp(i->mailbox, j->mailbox)) {
00856       return 0;
00857    }
00858    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00859 }
00860 
00861 static int inprocess_count(const char *context, const char *mailbox, int delta)
00862 {
00863    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00864    arg->context = arg->mailbox + strlen(mailbox) + 1;
00865    strcpy(arg->mailbox, mailbox); /* SAFE */
00866    strcpy(arg->context, context); /* SAFE */
00867    ao2_lock(inprocess_container);
00868    if ((i = ao2_find(inprocess_container, arg, 0))) {
00869       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00870       ao2_unlock(inprocess_container);
00871       ao2_ref(i, -1);
00872       return ret;
00873    }
00874    if (delta < 0) {
00875       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00876    }
00877    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00878       ao2_unlock(inprocess_container);
00879       return 0;
00880    }
00881    i->context = i->mailbox + strlen(mailbox) + 1;
00882    strcpy(i->mailbox, mailbox); /* SAFE */
00883    strcpy(i->context, context); /* SAFE */
00884    i->count = delta;
00885    ao2_link(inprocess_container, i);
00886    ao2_unlock(inprocess_container);
00887    ao2_ref(i, -1);
00888    return 0;
00889 }
00890 
00891 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00892 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00893 #endif
00894 
00895 /*!
00896  * \brief Strips control and non 7-bit clean characters from input string.
00897  *
00898  * \note To map control and none 7-bit characters to a 7-bit clean characters
00899  *  please use ast_str_encode_mine().
00900  */
00901 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00902 {
00903    char *bufptr = buf;
00904    for (; *input; input++) {
00905       if (*input < 32) {
00906          continue;
00907       }
00908       *bufptr++ = *input;
00909       if (bufptr == buf + buflen - 1) {
00910          break;
00911       }
00912    }
00913    *bufptr = '\0';
00914    return buf;
00915 }
00916 
00917 
00918 /*!
00919  * \brief Sets default voicemail system options to a voicemail user.
00920  *
00921  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00922  * - all the globalflags
00923  * - the saydurationminfo
00924  * - the callcontext
00925  * - the dialcontext
00926  * - the exitcontext
00927  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00928  * - volume gain.
00929  */
00930 static void populate_defaults(struct ast_vm_user *vmu)
00931 {
00932    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00933    if (saydurationminfo)
00934       vmu->saydurationm = saydurationminfo;
00935    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00936    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00937    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00938    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00939    if (vmmaxsecs)
00940       vmu->maxsecs = vmmaxsecs;
00941    if (maxmsg)
00942       vmu->maxmsg = maxmsg;
00943    if (maxdeletedmsg)
00944       vmu->maxdeletedmsg = maxdeletedmsg;
00945    vmu->volgain = volgain;
00946    vmu->emailsubject = NULL;
00947    vmu->emailbody = NULL;
00948 }
00949 
00950 /*!
00951  * \brief Sets a a specific property value.
00952  * \param vmu The voicemail user object to work with.
00953  * \param var The name of the property to be set.
00954  * \param value The value to be set to the property.
00955  * 
00956  * The property name must be one of the understood properties. See the source for details.
00957  */
00958 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00959 {
00960    int x;
00961    if (!strcasecmp(var, "attach")) {
00962       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00963    } else if (!strcasecmp(var, "attachfmt")) {
00964       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00965    } else if (!strcasecmp(var, "serveremail")) {
00966       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00967    } else if (!strcasecmp(var, "language")) {
00968       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00969    } else if (!strcasecmp(var, "tz")) {
00970       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00971 #ifdef IMAP_STORAGE
00972    } else if (!strcasecmp(var, "imapuser")) {
00973       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00974       vmu->imapversion = imapversion;
00975    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00976       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00977       vmu->imapversion = imapversion;
00978    } else if (!strcasecmp(var, "imapvmshareid")) {
00979       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00980       vmu->imapversion = imapversion;
00981 #endif
00982    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00983       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00984    } else if (!strcasecmp(var, "saycid")){
00985       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00986    } else if (!strcasecmp(var,"sendvoicemail")){
00987       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00988    } else if (!strcasecmp(var, "review")){
00989       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00990    } else if (!strcasecmp(var, "tempgreetwarn")){
00991       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00992    } else if (!strcasecmp(var, "messagewrap")){
00993       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00994    } else if (!strcasecmp(var, "operator")) {
00995       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00996    } else if (!strcasecmp(var, "envelope")){
00997       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00998    } else if (!strcasecmp(var, "moveheard")){
00999       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01000    } else if (!strcasecmp(var, "sayduration")){
01001       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01002    } else if (!strcasecmp(var, "saydurationm")){
01003       if (sscanf(value, "%30d", &x) == 1) {
01004          vmu->saydurationm = x;
01005       } else {
01006          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01007       }
01008    } else if (!strcasecmp(var, "forcename")){
01009       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01010    } else if (!strcasecmp(var, "forcegreetings")){
01011       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01012    } else if (!strcasecmp(var, "callback")) {
01013       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01014    } else if (!strcasecmp(var, "dialout")) {
01015       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01016    } else if (!strcasecmp(var, "exitcontext")) {
01017       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01018    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01019       vmu->maxsecs = atoi(value);
01020       if (vmu->maxsecs <= 0) {
01021          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01022          vmu->maxsecs = vmmaxsecs;
01023       } else {
01024          vmu->maxsecs = atoi(value);
01025       }
01026       if (!strcasecmp(var, "maxmessage"))
01027          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01028    } else if (!strcasecmp(var, "maxmsg")) {
01029       vmu->maxmsg = atoi(value);
01030       if (vmu->maxmsg <= 0) {
01031          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01032          vmu->maxmsg = MAXMSG;
01033       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01034          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01035          vmu->maxmsg = MAXMSGLIMIT;
01036       }
01037    } else if (!strcasecmp(var, "backupdeleted")) {
01038       if (sscanf(value, "%30d", &x) == 1)
01039          vmu->maxdeletedmsg = x;
01040       else if (ast_true(value))
01041          vmu->maxdeletedmsg = MAXMSG;
01042       else
01043          vmu->maxdeletedmsg = 0;
01044 
01045       if (vmu->maxdeletedmsg < 0) {
01046          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01047          vmu->maxdeletedmsg = MAXMSG;
01048       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01049          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01050          vmu->maxdeletedmsg = MAXMSGLIMIT;
01051       }
01052    } else if (!strcasecmp(var, "volgain")) {
01053       sscanf(value, "%30lf", &vmu->volgain);
01054    } else if (!strcasecmp(var, "options")) {
01055       apply_options(vmu, value);
01056    }
01057 }
01058 
01059 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01060 {
01061    int fds[2], pid = 0;
01062 
01063    memset(buf, 0, len);
01064 
01065    if (pipe(fds)) {
01066       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01067    } else {
01068       /* good to go*/
01069       pid = ast_safe_fork(0);
01070 
01071       if (pid < 0) {
01072          /* ok maybe not */
01073          close(fds[0]);
01074          close(fds[1]);
01075          snprintf(buf, len, "FAILURE: Fork failed");
01076       } else if (pid) {
01077          /* parent */
01078          close(fds[1]);
01079          if (read(fds[0], buf, len) < 0) {
01080             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01081          }
01082          close(fds[0]);
01083       } else {
01084          /*  child */
01085          AST_DECLARE_APP_ARGS(arg,
01086             AST_APP_ARG(v)[20];
01087          );
01088          char *mycmd = ast_strdupa(command);
01089 
01090          close(fds[0]);
01091          dup2(fds[1], STDOUT_FILENO);
01092          close(fds[1]);
01093          ast_close_fds_above_n(STDOUT_FILENO);
01094 
01095          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01096 
01097          execv(arg.v[0], arg.v); 
01098          printf("FAILURE: %s", strerror(errno));
01099          _exit(0);
01100       }
01101    }
01102    return buf;
01103 }
01104 
01105 /*!
01106  * \brief Check that password meets minimum required length
01107  * \param vmu The voicemail user to change the password for.
01108  * \param password The password string to check
01109  *
01110  * \return zero on ok, 1 on not ok.
01111  */
01112 static int check_password(struct ast_vm_user *vmu, char *password)
01113 {
01114    /* check minimum length */
01115    if (strlen(password) < minpassword)
01116       return 1;
01117    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01118       char cmd[255], buf[255];
01119 
01120       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01121 
01122       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01123       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01124          ast_debug(5, "Result: %s\n", buf);
01125          if (!strncasecmp(buf, "VALID", 5)) {
01126             ast_debug(3, "Passed password check: '%s'\n", buf);
01127             return 0;
01128          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01129             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01130             return 0;
01131          } else {
01132             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01133             return 1;
01134          }
01135       }
01136    }
01137    return 0;
01138 }
01139 
01140 /*! 
01141  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01142  * \param vmu The voicemail user to change the password for.
01143  * \param password The new value to be set to the password for this user.
01144  * 
01145  * This only works if there is a realtime engine configured.
01146  * This is called from the (top level) vm_change_password.
01147  *
01148  * \return zero on success, -1 on error.
01149  */
01150 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01151 {
01152    int res = -1;
01153    if (!strcmp(vmu->password, password)) {
01154       /* No change (but an update would return 0 rows updated, so we opt out here) */
01155       return 0;
01156    }
01157 
01158    if (strlen(password) > 10) {
01159       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01160    }
01161    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01162       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01163       res = 0;
01164    }
01165    return res;
01166 }
01167 
01168 /*!
01169  * \brief Destructively Parse options and apply.
01170  */
01171 static void apply_options(struct ast_vm_user *vmu, const char *options)
01172 {  
01173    char *stringp;
01174    char *s;
01175    char *var, *value;
01176    stringp = ast_strdupa(options);
01177    while ((s = strsep(&stringp, "|"))) {
01178       value = s;
01179       if ((var = strsep(&value, "=")) && value) {
01180          apply_option(vmu, var, value);
01181       }
01182    }  
01183 }
01184 
01185 /*!
01186  * \brief Loads the options specific to a voicemail user.
01187  * 
01188  * This is called when a vm_user structure is being set up, such as from load_options.
01189  */
01190 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01191 {
01192    for (; var; var = var->next) {
01193       if (!strcasecmp(var->name, "vmsecret")) {
01194          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01195       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01196          if (ast_strlen_zero(retval->password))
01197             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01198       } else if (!strcasecmp(var->name, "uniqueid")) {
01199          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01200       } else if (!strcasecmp(var->name, "pager")) {
01201          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01202       } else if (!strcasecmp(var->name, "email")) {
01203          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01204       } else if (!strcasecmp(var->name, "fullname")) {
01205          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01206       } else if (!strcasecmp(var->name, "context")) {
01207          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01208       } else if (!strcasecmp(var->name, "emailsubject")) {
01209          retval->emailsubject = ast_strdup(var->value);
01210       } else if (!strcasecmp(var->name, "emailbody")) {
01211          retval->emailbody = ast_strdup(var->value);
01212 #ifdef IMAP_STORAGE
01213       } else if (!strcasecmp(var->name, "imapuser")) {
01214          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01215          retval->imapversion = imapversion;
01216       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01217          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01218          retval->imapversion = imapversion;
01219       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01220          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01221          retval->imapversion = imapversion;
01222 #endif
01223       } else
01224          apply_option(retval, var->name, var->value);
01225    }
01226 }
01227 
01228 /*!
01229  * \brief Determines if a DTMF key entered is valid.
01230  * \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.
01231  *
01232  * Tests the character entered against the set of valid DTMF characters. 
01233  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01234  */
01235 static int is_valid_dtmf(const char *key)
01236 {
01237    int i;
01238    char *local_key = ast_strdupa(key);
01239 
01240    for (i = 0; i < strlen(key); ++i) {
01241       if (!strchr(VALID_DTMF, *local_key)) {
01242          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01243          return 0;
01244       }
01245       local_key++;
01246    }
01247    return 1;
01248 }
01249 
01250 /*!
01251  * \brief Finds a voicemail user from the realtime engine.
01252  * \param ivm
01253  * \param context
01254  * \param mailbox
01255  *
01256  * 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.
01257  *
01258  * \return The ast_vm_user structure for the user that was found.
01259  */
01260 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01261 {
01262    struct ast_variable *var;
01263    struct ast_vm_user *retval;
01264 
01265    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01266       if (!ivm)
01267          ast_set_flag(retval, VM_ALLOCED);   
01268       else
01269          memset(retval, 0, sizeof(*retval));
01270       if (mailbox) 
01271          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01272       populate_defaults(retval);
01273       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01274          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01275       else
01276          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01277       if (var) {
01278          apply_options_full(retval, var);
01279          ast_variables_destroy(var);
01280       } else { 
01281          if (!ivm) 
01282             ast_free(retval);
01283          retval = NULL;
01284       }  
01285    } 
01286    return retval;
01287 }
01288 
01289 /*!
01290  * \brief Finds a voicemail user from the users file or the realtime engine.
01291  * \param ivm
01292  * \param context
01293  * \param mailbox
01294  * 
01295  * \return The ast_vm_user structure for the user that was found.
01296  */
01297 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01298 {
01299    /* This function could be made to generate one from a database, too */
01300    struct ast_vm_user *vmu=NULL, *cur;
01301    AST_LIST_LOCK(&users);
01302 
01303    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01304       context = "default";
01305 
01306    AST_LIST_TRAVERSE(&users, cur, list) {
01307 #ifdef IMAP_STORAGE
01308       if (cur->imapversion != imapversion) {
01309          continue;
01310       }
01311 #endif
01312       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01313          break;
01314       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01315          break;
01316    }
01317    if (cur) {
01318       /* Make a copy, so that on a reload, we have no race */
01319       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01320          memcpy(vmu, cur, sizeof(*vmu));
01321          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01322          AST_LIST_NEXT(vmu, list) = NULL;
01323       }
01324    } else
01325       vmu = find_user_realtime(ivm, context, mailbox);
01326    AST_LIST_UNLOCK(&users);
01327    return vmu;
01328 }
01329 
01330 /*!
01331  * \brief Resets a user password to a specified password.
01332  * \param context
01333  * \param mailbox
01334  * \param newpass
01335  *
01336  * This does the actual change password work, called by the vm_change_password() function.
01337  *
01338  * \return zero on success, -1 on error.
01339  */
01340 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01341 {
01342    /* This function could be made to generate one from a database, too */
01343    struct ast_vm_user *cur;
01344    int res = -1;
01345    AST_LIST_LOCK(&users);
01346    AST_LIST_TRAVERSE(&users, cur, list) {
01347       if ((!context || !strcasecmp(context, cur->context)) &&
01348          (!strcasecmp(mailbox, cur->mailbox)))
01349             break;
01350    }
01351    if (cur) {
01352       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01353       res = 0;
01354    }
01355    AST_LIST_UNLOCK(&users);
01356    return res;
01357 }
01358 
01359 /*! 
01360  * \brief The handler for the change password option.
01361  * \param vmu The voicemail user to work with.
01362  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01363  * 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.
01364  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01365  */
01366 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01367 {
01368    struct ast_config   *cfg=NULL;
01369    struct ast_variable *var=NULL;
01370    struct ast_category *cat=NULL;
01371    char *category=NULL, *value=NULL, *new=NULL;
01372    const char *tmp=NULL;
01373    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01374    if (!change_password_realtime(vmu, newpassword))
01375       return;
01376 
01377    /* check voicemail.conf */
01378    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01379       while ((category = ast_category_browse(cfg, category))) {
01380          if (!strcasecmp(category, vmu->context)) {
01381             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01382                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01383                break;
01384             }
01385             value = strstr(tmp,",");
01386             if (!value) {
01387                new = alloca(strlen(newpassword)+1);
01388                sprintf(new, "%s", newpassword);
01389             } else {
01390                new = alloca((strlen(value)+strlen(newpassword)+1));
01391                sprintf(new,"%s%s", newpassword, value);
01392             }
01393             if (!(cat = ast_category_get(cfg, category))) {
01394                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01395                break;
01396             }
01397             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01398          }
01399       }
01400       /* save the results */
01401       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01402       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01403       ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01404    }
01405    category = NULL;
01406    var = NULL;
01407    /* check users.conf and update the password stored for the mailbox*/
01408    /* if no vmsecret entry exists create one. */
01409    if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01410       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01411       while ((category = ast_category_browse(cfg, category))) {
01412          ast_debug(4, "users.conf: %s\n", category);
01413          if (!strcasecmp(category, vmu->mailbox)) {
01414             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01415                ast_debug(3, "looks like we need to make vmsecret!\n");
01416                var = ast_variable_new("vmsecret", newpassword, "");
01417             } 
01418             new = alloca(strlen(newpassword)+1);
01419             sprintf(new, "%s", newpassword);
01420             if (!(cat = ast_category_get(cfg, category))) {
01421                ast_debug(4, "failed to get category!\n");
01422                break;
01423             }
01424             if (!var)      
01425                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01426             else
01427                ast_variable_append(cat, var);
01428          }
01429       }
01430       /* save the results and clean things up */
01431       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01432       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01433       ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01434    }
01435 }
01436 
01437 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01438 {
01439    char buf[255];
01440    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01441    if (!ast_safe_system(buf)) {
01442       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01443       /* Reset the password in memory, too */
01444       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01445    }
01446 }
01447 
01448 /*! 
01449  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01450  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01451  * \param len The length of the path string that was written out.
01452  * 
01453  * The path is constructed as 
01454  *    VM_SPOOL_DIRcontext/ext/folder
01455  *
01456  * \return zero on success, -1 on error.
01457  */
01458 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01459 {
01460    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01461 }
01462 
01463 /*! 
01464  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01465  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01466  * \param len The length of the path string that was written out.
01467  * 
01468  * The path is constructed as 
01469  *    VM_SPOOL_DIRcontext/ext/folder
01470  *
01471  * \return zero on success, -1 on error.
01472  */
01473 static int make_file(char *dest, const int len, const char *dir, const int num)
01474 {
01475    return snprintf(dest, len, "%s/msg%04d", dir, num);
01476 }
01477 
01478 /* same as mkstemp, but return a FILE * */
01479 static FILE *vm_mkftemp(char *template)
01480 {
01481    FILE *p = NULL;
01482    int pfd = mkstemp(template);
01483    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01484    if (pfd > -1) {
01485       p = fdopen(pfd, "w+");
01486       if (!p) {
01487          close(pfd);
01488          pfd = -1;
01489       }
01490    }
01491    return p;
01492 }
01493 
01494 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01495  * \param dest    String. base directory.
01496  * \param len     Length of dest.
01497  * \param context String. Ignored if is null or empty string.
01498  * \param ext     String. Ignored if is null or empty string.
01499  * \param folder  String. Ignored if is null or empty string. 
01500  * \return -1 on failure, 0 on success.
01501  */
01502 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01503 {
01504    mode_t   mode = VOICEMAIL_DIR_MODE;
01505    int res;
01506 
01507    make_dir(dest, len, context, ext, folder);
01508    if ((res = ast_mkdir(dest, mode))) {
01509       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01510       return -1;
01511    }
01512    return 0;
01513 }
01514 
01515 static const char *mbox(int id)
01516 {
01517    static const char *msgs[] = {
01518 #ifdef IMAP_STORAGE
01519       imapfolder,
01520 #else
01521       "INBOX",
01522 #endif
01523       "Old",
01524       "Work",
01525       "Family",
01526       "Friends",
01527       "Cust1",
01528       "Cust2",
01529       "Cust3",
01530       "Cust4",
01531       "Cust5",
01532       "Deleted",
01533       "Urgent"
01534    };
01535    return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
01536 }
01537 
01538 static void free_user(struct ast_vm_user *vmu)
01539 {
01540    if (ast_test_flag(vmu, VM_ALLOCED)) {
01541       if (vmu->emailbody != NULL) {
01542          ast_free(vmu->emailbody);
01543          vmu->emailbody = NULL;
01544       }
01545       if (vmu->emailsubject != NULL) {
01546          ast_free(vmu->emailsubject);
01547          vmu->emailsubject = NULL;
01548       }
01549       ast_free(vmu);
01550    }
01551 }
01552 
01553 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01554 
01555    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01556    if (!vms->dh_arraysize) {
01557       /* initial allocation */
01558       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01559          return -1;
01560       }
01561       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01562          return -1;
01563       }
01564       vms->dh_arraysize = arraysize;
01565    } else if (vms->dh_arraysize < arraysize) {
01566       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01567          return -1;
01568       }
01569       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01570          return -1;
01571       }
01572       memset(vms->deleted, 0, arraysize * sizeof(int));
01573       memset(vms->heard, 0, arraysize * sizeof(int));
01574       vms->dh_arraysize = arraysize;
01575    }
01576 
01577    return 0;
01578 }
01579 
01580 /* All IMAP-specific functions should go in this block. This
01581  * keeps them from being spread out all over the code */
01582 #ifdef IMAP_STORAGE
01583 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01584 {
01585    char arg[10];
01586    struct vm_state *vms;
01587    unsigned long messageNum;
01588 
01589    /* If greetings aren't stored in IMAP, just delete the file */
01590    if (msgnum < 0 && !imapgreetings) {
01591       ast_filedelete(file, NULL);
01592       return;
01593    }
01594 
01595    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01596       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);
01597       return;
01598    }
01599 
01600    /* find real message number based on msgnum */
01601    /* this may be an index into vms->msgArray based on the msgnum. */
01602    messageNum = vms->msgArray[msgnum];
01603    if (messageNum == 0) {
01604       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01605       return;
01606    }
01607    if (option_debug > 2)
01608       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01609    /* delete message */
01610    snprintf (arg, sizeof(arg), "%lu",messageNum);
01611    ast_mutex_lock(&vms->lock);
01612    mail_setflag (vms->mailstream,arg,"\\DELETED");
01613    mail_expunge(vms->mailstream);
01614    ast_mutex_unlock(&vms->lock);
01615 }
01616 
01617 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01618 {
01619    struct vm_state *vms_p;
01620    char *file, *filename;
01621    char *attachment;
01622    int ret = 0, i;
01623    BODY *body;
01624 
01625    /* This function is only used for retrieval of IMAP greetings
01626     * regular messages are not retrieved this way, nor are greetings
01627     * if they are stored locally*/
01628    if (msgnum > -1 || !imapgreetings) {
01629       return 0;
01630    } else {
01631       file = strrchr(ast_strdupa(dir), '/');
01632       if (file)
01633          *file++ = '\0';
01634       else {
01635          ast_debug (1, "Failed to procure file name from directory passed.\n");
01636          return -1;
01637       }
01638    }
01639 
01640    /* check if someone is accessing this box right now... */
01641    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01642       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01643       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01644       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01645       * that's all we need to do.
01646       */
01647       if (!(vms_p = create_vm_state_from_user(vmu))) {
01648          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01649          return -1;
01650       }
01651    }
01652    
01653    /* Greetings will never have a prepended message */
01654    *vms_p->introfn = '\0';
01655 
01656    ast_mutex_lock(&vms_p->lock);
01657    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01658    if (!vms_p->mailstream) {
01659       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01660       ast_mutex_unlock(&vms_p->lock);
01661       return -1;
01662    }
01663 
01664    /*XXX Yuck, this could probably be done a lot better */
01665    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01666       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01667       /* We have the body, now we extract the file name of the first attachment. */
01668       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01669          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01670       } else {
01671          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01672          ast_mutex_unlock(&vms_p->lock);
01673          return -1;
01674       }
01675       filename = strsep(&attachment, ".");
01676       if (!strcmp(filename, file)) {
01677          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01678          vms_p->msgArray[vms_p->curmsg] = i + 1;
01679          save_body(body, vms_p, "2", attachment, 0);
01680          ast_mutex_unlock(&vms_p->lock);
01681          return 0;
01682       }
01683    }
01684    ast_mutex_unlock(&vms_p->lock);
01685 
01686    return -1;
01687 }
01688 
01689 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01690 {
01691    BODY *body;
01692    char *header_content;
01693    char *attachedfilefmt;
01694    char buf[80];
01695    struct vm_state *vms;
01696    char text_file[PATH_MAX];
01697    FILE *text_file_ptr;
01698    int res = 0;
01699    struct ast_vm_user *vmu;
01700 
01701    if (!(vmu = find_user(NULL, context, mailbox))) {
01702       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01703       return -1;
01704    }
01705    
01706    if (msgnum < 0) {
01707       if (imapgreetings) {
01708          res = imap_retrieve_greeting(dir, msgnum, vmu);
01709          goto exit;
01710       } else {
01711          res = 0;
01712          goto exit;
01713       }
01714    }
01715 
01716    /* Before anything can happen, we need a vm_state so that we can
01717     * actually access the imap server through the vms->mailstream
01718     */
01719    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01720       /* This should not happen. If it does, then I guess we'd
01721        * need to create the vm_state, extract which mailbox to
01722        * open, and then set up the msgArray so that the correct
01723        * IMAP message could be accessed. If I have seen correctly
01724        * though, the vms should be obtainable from the vmstates list
01725        * and should have its msgArray properly set up.
01726        */
01727       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01728       res = -1;
01729       goto exit;
01730    }
01731    
01732    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01733    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01734 
01735    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01736    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01737       res = 0;
01738       goto exit;
01739    }
01740 
01741    if (option_debug > 2)
01742       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01743    if (vms->msgArray[msgnum] == 0) {
01744       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01745       res = -1;
01746       goto exit;
01747    }
01748 
01749    /* This will only work for new messages... */
01750    ast_mutex_lock(&vms->lock);
01751    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01752    ast_mutex_unlock(&vms->lock);
01753    /* empty string means no valid header */
01754    if (ast_strlen_zero(header_content)) {
01755       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01756       res = -1;
01757       goto exit;
01758    }
01759 
01760    ast_mutex_lock(&vms->lock);
01761    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01762    ast_mutex_unlock(&vms->lock);
01763 
01764    /* We have the body, now we extract the file name of the first attachment. */
01765    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01766       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01767    } else {
01768       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01769       res = -1;
01770       goto exit;
01771    }
01772    
01773    /* Find the format of the attached file */
01774 
01775    strsep(&attachedfilefmt, ".");
01776    if (!attachedfilefmt) {
01777       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01778       res = -1;
01779       goto exit;
01780    }
01781    
01782    save_body(body, vms, "2", attachedfilefmt, 0);
01783    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01784       *vms->introfn = '\0';
01785    }
01786 
01787    /* Get info from headers!! */
01788    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01789 
01790    if (!(text_file_ptr = fopen(text_file, "w"))) {
01791       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01792    }
01793 
01794    fprintf(text_file_ptr, "%s\n", "[message]");
01795 
01796    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01797    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01798    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01799    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01800    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01801    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01802    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01803    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01804    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01805    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01806    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01807    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01808    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01809    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01810    fclose(text_file_ptr);
01811 
01812 exit:
01813    free_user(vmu);
01814    return res;
01815 }
01816 
01817 static int folder_int(const char *folder)
01818 {
01819    /*assume a NULL folder means INBOX*/
01820    if (!folder) {
01821       return 0;
01822    }
01823    if (!strcasecmp(folder, imapfolder)) {
01824       return 0;
01825    } else if (!strcasecmp(folder, "Old")) {
01826       return 1;
01827    } else if (!strcasecmp(folder, "Work")) {
01828       return 2;
01829    } else if (!strcasecmp(folder, "Family")) {
01830       return 3;
01831    } else if (!strcasecmp(folder, "Friends")) {
01832       return 4;
01833    } else if (!strcasecmp(folder, "Cust1")) {
01834       return 5;
01835    } else if (!strcasecmp(folder, "Cust2")) {
01836       return 6;
01837    } else if (!strcasecmp(folder, "Cust3")) {
01838       return 7;
01839    } else if (!strcasecmp(folder, "Cust4")) {
01840       return 8;
01841    } else if (!strcasecmp(folder, "Cust5")) {
01842       return 9;
01843    } else if (!strcasecmp(folder, "Urgent")) {
01844       return 11;
01845    } else { /*assume they meant INBOX if folder is not found otherwise*/
01846       return 0;
01847    }
01848 }
01849 
01850 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01851 {
01852    SEARCHPGM *pgm;
01853    SEARCHHEADER *hdr;
01854 
01855    struct ast_vm_user *vmu, vmus;
01856    struct vm_state *vms_p;
01857    int ret = 0;
01858    int fold = folder_int(folder);
01859    int urgent = 0;
01860    
01861    /* If URGENT, then look at INBOX */
01862    if (fold == 11) {
01863       fold = NEW_FOLDER;
01864       urgent = 1;
01865    }
01866 
01867    if (ast_strlen_zero(mailbox))
01868       return 0;
01869 
01870    /* We have to get the user before we can open the stream! */
01871    vmu = find_user(&vmus, context, mailbox);
01872    if (!vmu) {
01873       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01874       return -1;
01875    } else {
01876       /* No IMAP account available */
01877       if (vmu->imapuser[0] == '\0') {
01878          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01879          return -1;
01880       }
01881    }
01882    
01883    /* No IMAP account available */
01884    if (vmu->imapuser[0] == '\0') {
01885       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01886       free_user(vmu);
01887       return -1;
01888    }
01889 
01890    /* check if someone is accessing this box right now... */
01891    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01892    if (!vms_p) {
01893       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01894    }
01895    if (vms_p) {
01896       ast_debug(3, "Returning before search - user is logged in\n");
01897       if (fold == 0) { /* INBOX */
01898          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
01899       }
01900       if (fold == 1) { /* Old messages */
01901          return vms_p->oldmessages;
01902       }
01903    }
01904 
01905    /* add one if not there... */
01906    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01907    if (!vms_p) {
01908       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01909    }
01910 
01911    if (!vms_p) {
01912       vms_p = create_vm_state_from_user(vmu);
01913    }
01914    ret = init_mailstream(vms_p, fold);
01915    if (!vms_p->mailstream) {
01916       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01917       return -1;
01918    }
01919    if (ret == 0) {
01920       ast_mutex_lock(&vms_p->lock);
01921       pgm = mail_newsearchpgm ();
01922       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01923       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01924       pgm->header = hdr;
01925       if (fold != OLD_FOLDER) {
01926          pgm->unseen = 1;
01927          pgm->seen = 0;
01928       }
01929       /* In the special case where fold is 1 (old messages) we have to do things a bit
01930        * differently. Old messages are stored in the INBOX but are marked as "seen"
01931        */
01932       else {
01933          pgm->unseen = 0;
01934          pgm->seen = 1;
01935       }
01936       /* look for urgent messages */
01937       if (fold == NEW_FOLDER) {
01938          if (urgent) {
01939             pgm->flagged = 1;
01940             pgm->unflagged = 0;
01941          } else {
01942             pgm->flagged = 0;
01943             pgm->unflagged = 1;
01944          }
01945       }
01946       pgm->undeleted = 1;
01947       pgm->deleted = 0;
01948 
01949       vms_p->vmArrayIndex = 0;
01950       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01951       if (fold == 0 && urgent == 0)
01952          vms_p->newmessages = vms_p->vmArrayIndex;
01953       if (fold == 1)
01954          vms_p->oldmessages = vms_p->vmArrayIndex;
01955       if (fold == 0 && urgent == 1)
01956          vms_p->urgentmessages = vms_p->vmArrayIndex;
01957       /*Freeing the searchpgm also frees the searchhdr*/
01958       mail_free_searchpgm(&pgm);
01959       ast_mutex_unlock(&vms_p->lock);
01960       vms_p->updated = 0;
01961       return vms_p->vmArrayIndex;
01962    } else {
01963       ast_mutex_lock(&vms_p->lock);
01964       mail_ping(vms_p->mailstream);
01965       ast_mutex_unlock(&vms_p->lock);
01966    }
01967    return 0;
01968 }
01969 
01970 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01971 {
01972    /* Check if mailbox is full */
01973    check_quota(vms, imapfolder);
01974    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01975       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01976       ast_play_and_wait(chan, "vm-mailboxfull");
01977       return -1;
01978    }
01979    
01980    /* Check if we have exceeded maxmsg */
01981    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));
01982    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
01983       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
01984       ast_play_and_wait(chan, "vm-mailboxfull");
01985       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
01986       return -1;
01987    }
01988 
01989    return 0;
01990 }
01991 
01992 /*!
01993  * \brief Gets the number of messages that exist in a mailbox folder.
01994  * \param context
01995  * \param mailbox
01996  * \param folder
01997  * 
01998  * This method is used when IMAP backend is used.
01999  * \return The number of messages in this mailbox folder (zero or more).
02000  */
02001 static int messagecount(const char *context, const char *mailbox, const char *folder)
02002 {
02003    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02004       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02005    } else {
02006       return __messagecount(context, mailbox, folder);
02007    }
02008 }
02009 
02010 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)
02011 {
02012    char *myserveremail = serveremail;
02013    char fn[PATH_MAX];
02014    char introfn[PATH_MAX];
02015    char mailbox[256];
02016    char *stringp;
02017    FILE *p=NULL;
02018    char tmp[80] = "/tmp/astmail-XXXXXX";
02019    long len;
02020    void *buf;
02021    int tempcopy = 0;
02022    STRING str;
02023    int ret; /* for better error checking */
02024    char *imap_flags = NIL;
02025    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02026 
02027     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02028     if (msgnum < 0 && !imapgreetings) {
02029         return 0;
02030     }
02031    
02032    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02033       return -1;
02034    }
02035 
02036    /* Set urgent flag for IMAP message */
02037    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02038       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02039       imap_flags="\\FLAGGED";
02040    }
02041    
02042    /* Attach only the first format */
02043    fmt = ast_strdupa(fmt);
02044    stringp = fmt;
02045    strsep(&stringp, "|");
02046 
02047    if (!ast_strlen_zero(vmu->serveremail))
02048       myserveremail = vmu->serveremail;
02049 
02050    if (msgnum > -1)
02051       make_file(fn, sizeof(fn), dir, msgnum);
02052    else
02053       ast_copy_string (fn, dir, sizeof(fn));
02054 
02055    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02056    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02057       *introfn = '\0';
02058    }
02059    
02060    if (ast_strlen_zero(vmu->email)) {
02061       /* We need the vmu->email to be set when we call make_email_file, but
02062        * if we keep it set, a duplicate e-mail will be created. So at the end
02063        * of this function, we will revert back to an empty string if tempcopy
02064        * is 1.
02065        */
02066       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02067       tempcopy = 1;
02068    }
02069 
02070    if (!strcmp(fmt, "wav49"))
02071       fmt = "WAV";
02072    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02073 
02074    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02075       command hangs. */
02076    if (!(p = vm_mkftemp(tmp))) {
02077       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02078       if (tempcopy)
02079          *(vmu->email) = '\0';
02080       return -1;
02081    }
02082 
02083    if (msgnum < 0 && imapgreetings) {
02084       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02085          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02086          return -1;
02087       }
02088       imap_delete_old_greeting(fn, vms);
02089    }
02090 
02091    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02092    /* read mail file to memory */
02093    len = ftell(p);
02094    rewind(p);
02095    if (!(buf = ast_malloc(len + 1))) {
02096       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02097       fclose(p);
02098       if (tempcopy)
02099          *(vmu->email) = '\0';
02100       return -1;
02101    }
02102    if (fread(buf, len, 1, p) < len) {
02103       if (ferror(p)) {
02104          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02105          return -1;
02106       }
02107    }
02108    ((char *)buf)[len] = '\0';
02109    INIT(&str, mail_string, buf, len);
02110    ret = init_mailstream(vms, NEW_FOLDER);
02111    if (ret == 0) {
02112       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02113       ast_mutex_lock(&vms->lock);
02114       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02115          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02116       ast_mutex_unlock(&vms->lock);
02117       fclose(p);
02118       unlink(tmp);
02119       ast_free(buf);
02120    } else {
02121       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
02122       fclose(p);
02123       unlink(tmp);
02124       ast_free(buf);
02125       return -1;
02126    }
02127    ast_debug(3, "%s stored\n", fn);
02128    
02129    if (tempcopy)
02130       *(vmu->email) = '\0';
02131    inprocess_count(vmu->mailbox, vmu->context, -1);
02132    return 0;
02133 
02134 }
02135 
02136 /*!
02137  * \brief Gets the number of messages that exist in the inbox folder.
02138  * \param mailbox_context
02139  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02140  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02141  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02142  * 
02143  * This method is used when IMAP backend is used.
02144  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02145  *
02146  * \return zero on success, -1 on error.
02147  */
02148 
02149 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02150 {
02151    char tmp[PATH_MAX] = "";
02152    char *mailboxnc;
02153    char *context;
02154    char *mb;
02155    char *cur;
02156    if (newmsgs)
02157       *newmsgs = 0;
02158    if (oldmsgs)
02159       *oldmsgs = 0;
02160    if (urgentmsgs)
02161       *urgentmsgs = 0;
02162 
02163    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
02164    /* If no mailbox, return immediately */
02165    if (ast_strlen_zero(mailbox_context))
02166       return 0;
02167    
02168    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02169    context = strchr(tmp, '@');
02170    if (strchr(mailbox_context, ',')) {
02171       int tmpnew, tmpold, tmpurgent;
02172       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02173       mb = tmp;
02174       while ((cur = strsep(&mb, ", "))) {
02175          if (!ast_strlen_zero(cur)) {
02176             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02177                return -1;
02178             else {
02179                if (newmsgs)
02180                   *newmsgs += tmpnew; 
02181                if (oldmsgs)
02182                   *oldmsgs += tmpold;
02183                if (urgentmsgs)
02184                   *urgentmsgs += tmpurgent;
02185             }
02186          }
02187       }
02188       return 0;
02189    }
02190    if (context) {
02191       *context = '\0';
02192       mailboxnc = tmp;
02193       context++;
02194    } else {
02195       context = "default";
02196       mailboxnc = (char *)mailbox_context;
02197    }
02198    if (newmsgs) {
02199       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02200          return -1;
02201       }
02202    }
02203    if (oldmsgs) {
02204       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02205          return -1;
02206       }
02207    }
02208    if (urgentmsgs) {
02209       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02210          return -1;
02211       }
02212    }
02213    return 0;
02214 }
02215 
02216 /** 
02217  * \brief Determines if the given folder has messages.
02218  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02219  * \param folder the folder to look in
02220  *
02221  * This function is used when the mailbox is stored in an IMAP back end.
02222  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02223  * \return 1 if the folder has one or more messages. zero otherwise.
02224  */
02225 
02226 static int has_voicemail(const char *mailbox, const char *folder)
02227 {
02228    char tmp[256], *tmp2, *box, *context;
02229    ast_copy_string(tmp, mailbox, sizeof(tmp));
02230    tmp2 = tmp;
02231    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02232       while ((box = strsep(&tmp2, ",&"))) {
02233          if (!ast_strlen_zero(box)) {
02234             if (has_voicemail(box, folder)) {
02235                return 1;
02236             }
02237          }
02238       }
02239    }
02240    if ((context = strchr(tmp, '@'))) {
02241       *context++ = '\0';
02242    } else {
02243       context = "default";
02244    }
02245    return __messagecount(context, tmp, folder) ? 1 : 0;
02246 }
02247 
02248 /*!
02249  * \brief Copies a message from one mailbox to another.
02250  * \param chan
02251  * \param vmu
02252  * \param imbox
02253  * \param msgnum
02254  * \param duration
02255  * \param recip
02256  * \param fmt
02257  * \param dir
02258  *
02259  * This works with IMAP storage based mailboxes.
02260  *
02261  * \return zero on success, -1 on error.
02262  */
02263 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)
02264 {
02265    struct vm_state *sendvms = NULL, *destvms = NULL;
02266    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02267    if (msgnum >= recip->maxmsg) {
02268       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02269       return -1;
02270    }
02271    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02272       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02273       return -1;
02274    }
02275    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02276       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02277       return -1;
02278    }
02279    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02280    ast_mutex_lock(&sendvms->lock);
02281    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02282       ast_mutex_unlock(&sendvms->lock);
02283       return 0;
02284    }
02285    ast_mutex_unlock(&sendvms->lock);
02286    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02287    return -1;
02288 }
02289 
02290 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02291 {
02292    char tmp[256], *t = tmp;
02293    size_t left = sizeof(tmp);
02294    
02295    if (box == OLD_FOLDER) {
02296       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02297    } else {
02298       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02299    }
02300 
02301    if (box == NEW_FOLDER) {
02302       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02303    } else {
02304       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02305    }
02306 
02307    /* Build up server information */
02308    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02309 
02310    /* Add authentication user if present */
02311    if (!ast_strlen_zero(authuser))
02312       ast_build_string(&t, &left, "/authuser=%s", authuser);
02313 
02314    /* Add flags if present */
02315    if (!ast_strlen_zero(imapflags))
02316       ast_build_string(&t, &left, "/%s", imapflags);
02317 
02318    /* End with username */
02319 #if 1
02320    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02321 #else
02322    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02323 #endif
02324    if (box == NEW_FOLDER || box == OLD_FOLDER)
02325       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02326    else if (box == GREETINGS_FOLDER)
02327       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02328    else {   /* Other folders such as Friends, Family, etc... */
02329       if (!ast_strlen_zero(imapparentfolder)) {
02330          /* imapparentfolder would typically be set to INBOX */
02331          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02332       } else {
02333          snprintf(spec, len, "%s%s", tmp, mbox(box));
02334       }
02335    }
02336 }
02337 
02338 static int init_mailstream(struct vm_state *vms, int box)
02339 {
02340    MAILSTREAM *stream = NIL;
02341    long debug;
02342    char tmp[256];
02343    
02344    if (!vms) {
02345       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02346       return -1;
02347    }
02348    if (option_debug > 2)
02349       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02350    if (vms->mailstream == NIL || !vms->mailstream) {
02351       if (option_debug)
02352          ast_log (LOG_DEBUG,"mailstream not set.\n");
02353    } else {
02354       stream = vms->mailstream;
02355    }
02356    /* debug = T;  user wants protocol telemetry? */
02357    debug = NIL;  /* NO protocol telemetry? */
02358 
02359    if (delimiter == '\0') {      /* did not probe the server yet */
02360       char *cp;
02361 #ifdef USE_SYSTEM_IMAP
02362 #include <imap/linkage.c>
02363 #elif defined(USE_SYSTEM_CCLIENT)
02364 #include <c-client/linkage.c>
02365 #else
02366 #include "linkage.c"
02367 #endif
02368       /* Connect to INBOX first to get folders delimiter */
02369       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02370       ast_mutex_lock(&vms->lock);
02371       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02372       ast_mutex_unlock(&vms->lock);
02373       if (stream == NIL) {
02374          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02375          return -1;
02376       }
02377       get_mailbox_delimiter(stream);
02378       /* update delimiter in imapfolder */
02379       for (cp = imapfolder; *cp; cp++)
02380          if (*cp == '/')
02381             *cp = delimiter;
02382    }
02383    /* Now connect to the target folder */
02384    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02385    if (option_debug > 2)
02386       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02387    ast_mutex_lock(&vms->lock);
02388    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02389    ast_mutex_unlock(&vms->lock);
02390    if (vms->mailstream == NIL) {
02391       return -1;
02392    } else {
02393       return 0;
02394    }
02395 }
02396 
02397 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02398 {
02399    SEARCHPGM *pgm;
02400    SEARCHHEADER *hdr;
02401    int ret, urgent = 0;
02402 
02403    /* If Urgent, then look at INBOX */
02404    if (box == 11) {
02405       box = NEW_FOLDER;
02406       urgent = 1;
02407    }
02408 
02409    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02410    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02411    vms->imapversion = vmu->imapversion;
02412 
02413    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02414       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02415       return -1;
02416    }
02417    
02418    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02419    
02420    /* Check Quota */
02421    if  (box == 0)  {
02422       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02423       check_quota(vms,(char *)mbox(box));
02424    }
02425 
02426    ast_mutex_lock(&vms->lock);
02427    pgm = mail_newsearchpgm();
02428 
02429    /* Check IMAP folder for Asterisk messages only... */
02430    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02431    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02432    pgm->header = hdr;
02433    pgm->deleted = 0;
02434    pgm->undeleted = 1;
02435 
02436    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02437    if (box == NEW_FOLDER && urgent == 1) {
02438       pgm->unseen = 1;
02439       pgm->seen = 0;
02440       pgm->flagged = 1;
02441       pgm->unflagged = 0;
02442    } else if (box == NEW_FOLDER && urgent == 0) {
02443       pgm->unseen = 1;
02444       pgm->seen = 0;
02445       pgm->flagged = 0;
02446       pgm->unflagged = 1;
02447    } else if (box == OLD_FOLDER) {
02448       pgm->seen = 1;
02449       pgm->unseen = 0;
02450    }
02451 
02452    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02453 
02454    vms->vmArrayIndex = 0;
02455    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02456    vms->lastmsg = vms->vmArrayIndex - 1;
02457    mail_free_searchpgm(&pgm);
02458    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02459     * ensure to allocate enough space to account for all of them. Warn if old messages
02460     * have not been checked first as that is required.
02461     */
02462    if (box == 0 && !vms->dh_arraysize) {
02463       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02464    }
02465    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02466       ast_mutex_unlock(&vms->lock);
02467       return -1;
02468    }
02469 
02470    ast_mutex_unlock(&vms->lock);
02471    return 0;
02472 }
02473 
02474 static void write_file(char *filename, char *buffer, unsigned long len)
02475 {
02476    FILE *output;
02477 
02478    output = fopen (filename, "w");
02479    if (fwrite(buffer, len, 1, output) != 1) {
02480       if (ferror(output)) {
02481          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02482       }
02483    }
02484    fclose (output);
02485 }
02486 
02487 static void update_messages_by_imapuser(const char *user, unsigned long number)
02488 {
02489    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02490 
02491    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02492       return;
02493    }
02494 
02495    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02496    vms->msgArray[vms->vmArrayIndex++] = number;
02497 }
02498 
02499 void mm_searched(MAILSTREAM *stream, unsigned long number)
02500 {
02501    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02502 
02503    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02504       return;
02505 
02506    update_messages_by_imapuser(user, number);
02507 }
02508 
02509 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02510 {
02511    struct ast_variable *var;
02512    struct ast_vm_user *vmu;
02513 
02514    vmu = ast_calloc(1, sizeof *vmu);
02515    if (!vmu)
02516       return NULL;
02517    ast_set_flag(vmu, VM_ALLOCED);
02518    populate_defaults(vmu);
02519 
02520    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02521    if (var) {
02522       apply_options_full(vmu, var);
02523       ast_variables_destroy(var);
02524       return vmu;
02525    } else {
02526       ast_free(vmu);
02527       return NULL;
02528    }
02529 }
02530 
02531 /* Interfaces to C-client */
02532 
02533 void mm_exists(MAILSTREAM * stream, unsigned long number)
02534 {
02535    /* mail_ping will callback here if new mail! */
02536    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02537    if (number == 0) return;
02538    set_update(stream);
02539 }
02540 
02541 
02542 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02543 {
02544    /* mail_ping will callback here if expunged mail! */
02545    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02546    if (number == 0) return;
02547    set_update(stream);
02548 }
02549 
02550 
02551 void mm_flags(MAILSTREAM * stream, unsigned long number)
02552 {
02553    /* mail_ping will callback here if read mail! */
02554    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02555    if (number == 0) return;
02556    set_update(stream);
02557 }
02558 
02559 
02560 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02561 {
02562    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02563    mm_log (string, errflg);
02564 }
02565 
02566 
02567 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02568 {
02569    if (delimiter == '\0') {
02570       delimiter = delim;
02571    }
02572 
02573    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02574    if (attributes & LATT_NOINFERIORS)
02575       ast_debug(5, "no inferiors\n");
02576    if (attributes & LATT_NOSELECT)
02577       ast_debug(5, "no select\n");
02578    if (attributes & LATT_MARKED)
02579       ast_debug(5, "marked\n");
02580    if (attributes & LATT_UNMARKED)
02581       ast_debug(5, "unmarked\n");
02582 }
02583 
02584 
02585 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02586 {
02587    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02588    if (attributes & LATT_NOINFERIORS)
02589       ast_debug(5, "no inferiors\n");
02590    if (attributes & LATT_NOSELECT)
02591       ast_debug(5, "no select\n");
02592    if (attributes & LATT_MARKED)
02593       ast_debug(5, "marked\n");
02594    if (attributes & LATT_UNMARKED)
02595       ast_debug(5, "unmarked\n");
02596 }
02597 
02598 
02599 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02600 {
02601    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02602    if (status->flags & SA_MESSAGES)
02603       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02604    if (status->flags & SA_RECENT)
02605       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02606    if (status->flags & SA_UNSEEN)
02607       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02608    if (status->flags & SA_UIDVALIDITY)
02609       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02610    if (status->flags & SA_UIDNEXT)
02611       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02612    ast_log(AST_LOG_NOTICE, "\n");
02613 }
02614 
02615 
02616 void mm_log(char *string, long errflg)
02617 {
02618    switch ((short) errflg) {
02619       case NIL:
02620          ast_debug(1,"IMAP Info: %s\n", string);
02621          break;
02622       case PARSE:
02623       case WARN:
02624          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02625          break;
02626       case ERROR:
02627          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02628          break;
02629    }
02630 }
02631 
02632 
02633 void mm_dlog(char *string)
02634 {
02635    ast_log(AST_LOG_NOTICE, "%s\n", string);
02636 }
02637 
02638 
02639 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02640 {
02641    struct ast_vm_user *vmu;
02642 
02643    ast_debug(4, "Entering callback mm_login\n");
02644 
02645    ast_copy_string(user, mb->user, MAILTMPLEN);
02646 
02647    /* We should only do this when necessary */
02648    if (!ast_strlen_zero(authpassword)) {
02649       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02650    } else {
02651       AST_LIST_TRAVERSE(&users, vmu, list) {
02652          if (!strcasecmp(mb->user, vmu->imapuser)) {
02653             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02654             break;
02655          }
02656       }
02657       if (!vmu) {
02658          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02659             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02660             free_user(vmu);
02661          }
02662       }
02663    }
02664 }
02665 
02666 
02667 void mm_critical(MAILSTREAM * stream)
02668 {
02669 }
02670 
02671 
02672 void mm_nocritical(MAILSTREAM * stream)
02673 {
02674 }
02675 
02676 
02677 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02678 {
02679    kill (getpid (), SIGSTOP);
02680    return NIL;
02681 }
02682 
02683 
02684 void mm_fatal(char *string)
02685 {
02686    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02687 }
02688 
02689 /* C-client callback to handle quota */
02690 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02691 {
02692    struct vm_state *vms;
02693    char *mailbox = stream->mailbox, *user;
02694    char buf[1024] = "";
02695    unsigned long usage = 0, limit = 0;
02696    
02697    while (pquota) {
02698       usage = pquota->usage;
02699       limit = pquota->limit;
02700       pquota = pquota->next;
02701    }
02702    
02703    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)))) {
02704       ast_log(AST_LOG_ERROR, "No state found.\n");
02705       return;
02706    }
02707 
02708    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02709 
02710    vms->quota_usage = usage;
02711    vms->quota_limit = limit;
02712 }
02713 
02714 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02715 {
02716    char *start, *eol_pnt;
02717    int taglen;
02718 
02719    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02720       return NULL;
02721 
02722    taglen = strlen(tag) + 1;
02723    if (taglen < 1)
02724       return NULL;
02725 
02726    if (!(start = strstr(header, tag)))
02727       return NULL;
02728 
02729    /* Since we can be called multiple times we should clear our buffer */
02730    memset(buf, 0, len);
02731 
02732    ast_copy_string(buf, start+taglen, len);
02733    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02734       *eol_pnt = '\0';
02735    return buf;
02736 }
02737 
02738 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02739 {
02740    char *start, *quote, *eol_pnt;
02741 
02742    if (ast_strlen_zero(mailbox))
02743       return NULL;
02744 
02745    if (!(start = strstr(mailbox, "/user=")))
02746       return NULL;
02747 
02748    ast_copy_string(buf, start+6, len);
02749 
02750    if (!(quote = strchr(buf, '\"'))) {
02751       if (!(eol_pnt = strchr(buf, '/')))
02752          eol_pnt = strchr(buf,'}');
02753       *eol_pnt = '\0';
02754       return buf;
02755    } else {
02756       eol_pnt = strchr(buf+1,'\"');
02757       *eol_pnt = '\0';
02758       return buf+1;
02759    }
02760 }
02761 
02762 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02763 {
02764    struct vm_state *vms_p;
02765 
02766    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02767    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02768       return vms_p;
02769    }
02770    if (option_debug > 4)
02771       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02772    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02773       return NULL;
02774    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02775    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02776    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02777    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02778    vms_p->imapversion = vmu->imapversion;
02779    if (option_debug > 4)
02780       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02781    vms_p->updated = 1;
02782    /* set mailbox to INBOX! */
02783    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02784    init_vm_state(vms_p);
02785    vmstate_insert(vms_p);
02786    return vms_p;
02787 }
02788 
02789 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02790 {
02791    struct vmstate *vlist = NULL;
02792 
02793    if (interactive) {
02794       struct vm_state *vms;
02795       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02796       vms = pthread_getspecific(ts_vmstate.key);
02797       return vms;
02798    }
02799 
02800    AST_LIST_LOCK(&vmstates);
02801    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02802       if (!vlist->vms) {
02803          ast_debug(3, "error: vms is NULL for %s\n", user);
02804          continue;
02805       }
02806       if (vlist->vms->imapversion != imapversion) {
02807          continue;
02808       }
02809       if (!vlist->vms->imapuser) {
02810          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02811          continue;
02812       }
02813 
02814       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02815          AST_LIST_UNLOCK(&vmstates);
02816          return vlist->vms;
02817       }
02818    }
02819    AST_LIST_UNLOCK(&vmstates);
02820 
02821    ast_debug(3, "%s not found in vmstates\n", user);
02822 
02823    return NULL;
02824 }
02825 
02826 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02827 {
02828 
02829    struct vmstate *vlist = NULL;
02830    const char *local_context = S_OR(context, "default");
02831 
02832    if (interactive) {
02833       struct vm_state *vms;
02834       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02835       vms = pthread_getspecific(ts_vmstate.key);
02836       return vms;
02837    }
02838 
02839    AST_LIST_LOCK(&vmstates);
02840    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02841       if (!vlist->vms) {
02842          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02843          continue;
02844       }
02845       if (vlist->vms->imapversion != imapversion) {
02846          continue;
02847       }
02848       if (!vlist->vms->username || !vlist->vms->context) {
02849          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02850          continue;
02851       }
02852 
02853       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);
02854       
02855       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02856          ast_debug(3, "Found it!\n");
02857          AST_LIST_UNLOCK(&vmstates);
02858          return vlist->vms;
02859       }
02860    }
02861    AST_LIST_UNLOCK(&vmstates);
02862 
02863    ast_debug(3, "%s not found in vmstates\n", mailbox);
02864 
02865    return NULL;
02866 }
02867 
02868 static void vmstate_insert(struct vm_state *vms) 
02869 {
02870    struct vmstate *v;
02871    struct vm_state *altvms;
02872 
02873    /* If interactive, it probably already exists, and we should
02874       use the one we already have since it is more up to date.
02875       We can compare the username to find the duplicate */
02876    if (vms->interactive == 1) {
02877       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02878       if (altvms) {  
02879          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02880          vms->newmessages = altvms->newmessages;
02881          vms->oldmessages = altvms->oldmessages;
02882          vms->vmArrayIndex = altvms->vmArrayIndex;
02883          vms->lastmsg = altvms->lastmsg;
02884          vms->curmsg = altvms->curmsg;
02885          /* get a pointer to the persistent store */
02886          vms->persist_vms = altvms;
02887          /* Reuse the mailstream? */
02888 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02889          vms->mailstream = altvms->mailstream;
02890 #else
02891          vms->mailstream = NIL;
02892 #endif
02893       }
02894       return;
02895    }
02896 
02897    if (!(v = ast_calloc(1, sizeof(*v))))
02898       return;
02899    
02900    v->vms = vms;
02901 
02902    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02903 
02904    AST_LIST_LOCK(&vmstates);
02905    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02906    AST_LIST_UNLOCK(&vmstates);
02907 }
02908 
02909 static void vmstate_delete(struct vm_state *vms) 
02910 {
02911    struct vmstate *vc = NULL;
02912    struct vm_state *altvms = NULL;
02913 
02914    /* If interactive, we should copy pertinent info
02915       back to the persistent state (to make update immediate) */
02916    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02917       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02918       altvms->newmessages = vms->newmessages;
02919       altvms->oldmessages = vms->oldmessages;
02920       altvms->updated = 1;
02921       vms->mailstream = mail_close(vms->mailstream);
02922 
02923       /* Interactive states are not stored within the persistent list */
02924       return;
02925    }
02926    
02927    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02928    
02929    AST_LIST_LOCK(&vmstates);
02930    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02931       if (vc->vms == vms) {
02932          AST_LIST_REMOVE_CURRENT(list);
02933          break;
02934       }
02935    }
02936    AST_LIST_TRAVERSE_SAFE_END
02937    AST_LIST_UNLOCK(&vmstates);
02938    
02939    if (vc) {
02940       ast_mutex_destroy(&vc->vms->lock);
02941       ast_free(vc);
02942    }
02943    else
02944       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02945 }
02946 
02947 static void set_update(MAILSTREAM * stream) 
02948 {
02949    struct vm_state *vms;
02950    char *mailbox = stream->mailbox, *user;
02951    char buf[1024] = "";
02952 
02953    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02954       if (user && option_debug > 2)
02955          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02956       return;
02957    }
02958 
02959    ast_debug(3, "User %s mailbox set for update.\n", user);
02960 
02961    vms->updated = 1; /* Set updated flag since mailbox changed */
02962 }
02963 
02964 static void init_vm_state(struct vm_state *vms) 
02965 {
02966    int x;
02967    vms->vmArrayIndex = 0;
02968    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02969       vms->msgArray[x] = 0;
02970    }
02971    ast_mutex_init(&vms->lock);
02972 }
02973 
02974 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02975 {
02976    char *body_content;
02977    char *body_decoded;
02978    char *fn = is_intro ? vms->introfn : vms->fn;
02979    unsigned long len;
02980    unsigned long newlen;
02981    char filename[256];
02982    
02983    if (!body || body == NIL)
02984       return -1;
02985 
02986    ast_mutex_lock(&vms->lock);
02987    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02988    ast_mutex_unlock(&vms->lock);
02989    if (body_content != NIL) {
02990       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02991       /* ast_debug(1,body_content); */
02992       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02993       /* If the body of the file is empty, return an error */
02994       if (!newlen) {
02995          return -1;
02996       }
02997       write_file(filename, (char *) body_decoded, newlen);
02998    } else {
02999       ast_debug(5, "Body of message is NULL.\n");
03000       return -1;
03001    }
03002    return 0;
03003 }
03004 
03005 /*! 
03006  * \brief Get delimiter via mm_list callback 
03007  * \param stream
03008  *
03009  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03010  */
03011 /* MUTEX should already be held */
03012 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03013    char tmp[50];
03014    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03015    mail_list(stream, tmp, "*");
03016 }
03017 
03018 /*! 
03019  * \brief Check Quota for user 
03020  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03021  * \param mailbox the mailbox to check the quota for.
03022  *
03023  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03024  */
03025 static void check_quota(struct vm_state *vms, char *mailbox) {
03026    ast_mutex_lock(&vms->lock);
03027    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03028    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03029    if (vms && vms->mailstream != NULL) {
03030       imap_getquotaroot(vms->mailstream, mailbox);
03031    } else {
03032       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03033    }
03034    ast_mutex_unlock(&vms->lock);
03035 }
03036 
03037 #endif /* IMAP_STORAGE */
03038 
03039 /*! \brief Lock file path
03040     only return failure if ast_lock_path returns 'timeout',
03041    not if the path does not exist or any other reason
03042 */
03043 static int vm_lock_path(const char *path)
03044 {
03045    switch (ast_lock_path(path)) {
03046    case AST_LOCK_TIMEOUT:
03047       return -1;
03048    default:
03049       return 0;
03050    }
03051 }
03052 
03053 
03054 #ifdef ODBC_STORAGE
03055 struct generic_prepare_struct {
03056    char *sql;
03057    int argc;
03058    char **argv;
03059 };
03060 
03061 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03062 {
03063    struct generic_prepare_struct *gps = data;
03064    int res, i;
03065    SQLHSTMT stmt;
03066 
03067    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03068    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03069       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03070       return NULL;
03071    }
03072    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
03073    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03074       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03075       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03076       return NULL;
03077    }
03078    for (i = 0; i < gps->argc; i++)
03079       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03080 
03081    return stmt;
03082 }
03083 
03084 /*!
03085  * \brief Retrieves a file from an ODBC data store.
03086  * \param dir the path to the file to be retreived.
03087  * \param msgnum the message number, such as within a mailbox folder.
03088  * 
03089  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03090  * 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.
03091  *
03092  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03093  * The output is the message information file with the name msgnum and the extension .txt
03094  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03095  * 
03096  * \return 0 on success, -1 on error.
03097  */
03098 static int retrieve_file(char *dir, int msgnum)
03099 {
03100    int x = 0;
03101    int res;
03102    int fd=-1;
03103    size_t fdlen = 0;
03104    void *fdm = MAP_FAILED;
03105    SQLSMALLINT colcount=0;
03106    SQLHSTMT stmt;
03107    char sql[PATH_MAX];
03108    char fmt[80]="";
03109    char *c;
03110    char coltitle[256];
03111    SQLSMALLINT collen;
03112    SQLSMALLINT datatype;
03113    SQLSMALLINT decimaldigits;
03114    SQLSMALLINT nullable;
03115    SQLULEN colsize;
03116    SQLLEN colsize2;
03117    FILE *f=NULL;
03118    char rowdata[80];
03119    char fn[PATH_MAX];
03120    char full_fn[PATH_MAX];
03121    char msgnums[80];
03122    char *argv[] = { dir, msgnums };
03123    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03124 
03125    struct odbc_obj *obj;
03126    obj = ast_odbc_request_obj(odbc_database, 0);
03127    if (obj) {
03128       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03129       c = strchr(fmt, '|');
03130       if (c)
03131          *c = '\0';
03132       if (!strcasecmp(fmt, "wav49"))
03133          strcpy(fmt, "WAV");
03134       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03135       if (msgnum > -1)
03136          make_file(fn, sizeof(fn), dir, msgnum);
03137       else
03138          ast_copy_string(fn, dir, sizeof(fn));
03139 
03140       /* Create the information file */
03141       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03142       
03143       if (!(f = fopen(full_fn, "w+"))) {
03144          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03145          goto yuck;
03146       }
03147       
03148       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03149       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03150       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03151       if (!stmt) {
03152          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03153          ast_odbc_release_obj(obj);
03154          goto yuck;
03155       }
03156       res = SQLFetch(stmt);
03157       if (res == SQL_NO_DATA) {
03158          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03159          ast_odbc_release_obj(obj);
03160          goto yuck;
03161       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03162          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03163          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03164          ast_odbc_release_obj(obj);
03165          goto yuck;
03166       }
03167       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03168       if (fd < 0) {
03169          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03170          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03171          ast_odbc_release_obj(obj);
03172          goto yuck;
03173       }
03174       res = SQLNumResultCols(stmt, &colcount);
03175       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03176          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03177          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03178          ast_odbc_release_obj(obj);
03179          goto yuck;
03180       }
03181       if (f) 
03182          fprintf(f, "[message]\n");
03183       for (x=0;x<colcount;x++) {
03184          rowdata[0] = '\0';
03185          colsize = 0;
03186          collen = sizeof(coltitle);
03187          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
03188                   &datatype, &colsize, &decimaldigits, &nullable);
03189          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03190             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03191             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03192             ast_odbc_release_obj(obj);
03193             goto yuck;
03194          }
03195          if (!strcasecmp(coltitle, "recording")) {
03196             off_t offset;
03197             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03198             fdlen = colsize2;
03199             if (fd > -1) {
03200                char tmp[1]="";
03201                lseek(fd, fdlen - 1, SEEK_SET);
03202                if (write(fd, tmp, 1) != 1) {
03203                   close(fd);
03204                   fd = -1;
03205                   continue;
03206                }
03207                /* Read out in small chunks */
03208                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03209                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03210                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03211                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03212                      ast_odbc_release_obj(obj);
03213                      goto yuck;
03214                   } else {
03215                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03216                      munmap(fdm, CHUNKSIZE);
03217                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03218                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03219                         unlink(full_fn);
03220                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03221                         ast_odbc_release_obj(obj);
03222                         goto yuck;
03223                      }
03224                   }
03225                }
03226                if (truncate(full_fn, fdlen) < 0) {
03227                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03228                }
03229             }
03230          } else {
03231             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03232             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03233                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03234                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03235                ast_odbc_release_obj(obj);
03236                goto yuck;
03237             }
03238             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03239                fprintf(f, "%s=%s\n", coltitle, rowdata);
03240          }
03241       }
03242       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03243       ast_odbc_release_obj(obj);
03244    } else
03245       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03246 yuck:
03247    if (f)
03248       fclose(f);
03249    if (fd > -1)
03250       close(fd);
03251    return x - 1;
03252 }
03253 
03254 /*!
03255  * \brief Determines the highest message number in use for a given user and mailbox folder.
03256  * \param vmu 
03257  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03258  *
03259  * This method is used when mailboxes are stored in an ODBC back end.
03260  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03261  *
03262  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03263  */
03264 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03265 {
03266    int x = 0;
03267    int res;
03268    SQLHSTMT stmt;
03269    char sql[PATH_MAX];
03270    char rowdata[20];
03271    char *argv[] = { dir };
03272    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03273 
03274    struct odbc_obj *obj;
03275    obj = ast_odbc_request_obj(odbc_database, 0);
03276    if (obj) {
03277       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc limit 1", odbc_table);
03278 
03279       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03280       if (!stmt) {
03281          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03282          ast_odbc_release_obj(obj);
03283          goto yuck;
03284       }
03285       res = SQLFetch(stmt);
03286       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03287          if (res == SQL_NO_DATA) {
03288             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03289          } else {
03290             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03291          }
03292 
03293          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03294          ast_odbc_release_obj(obj);
03295          goto yuck;
03296       }
03297       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03298       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03299          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03300          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03301          ast_odbc_release_obj(obj);
03302          goto yuck;
03303       }
03304       if (sscanf(rowdata, "%30d", &x) != 1)
03305          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03306       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03307       ast_odbc_release_obj(obj);
03308       return x;
03309    } else
03310       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03311 yuck:
03312    return x - 1;
03313 }
03314 
03315 /*!
03316  * \brief Determines if the specified message exists.
03317  * \param dir the folder the mailbox folder to look for messages. 
03318  * \param msgnum the message index to query for.
03319  *
03320  * This method is used when mailboxes are stored in an ODBC back end.
03321  *
03322  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03323  */
03324 static int message_exists(char *dir, int msgnum)
03325 {
03326    int x = 0;
03327    int res;
03328    SQLHSTMT stmt;
03329    char sql[PATH_MAX];
03330    char rowdata[20];
03331    char msgnums[20];
03332    char *argv[] = { dir, msgnums };
03333    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03334 
03335    struct odbc_obj *obj;
03336    obj = ast_odbc_request_obj(odbc_database, 0);
03337    if (obj) {
03338       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03339       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03340       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03341       if (!stmt) {
03342          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03343          ast_odbc_release_obj(obj);
03344          goto yuck;
03345       }
03346       res = SQLFetch(stmt);
03347       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03348          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03349          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03350          ast_odbc_release_obj(obj);
03351          goto yuck;
03352       }
03353       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03354       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03355          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03356          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03357          ast_odbc_release_obj(obj);
03358          goto yuck;
03359       }
03360       if (sscanf(rowdata, "%30d", &x) != 1)
03361          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03362       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03363       ast_odbc_release_obj(obj);
03364    } else
03365       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03366 yuck:
03367    return x;
03368 }
03369 
03370 /*!
03371  * \brief returns the one-based count for messages.
03372  * \param vmu
03373  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03374  *
03375  * This method is used when mailboxes are stored in an ODBC back end.
03376  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03377  * one-based messages.
03378  * This method just calls last_message_index and returns +1 of its value.
03379  *
03380  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03381  */
03382 static int count_messages(struct ast_vm_user *vmu, char *dir)
03383 {
03384    int x = 0;
03385    int res;
03386    SQLHSTMT stmt;
03387    char sql[PATH_MAX];
03388    char rowdata[20];
03389    char *argv[] = { dir };
03390    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03391 
03392    struct odbc_obj *obj;
03393    obj = ast_odbc_request_obj(odbc_database, 0);
03394    if (obj) {
03395       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03396       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03397       if (!stmt) {
03398          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03399          ast_odbc_release_obj(obj);
03400          goto yuck;
03401       }
03402       res = SQLFetch(stmt);
03403       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03404          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03405          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03406          ast_odbc_release_obj(obj);
03407          goto yuck;
03408       }
03409       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03410       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03411          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03412          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03413          ast_odbc_release_obj(obj);
03414          goto yuck;
03415       }
03416       if (sscanf(rowdata, "%30d", &x) != 1)
03417          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03418       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03419       ast_odbc_release_obj(obj);
03420       return x;
03421    } else
03422       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03423 yuck:
03424    return x - 1;
03425 
03426 }
03427 
03428 /*!
03429  * \brief Deletes a message from the mailbox folder.
03430  * \param sdir The mailbox folder to work in.
03431  * \param smsg The message index to be deleted.
03432  *
03433  * This method is used when mailboxes are stored in an ODBC back end.
03434  * The specified message is directly deleted from the database 'voicemessages' table.
03435  * 
03436  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03437  */
03438 static void delete_file(const char *sdir, int smsg)
03439 {
03440    SQLHSTMT stmt;
03441    char sql[PATH_MAX];
03442    char msgnums[20];
03443    char *argv[] = { NULL, msgnums };
03444    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03445    struct odbc_obj *obj;
03446 
03447    argv[0] = ast_strdupa(sdir);
03448 
03449    obj = ast_odbc_request_obj(odbc_database, 0);
03450    if (obj) {
03451       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03452       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03453       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03454       if (!stmt)
03455          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03456       else
03457          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03458       ast_odbc_release_obj(obj);
03459    } else
03460       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03461    return;  
03462 }
03463 
03464 /*!
03465  * \brief Copies a voicemail from one mailbox to another.
03466  * \param sdir the folder for which to look for the message to be copied.
03467  * \param smsg the index of the message to be copied.
03468  * \param ddir the destination folder to copy the message into.
03469  * \param dmsg the index to be used for the copied message.
03470  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03471  * \param dmailboxcontext The context for the destination user.
03472  *
03473  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03474  */
03475 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03476 {
03477    SQLHSTMT stmt;
03478    char sql[512];
03479    char msgnums[20];
03480    char msgnumd[20];
03481    struct odbc_obj *obj;
03482    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03483    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03484 
03485    delete_file(ddir, dmsg);
03486    obj = ast_odbc_request_obj(odbc_database, 0);
03487    if (obj) {
03488       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03489       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03490       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);
03491       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03492       if (!stmt)
03493          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03494       else
03495          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03496       ast_odbc_release_obj(obj);
03497    } else
03498       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03499    return;  
03500 }
03501 
03502 struct insert_data {
03503    char *sql;
03504    const char *dir;
03505    const char *msgnums;
03506    void *data;
03507    SQLLEN datalen;
03508    SQLLEN indlen;
03509    const char *context;
03510    const char *macrocontext;
03511    const char *callerid;
03512    const char *origtime;
03513    const char *duration;
03514    const char *mailboxuser;
03515    const char *mailboxcontext;
03516    const char *category;
03517    const char *flag;
03518 };
03519 
03520 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03521 {
03522    struct insert_data *data = vdata;
03523    int res;
03524    SQLHSTMT stmt;
03525 
03526    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03527    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03528       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03529       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03530       return NULL;
03531    }
03532 
03533    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03534    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03535    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03536    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03537    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03538    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03539    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03540    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03541    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03542    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03543    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03544    if (!ast_strlen_zero(data->category)) {
03545       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03546    }
03547    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03548    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03549       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03550       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03551       return NULL;
03552    }
03553 
03554    return stmt;
03555 }
03556 
03557 /*!
03558  * \brief Stores a voicemail into the database.
03559  * \param dir the folder the mailbox folder to store the message.
03560  * \param mailboxuser the user owning the mailbox folder.
03561  * \param mailboxcontext
03562  * \param msgnum the message index for the message to be stored.
03563  *
03564  * This method is used when mailboxes are stored in an ODBC back end.
03565  * The message sound file and information file is looked up on the file system. 
03566  * A SQL query is invoked to store the message into the (MySQL) database.
03567  *
03568  * \return the zero on success -1 on error.
03569  */
03570 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03571 {
03572    int res = 0;
03573    int fd = -1;
03574    void *fdm = MAP_FAILED;
03575    size_t fdlen = -1;
03576    SQLHSTMT stmt;
03577    char sql[PATH_MAX];
03578    char msgnums[20];
03579    char fn[PATH_MAX];
03580    char full_fn[PATH_MAX];
03581    char fmt[80]="";
03582    char *c;
03583    struct ast_config *cfg=NULL;
03584    struct odbc_obj *obj;
03585    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03586       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03587    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03588 
03589    delete_file(dir, msgnum);
03590    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03591       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03592       return -1;
03593    }
03594 
03595    do {
03596       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03597       c = strchr(fmt, '|');
03598       if (c)
03599          *c = '\0';
03600       if (!strcasecmp(fmt, "wav49"))
03601          strcpy(fmt, "WAV");
03602       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03603       if (msgnum > -1)
03604          make_file(fn, sizeof(fn), dir, msgnum);
03605       else
03606          ast_copy_string(fn, dir, sizeof(fn));
03607       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03608       cfg = ast_config_load(full_fn, config_flags);
03609       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03610       fd = open(full_fn, O_RDWR);
03611       if (fd < 0) {
03612          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03613          res = -1;
03614          break;
03615       }
03616       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03617          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03618             idata.context = "";
03619          }
03620          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03621             idata.macrocontext = "";
03622          }
03623          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03624             idata.callerid = "";
03625          }
03626          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03627             idata.origtime = "";
03628          }
03629          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03630             idata.duration = "";
03631          }
03632          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03633             idata.category = "";
03634          }
03635          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03636             idata.flag = "";
03637          }
03638       }
03639       fdlen = lseek(fd, 0, SEEK_END);
03640       lseek(fd, 0, SEEK_SET);
03641       printf("Length is %zd\n", fdlen);
03642       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03643       if (fdm == MAP_FAILED) {
03644          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03645          res = -1;
03646          break;
03647       } 
03648       idata.data = fdm;
03649       idata.datalen = idata.indlen = fdlen;
03650 
03651       if (!ast_strlen_zero(idata.category)) 
03652          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03653       else
03654          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03655 
03656       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03657          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03658       } else {
03659          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03660          res = -1;
03661       }
03662    } while (0);
03663    if (obj) {
03664       ast_odbc_release_obj(obj);
03665    }
03666    if (cfg)
03667       ast_config_destroy(cfg);
03668    if (fdm != MAP_FAILED)
03669       munmap(fdm, fdlen);
03670    if (fd > -1)
03671       close(fd);
03672    return res;
03673 }
03674 
03675 /*!
03676  * \brief Renames a message in a mailbox folder.
03677  * \param sdir The folder of the message to be renamed.
03678  * \param smsg The index of the message to be renamed.
03679  * \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.
03680  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03681  * \param ddir The destination folder for the message to be renamed into
03682  * \param dmsg The destination message for the message to be renamed.
03683  *
03684  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03685  * 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.
03686  * 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.
03687  */
03688 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03689 {
03690    SQLHSTMT stmt;
03691    char sql[PATH_MAX];
03692    char msgnums[20];
03693    char msgnumd[20];
03694    struct odbc_obj *obj;
03695    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03696    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03697 
03698    delete_file(ddir, dmsg);
03699    obj = ast_odbc_request_obj(odbc_database, 0);
03700    if (obj) {
03701       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03702       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03703       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03704       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03705       if (!stmt)
03706          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03707       else
03708          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03709       ast_odbc_release_obj(obj);
03710    } else
03711       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03712    return;  
03713 }
03714 
03715 /*!
03716  * \brief Removes a voicemail message file.
03717  * \param dir the path to the message file.
03718  * \param msgnum the unique number for the message within the mailbox.
03719  *
03720  * Removes the message content file and the information file.
03721  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03722  * Typical use is to clean up after a RETRIEVE operation. 
03723  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03724  * \return zero on success, -1 on error.
03725  */
03726 static int remove_file(char *dir, int msgnum)
03727 {
03728    char fn[PATH_MAX];
03729    char full_fn[PATH_MAX];
03730    char msgnums[80];
03731    
03732    if (msgnum > -1) {
03733       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03734       make_file(fn, sizeof(fn), dir, msgnum);
03735    } else
03736       ast_copy_string(fn, dir, sizeof(fn));
03737    ast_filedelete(fn, NULL);  
03738    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03739    unlink(full_fn);
03740    return 0;
03741 }
03742 #else
03743 #ifndef IMAP_STORAGE
03744 /*!
03745  * \brief Find all .txt files - even if they are not in sequence from 0000.
03746  * \param vmu
03747  * \param dir
03748  *
03749  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03750  *
03751  * \return the count of messages, zero or more.
03752  */
03753 static int count_messages(struct ast_vm_user *vmu, char *dir)
03754 {
03755 
03756    int vmcount = 0;
03757    DIR *vmdir = NULL;
03758    struct dirent *vment = NULL;
03759 
03760    if (vm_lock_path(dir))
03761       return ERROR_LOCK_PATH;
03762 
03763    if ((vmdir = opendir(dir))) {
03764       while ((vment = readdir(vmdir))) {
03765          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03766             vmcount++;
03767          }
03768       }
03769       closedir(vmdir);
03770    }
03771    ast_unlock_path(dir);
03772    
03773    return vmcount;
03774 }
03775 
03776 /*!
03777  * \brief Renames a message in a mailbox folder.
03778  * \param sfn The path to the mailbox information and data file to be renamed.
03779  * \param dfn The path for where the message data and information files will be renamed to.
03780  *
03781  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03782  */
03783 static void rename_file(char *sfn, char *dfn)
03784 {
03785    char stxt[PATH_MAX];
03786    char dtxt[PATH_MAX];
03787    ast_filerename(sfn,dfn,NULL);
03788    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03789    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03790    if (ast_check_realtime("voicemail_data")) {
03791       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03792    }
03793    rename(stxt, dtxt);
03794 }
03795 
03796 /*! 
03797  * \brief Determines the highest message number in use for a given user and mailbox folder.
03798  * \param vmu 
03799  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03800  *
03801  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03802  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03803  *
03804  * \note Should always be called with a lock already set on dir.
03805  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03806  */
03807 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03808 {
03809    int x;
03810    unsigned char map[MAXMSGLIMIT] = "";
03811    DIR *msgdir;
03812    struct dirent *msgdirent;
03813    int msgdirint;
03814    char extension[4];
03815    int stopcount = 0;
03816 
03817    /* Reading the entire directory into a file map scales better than
03818     * doing a stat repeatedly on a predicted sequence.  I suspect this
03819     * is partially due to stat(2) internally doing a readdir(2) itself to
03820     * find each file. */
03821    if (!(msgdir = opendir(dir))) {
03822       return -1;
03823    }
03824 
03825    while ((msgdirent = readdir(msgdir))) {
03826       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
03827          map[msgdirint] = 1;
03828          stopcount++;
03829          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
03830       }
03831    }
03832    closedir(msgdir);
03833 
03834    for (x = 0; x < vmu->maxmsg; x++) {
03835       if (map[x] == 1) {
03836          stopcount--;
03837       } else if (map[x] == 0 && !stopcount) {
03838          break;
03839       }
03840    }
03841 
03842    return x - 1;
03843 }
03844 
03845 #endif /* #ifndef IMAP_STORAGE */
03846 #endif /* #else of #ifdef ODBC_STORAGE */
03847 #ifndef IMAP_STORAGE
03848 /*!
03849  * \brief Utility function to copy a file.
03850  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03851  * \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.
03852  *
03853  * 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.
03854  * The copy operation copies up to 4096 bytes at once.
03855  *
03856  * \return zero on success, -1 on error.
03857  */
03858 static int copy(char *infile, char *outfile)
03859 {
03860    int ifd;
03861    int ofd;
03862    int res;
03863    int len;
03864    char buf[4096];
03865 
03866 #ifdef HARDLINK_WHEN_POSSIBLE
03867    /* Hard link if possible; saves disk space & is faster */
03868    if (link(infile, outfile)) {
03869 #endif
03870       if ((ifd = open(infile, O_RDONLY)) < 0) {
03871          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03872          return -1;
03873       }
03874       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03875          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03876          close(ifd);
03877          return -1;
03878       }
03879       do {
03880          len = read(ifd, buf, sizeof(buf));
03881          if (len < 0) {
03882             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03883             close(ifd);
03884             close(ofd);
03885             unlink(outfile);
03886          }
03887          if (len) {
03888             res = write(ofd, buf, len);
03889             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03890                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03891                close(ifd);
03892                close(ofd);
03893                unlink(outfile);
03894             }
03895          }
03896       } while (len);
03897       close(ifd);
03898       close(ofd);
03899       return 0;
03900 #ifdef HARDLINK_WHEN_POSSIBLE
03901    } else {
03902       /* Hard link succeeded */
03903       return 0;
03904    }
03905 #endif
03906 }
03907 
03908 /*!
03909  * \brief Copies a voicemail information (envelope) file.
03910  * \param frompath
03911  * \param topath 
03912  *
03913  * Every voicemail has the data (.wav) file, and the information file.
03914  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03915  * This is used by the COPY macro when not using IMAP storage.
03916  */
03917 static void copy_plain_file(char *frompath, char *topath)
03918 {
03919    char frompath2[PATH_MAX], topath2[PATH_MAX];
03920    struct ast_variable *tmp,*var = NULL;
03921    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03922    ast_filecopy(frompath, topath, NULL);
03923    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03924    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03925    if (ast_check_realtime("voicemail_data")) {
03926       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03927       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03928       for (tmp = var; tmp; tmp = tmp->next) {
03929          if (!strcasecmp(tmp->name, "origmailbox")) {
03930             origmailbox = tmp->value;
03931          } else if (!strcasecmp(tmp->name, "context")) {
03932             context = tmp->value;
03933          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03934             macrocontext = tmp->value;
03935          } else if (!strcasecmp(tmp->name, "exten")) {
03936             exten = tmp->value;
03937          } else if (!strcasecmp(tmp->name, "priority")) {
03938             priority = tmp->value;
03939          } else if (!strcasecmp(tmp->name, "callerchan")) {
03940             callerchan = tmp->value;
03941          } else if (!strcasecmp(tmp->name, "callerid")) {
03942             callerid = tmp->value;
03943          } else if (!strcasecmp(tmp->name, "origdate")) {
03944             origdate = tmp->value;
03945          } else if (!strcasecmp(tmp->name, "origtime")) {
03946             origtime = tmp->value;
03947          } else if (!strcasecmp(tmp->name, "category")) {
03948             category = tmp->value;
03949          } else if (!strcasecmp(tmp->name, "duration")) {
03950             duration = tmp->value;
03951          }
03952       }
03953       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);
03954    }
03955    copy(frompath2, topath2);
03956    ast_variables_destroy(var);
03957 }
03958 #endif
03959 
03960 /*! 
03961  * \brief Removes the voicemail sound and information file.
03962  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03963  *
03964  * This is used by the DELETE macro when voicemails are stored on the file system.
03965  *
03966  * \return zero on success, -1 on error.
03967  */
03968 static int vm_delete(char *file)
03969 {
03970    char *txt;
03971    int txtsize = 0;
03972 
03973    txtsize = (strlen(file) + 5)*sizeof(char);
03974    txt = alloca(txtsize);
03975    /* Sprintf here would safe because we alloca'd exactly the right length,
03976     * but trying to eliminate all sprintf's anyhow
03977     */
03978    if (ast_check_realtime("voicemail_data")) {
03979       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03980    }
03981    snprintf(txt, txtsize, "%s.txt", file);
03982    unlink(txt);
03983    return ast_filedelete(file, NULL);
03984 }
03985 
03986 /*!
03987  * \brief utility used by inchar(), for base_encode()
03988  */
03989 static int inbuf(struct baseio *bio, FILE *fi)
03990 {
03991    int l;
03992 
03993    if (bio->ateof)
03994       return 0;
03995 
03996    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03997       if (ferror(fi))
03998          return -1;
03999 
04000       bio->ateof = 1;
04001       return 0;
04002    }
04003 
04004    bio->iolen= l;
04005    bio->iocp= 0;
04006 
04007    return 1;
04008 }
04009 
04010 /*!
04011  * \brief utility used by base_encode()
04012  */
04013 static int inchar(struct baseio *bio, FILE *fi)
04014 {
04015    if (bio->iocp>=bio->iolen) {
04016       if (!inbuf(bio, fi))
04017          return EOF;
04018    }
04019 
04020    return bio->iobuf[bio->iocp++];
04021 }
04022 
04023 /*!
04024  * \brief utility used by base_encode()
04025  */
04026 static int ochar(struct baseio *bio, int c, FILE *so)
04027 {
04028    if (bio->linelength >= BASELINELEN) {
04029       if (fputs(ENDL, so) == EOF) {
04030          return -1;
04031       }
04032 
04033       bio->linelength= 0;
04034    }
04035 
04036    if (putc(((unsigned char) c), so) == EOF) {
04037       return -1;
04038    }
04039 
04040    bio->linelength++;
04041 
04042    return 1;
04043 }
04044 
04045 /*!
04046  * \brief Performs a base 64 encode algorithm on the contents of a File
04047  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04048  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04049  *
04050  * 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 ?
04051  *
04052  * \return zero on success, -1 on error.
04053  */
04054 static int base_encode(char *filename, FILE *so)
04055 {
04056    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04057       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04058       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04059       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04060    int i,hiteof= 0;
04061    FILE *fi;
04062    struct baseio bio;
04063 
04064    memset(&bio, 0, sizeof(bio));
04065    bio.iocp = BASEMAXINLINE;
04066 
04067    if (!(fi = fopen(filename, "rb"))) {
04068       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04069       return -1;
04070    }
04071 
04072    while (!hiteof){
04073       unsigned char igroup[3], ogroup[4];
04074       int c,n;
04075 
04076       igroup[0]= igroup[1]= igroup[2]= 0;
04077 
04078       for (n= 0;n<3;n++) {
04079          if ((c = inchar(&bio, fi)) == EOF) {
04080             hiteof= 1;
04081             break;
04082          }
04083 
04084          igroup[n]= (unsigned char)c;
04085       }
04086 
04087       if (n> 0) {
04088          ogroup[0]= dtable[igroup[0]>>2];
04089          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
04090          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
04091          ogroup[3]= dtable[igroup[2]&0x3F];
04092 
04093          if (n<3) {
04094             ogroup[3]= '=';
04095 
04096             if (n<2)
04097                ogroup[2]= '=';
04098          }
04099 
04100          for (i= 0;i<4;i++)
04101             ochar(&bio, ogroup[i], so);
04102       }
04103    }
04104 
04105    fclose(fi);
04106 
04107    if (fputs(ENDL, so) == EOF) {
04108       return 0;
04109    }
04110 
04111    return 1;
04112 }
04113 
04114 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, char *passdata, size_t passdatasize, const char *category, const char *flag)
04115 {
04116    char callerid[256];
04117    char fromdir[256], fromfile[256];
04118    struct ast_config *msg_cfg;
04119    const char *origcallerid, *origtime;
04120    char origcidname[80], origcidnum[80], origdate[80];
04121    int inttime;
04122    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04123 
04124    /* Prepare variables for substitution in email body and subject */
04125    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04126    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04127    snprintf(passdata, passdatasize, "%d", msgnum);
04128    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
04129    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04130    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04131    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04132       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04133    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04134    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04135    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04136    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04137    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04138 
04139    /* Retrieve info from VM attribute file */
04140    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04141    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04142    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04143       strcat(fromfile, ".txt");
04144    }
04145    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04146       if (option_debug > 0) {
04147          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04148       }
04149       return;
04150    }
04151 
04152    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04153       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04154       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04155       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04156       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04157    }
04158 
04159    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04160       struct timeval tv = { inttime, };
04161       struct ast_tm tm;
04162       ast_localtime(&tv, &tm, NULL);
04163       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04164       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04165    }
04166    ast_config_destroy(msg_cfg);
04167 }
04168 
04169 /*!
04170  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04171  * \param from The string to work with.
04172  * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
04173  * 
04174  * \return The destination string with quotes wrapped on it (the to field).
04175  */
04176 static char *quote(const char *from, char *to, size_t len)
04177 {
04178    char *ptr = to;
04179    *ptr++ = '"';
04180    for (; ptr < to + len - 1; from++) {
04181       if (*from == '"')
04182          *ptr++ = '\\';
04183       else if (*from == '\0')
04184          break;
04185       *ptr++ = *from;
04186    }
04187    if (ptr < to + len - 1)
04188       *ptr++ = '"';
04189    *ptr = '\0';
04190    return to;
04191 }
04192 
04193 /*! \brief
04194  * fill in *tm for current time according to the proper timezone, if any.
04195  * Return tm so it can be used as a function argument.
04196  */
04197 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04198 {
04199    const struct vm_zone *z = NULL;
04200    struct timeval t = ast_tvnow();
04201 
04202    /* Does this user have a timezone specified? */
04203    if (!ast_strlen_zero(vmu->zonetag)) {
04204       /* Find the zone in the list */
04205       AST_LIST_LOCK(&zones);
04206       AST_LIST_TRAVERSE(&zones, z, list) {
04207          if (!strcmp(z->name, vmu->zonetag))
04208             break;
04209       }
04210       AST_LIST_UNLOCK(&zones);
04211    }
04212    ast_localtime(&t, tm, z ? z->timezone : NULL);
04213    return tm;
04214 }
04215 
04216 /*!\brief Check if the string would need encoding within the MIME standard, to
04217  * avoid confusing certain mail software that expects messages to be 7-bit
04218  * clean.
04219  */
04220 static int check_mime(const char *str)
04221 {
04222    for (; *str; str++) {
04223       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04224          return 1;
04225       }
04226    }
04227    return 0;
04228 }
04229 
04230 /*!\brief Encode a string according to the MIME rules for encoding strings
04231  * that are not 7-bit clean or contain control characters.
04232  *
04233  * Additionally, if the encoded string would exceed the MIME limit of 76
04234  * characters per line, then the encoding will be broken up into multiple
04235  * sections, separated by a space character, in order to facilitate
04236  * breaking up the associated header across multiple lines.
04237  *
04238  * \param start A string to be encoded
04239  * \param end An expandable buffer for holding the result
04240  * \param preamble The length of the first line already used for this string,
04241  * to ensure that each line maintains a maximum length of 76 chars.
04242  * \param postamble the length of any additional characters appended to the
04243  * line, used to ensure proper field wrapping.
04244  * \retval The encoded string.
04245  */
04246 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
04247 {
04248    char tmp[80];
04249    int first_section = 1;
04250    size_t endlen = 0, tmplen = 0;
04251    *end = '\0';
04252 
04253    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04254    for (; *start; start++) {
04255       int need_encoding = 0;
04256       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04257          need_encoding = 1;
04258       }
04259       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04260          (first_section && !need_encoding && preamble + tmplen > 72) ||
04261          (!first_section && need_encoding && tmplen > 70) ||
04262          (!first_section && !need_encoding && tmplen > 72)) {
04263          /* Start new line */
04264          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04265          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04266          first_section = 0;
04267       }
04268       if (need_encoding && *start == ' ') {
04269          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04270       } else if (need_encoding) {
04271          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04272       } else {
04273          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04274       }
04275    }
04276    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04277    return end;
04278 }
04279 
04280 /*!
04281  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04282  * \param p The output file to generate the email contents into.
04283  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04284  * \param vmu The voicemail user who is sending the voicemail.
04285  * \param msgnum The message index in the mailbox folder.
04286  * \param context 
04287  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04288  * \param cidnum The caller ID number.
04289  * \param cidname The caller ID name.
04290  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04291  * \param format The message sound file format. i.e. .wav
04292  * \param duration The time of the message content, in seconds.
04293  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04294  * \param chan
04295  * \param category
04296  * \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.
04297  *
04298  * 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.
04299  */
04300 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)
04301 {
04302    char date[256];
04303    char host[MAXHOSTNAMELEN] = "";
04304    char who[256];
04305    char bound[256];
04306    char dur[256];
04307    struct ast_tm tm;
04308    char enc_cidnum[256] = "", enc_cidname[256] = "";
04309    char *passdata = NULL, *passdata2;
04310    size_t len_passdata = 0, len_passdata2, tmplen;
04311    char *greeting_attachment; 
04312    char filename[256];
04313 
04314 
04315    /* One alloca for multiple fields */
04316    len_passdata2 = strlen(vmu->fullname);
04317    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04318       len_passdata2 = tmplen;
04319    }
04320    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04321       len_passdata2 = tmplen;
04322    }
04323    len_passdata2 = len_passdata2 * 3 + 200;
04324    passdata2 = alloca(len_passdata2);
04325 
04326    if (cidnum) {
04327       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04328    }
04329    if (cidname) {
04330       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04331    }
04332    gethostname(host, sizeof(host) - 1);
04333 
04334    if (strchr(srcemail, '@'))
04335       ast_copy_string(who, srcemail, sizeof(who));
04336    else 
04337       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04338    
04339    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04340    if (greeting_attachment)
04341       *greeting_attachment++ = '\0';
04342 
04343    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04344    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04345    fprintf(p, "Date: %s" ENDL, date);
04346 
04347    /* Set date format for voicemail mail */
04348    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04349 
04350    if (!ast_strlen_zero(fromstring)) {
04351       struct ast_channel *ast;
04352       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04353          char *ptr;
04354          memset(passdata2, 0, len_passdata2);
04355          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04356          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04357          len_passdata = strlen(passdata2) * 3 + 300;
04358          passdata = alloca(len_passdata);
04359          if (check_mime(passdata2)) {
04360             int first_line = 1;
04361             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04362             while ((ptr = strchr(passdata, ' '))) {
04363                *ptr = '\0';
04364                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04365                first_line = 0;
04366                passdata = ptr + 1;
04367             }
04368             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04369          } else {
04370             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04371          }
04372          ast_channel_free(ast);
04373       } else {
04374          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04375       }
04376    } else {
04377       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04378    }
04379 
04380    if (check_mime(vmu->fullname)) {
04381       int first_line = 1;
04382       char *ptr;
04383       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04384       while ((ptr = strchr(passdata2, ' '))) {
04385          *ptr = '\0';
04386          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04387          first_line = 0;
04388          passdata2 = ptr + 1;
04389       }
04390       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04391    } else {
04392       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04393    }
04394    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04395       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04396       struct ast_channel *ast;
04397       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04398          int vmlen = strlen(e_subj) * 3 + 200;
04399          /* Only allocate more space if the previous was not large enough */
04400          if (vmlen > len_passdata) {
04401             passdata = alloca(vmlen);
04402             len_passdata = vmlen;
04403          }
04404 
04405          memset(passdata, 0, len_passdata);
04406          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04407          pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
04408          if (check_mime(passdata)) {
04409             int first_line = 1;
04410             char *ptr;
04411             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04412             while ((ptr = strchr(passdata2, ' '))) {
04413                *ptr = '\0';
04414                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04415                first_line = 0;
04416                passdata2 = ptr + 1;
04417             }
04418             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04419          } else {
04420             fprintf(p, "Subject: %s" ENDL, passdata);
04421          }
04422          ast_channel_free(ast);
04423       } else {
04424          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04425       }
04426    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04427       if (ast_strlen_zero(flag)) {
04428          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04429       } else {
04430          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04431       }
04432    } else {
04433       if (ast_strlen_zero(flag)) {
04434          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04435       } else {
04436          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04437       }
04438    }
04439 
04440    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04441    if (imap) {
04442       /* additional information needed for IMAP searching */
04443       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04444       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04445       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04446       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04447 #ifdef IMAP_STORAGE
04448       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04449 #else
04450       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04451 #endif
04452       /* flag added for Urgent */
04453       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04454       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04455       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04456       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04457       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04458       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04459       if (!ast_strlen_zero(category)) {
04460          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04461       } else {
04462          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04463       }
04464       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04465       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04466       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04467    }
04468    if (!ast_strlen_zero(cidnum)) {
04469       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04470    }
04471    if (!ast_strlen_zero(cidname)) {
04472       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04473    }
04474    fprintf(p, "MIME-Version: 1.0" ENDL);
04475    if (attach_user_voicemail) {
04476       /* Something unique. */
04477       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04478 
04479       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04480       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04481       fprintf(p, "--%s" ENDL, bound);
04482    }
04483    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04484    if (emailbody || vmu->emailbody) {
04485       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04486       struct ast_channel *ast;
04487       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04488          char *passdata;
04489          int vmlen = strlen(e_body) * 3 + 200;
04490          passdata = alloca(vmlen);
04491          memset(passdata, 0, vmlen);
04492          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04493          pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
04494 #ifdef IMAP_STORAGE
04495          {
04496             /* Convert body to native line terminators for IMAP backend */
04497             char *line = passdata, *next;
04498             do {
04499                /* Terminate line before outputting it to the file */
04500                if ((next = strchr(line, '\n'))) {
04501                   *next++ = '\0';
04502                }
04503                fprintf(p, "%s" ENDL, line);
04504                line = next;
04505             } while (!ast_strlen_zero(line));
04506          }
04507 #else
04508          fprintf(p, "%s" ENDL, passdata);
04509 #endif
04510          ast_channel_free(ast);
04511       } else
04512          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04513    } else if (msgnum > -1) {
04514       if (strcmp(vmu->mailbox, mailbox)) {
04515          /* Forwarded type */
04516          struct ast_config *msg_cfg;
04517          const char *v;
04518          int inttime;
04519          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04520          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04521          /* Retrieve info from VM attribute file */
04522          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04523          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04524          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04525             strcat(fromfile, ".txt");
04526          }
04527          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04528             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04529                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04530             }
04531 
04532             /* You might be tempted to do origdate, except that a) it's in the wrong
04533              * format, and b) it's missing for IMAP recordings. */
04534             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04535                struct timeval tv = { inttime, };
04536                struct ast_tm tm;
04537                ast_localtime(&tv, &tm, NULL);
04538                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04539             }
04540             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04541                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04542                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04543                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04544                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04545                date, origcallerid, origdate);
04546             ast_config_destroy(msg_cfg);
04547          } else {
04548             goto plain_message;
04549          }
04550       } else {
04551 plain_message:
04552          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04553             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04554             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04555             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04556             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04557       }
04558    } else {
04559       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04560             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04561    }
04562 
04563    if (imap || attach_user_voicemail) {
04564       if (!ast_strlen_zero(attach2)) {
04565          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04566          ast_debug(5, "creating second attachment filename %s\n", filename);
04567          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04568          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04569          ast_debug(5, "creating attachment filename %s\n", filename);
04570          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04571       } else {
04572          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04573          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04574          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04575       }
04576    }
04577 }
04578 
04579 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)
04580 {
04581    char tmpdir[256], newtmp[256];
04582    char fname[256];
04583    char tmpcmd[256];
04584    int tmpfd = -1;
04585    int soxstatus = 0;
04586 
04587    /* Eww. We want formats to tell us their own MIME type */
04588    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04589 
04590    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04591       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04592       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04593       tmpfd = mkstemp(newtmp);
04594       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04595       ast_debug(3, "newtmp: %s\n", newtmp);
04596       if (tmpfd > -1) {
04597          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04598          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04599             attach = newtmp;
04600             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04601          } else {
04602             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04603                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04604             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04605          }
04606       }
04607    }
04608    fprintf(p, "--%s" ENDL, bound);
04609    if (msgnum > -1)
04610       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04611    else
04612       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04613    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04614    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04615    if (msgnum > -1)
04616       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04617    else
04618       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04619    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04620    base_encode(fname, p);
04621    if (last)
04622       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04623    if (tmpfd > -1) {
04624       if (soxstatus == 0) {
04625          unlink(fname);
04626       }
04627       close(tmpfd);
04628       unlink(newtmp);
04629    }
04630    return 0;
04631 }
04632 #undef ENDL
04633 
04634 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)
04635 {
04636    FILE *p=NULL;
04637    char tmp[80] = "/tmp/astmail-XXXXXX";
04638    char tmp2[256];
04639    char *stringp;
04640 
04641    if (vmu && ast_strlen_zero(vmu->email)) {
04642       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04643       return(0);
04644    }
04645 
04646    /* Mail only the first format */
04647    format = ast_strdupa(format);
04648    stringp = format;
04649    strsep(&stringp, "|");
04650 
04651    if (!strcmp(format, "wav49"))
04652       format = "WAV";
04653    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));
04654    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04655       command hangs */
04656    if ((p = vm_mkftemp(tmp)) == NULL) {
04657       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04658       return -1;
04659    } else {
04660       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04661       fclose(p);
04662       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04663       ast_safe_system(tmp2);
04664       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04665    }
04666    return 0;
04667 }
04668 
04669 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)
04670 {
04671    char date[256];
04672    char host[MAXHOSTNAMELEN] = "";
04673    char who[256];
04674    char dur[PATH_MAX];
04675    char tmp[80] = "/tmp/astmail-XXXXXX";
04676    char tmp2[PATH_MAX];
04677    struct ast_tm tm;
04678    FILE *p;
04679 
04680    if ((p = vm_mkftemp(tmp)) == NULL) {
04681       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04682       return -1;
04683    }
04684    gethostname(host, sizeof(host)-1);
04685    if (strchr(srcemail, '@'))
04686       ast_copy_string(who, srcemail, sizeof(who));
04687    else 
04688       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04689    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04690    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04691    fprintf(p, "Date: %s\n", date);
04692 
04693    if (*pagerfromstring) {
04694       struct ast_channel *ast;
04695       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04696          char *passdata;
04697          int vmlen = strlen(fromstring)*3 + 200;
04698          passdata = alloca(vmlen);
04699          memset(passdata, 0, vmlen);
04700          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04701          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04702          fprintf(p, "From: %s <%s>\n", passdata, who);
04703          ast_channel_free(ast);
04704       } else 
04705          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04706    } else
04707       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04708    fprintf(p, "To: %s\n", pager);
04709    if (pagersubject) {
04710       struct ast_channel *ast;
04711       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04712          char *passdata;
04713          int vmlen = strlen(pagersubject) * 3 + 200;
04714          passdata = alloca(vmlen);
04715          memset(passdata, 0, vmlen);
04716          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04717          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04718          fprintf(p, "Subject: %s\n\n", passdata);
04719          ast_channel_free(ast);
04720       } else
04721          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04722    } else {
04723       if (ast_strlen_zero(flag)) {
04724          fprintf(p, "Subject: New VM\n\n");
04725       } else {
04726          fprintf(p, "Subject: New %s VM\n\n", flag);
04727       }
04728    }
04729 
04730    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04731    if (pagerbody) {
04732       struct ast_channel *ast;
04733       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04734          char *passdata;
04735          int vmlen = strlen(pagerbody) * 3 + 200;
04736          passdata = alloca(vmlen);
04737          memset(passdata, 0, vmlen);
04738          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04739          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04740          fprintf(p, "%s\n", passdata);
04741          ast_channel_free(ast);
04742       } else
04743          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04744    } else {
04745       fprintf(p, "New %s long %s msg in box %s\n"
04746             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04747    }
04748    fclose(p);
04749    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04750    ast_safe_system(tmp2);
04751    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04752    return 0;
04753 }
04754 
04755 /*!
04756  * \brief Gets the current date and time, as formatted string.
04757  * \param s The buffer to hold the output formatted date.
04758  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04759  * 
04760  * The date format string used is "%a %b %e %r UTC %Y".
04761  * 
04762  * \return zero on success, -1 on error.
04763  */
04764 static int get_date(char *s, int len)
04765 {
04766    struct ast_tm tm;
04767    struct timeval t = ast_tvnow();
04768    
04769    ast_localtime(&t, &tm, "UTC");
04770 
04771    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04772 }
04773 
04774 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04775 {
04776    int res;
04777    char fn[PATH_MAX];
04778    char dest[PATH_MAX];
04779 
04780    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04781 
04782    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04783       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04784       return -1;
04785    }
04786 
04787    RETRIEVE(fn, -1, ext, context);
04788    if (ast_fileexists(fn, NULL, NULL) > 0) {
04789       res = ast_stream_and_wait(chan, fn, ecodes);
04790       if (res) {
04791          DISPOSE(fn, -1);
04792          return res;
04793       }
04794    } else {
04795       /* Dispose just in case */
04796       DISPOSE(fn, -1);
04797       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04798       if (res)
04799          return res;
04800       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04801       if (res)
04802          return res;
04803    }
04804    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04805    return res;
04806 }
04807 
04808 static void free_zone(struct vm_zone *z)
04809 {
04810    ast_free(z);
04811 }
04812 
04813 #ifdef ODBC_STORAGE
04814 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04815 {
04816    int x = -1;
04817    int res;
04818    SQLHSTMT stmt = NULL;
04819    char sql[PATH_MAX];
04820    char rowdata[20];
04821    char tmp[PATH_MAX] = "";
04822    struct odbc_obj *obj = NULL;
04823    char *context;
04824    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04825 
04826    if (newmsgs)
04827       *newmsgs = 0;
04828    if (oldmsgs)
04829       *oldmsgs = 0;
04830    if (urgentmsgs)
04831       *urgentmsgs = 0;
04832 
04833    /* If no mailbox, return immediately */
04834    if (ast_strlen_zero(mailbox))
04835       return 0;
04836 
04837    ast_copy_string(tmp, mailbox, sizeof(tmp));
04838 
04839    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04840       int u, n, o;
04841       char *next, *remaining = tmp;
04842       while ((next = strsep(&remaining, " ,"))) {
04843          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04844             return -1;
04845          }
04846          if (urgentmsgs) {
04847             *urgentmsgs += u;
04848          }
04849          if (newmsgs) {
04850             *newmsgs += n;
04851          }
04852          if (oldmsgs) {
04853             *oldmsgs += o;
04854          }
04855       }
04856       return 0;
04857    }
04858 
04859    context = strchr(tmp, '@');
04860    if (context) {
04861       *context = '\0';
04862       context++;
04863    } else
04864       context = "default";
04865 
04866    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04867       do {
04868          if (newmsgs) {
04869             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04870             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04871                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04872                break;
04873             }
04874             res = SQLFetch(stmt);
04875             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04876                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04877                break;
04878             }
04879             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04880             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04881                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04882                break;
04883             }
04884             *newmsgs = atoi(rowdata);
04885             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04886          }
04887 
04888          if (oldmsgs) {
04889             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04890             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04891                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04892                break;
04893             }
04894             res = SQLFetch(stmt);
04895             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04896                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04897                break;
04898             }
04899             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04900             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04901                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04902                break;
04903             }
04904             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04905             *oldmsgs = atoi(rowdata);
04906          }
04907 
04908          if (urgentmsgs) {
04909             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04910             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04911                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04912                break;
04913             }
04914             res = SQLFetch(stmt);
04915             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04916                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04917                break;
04918             }
04919             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04920             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04921                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04922                break;
04923             }
04924             *urgentmsgs = atoi(rowdata);
04925          }
04926 
04927          x = 0;
04928       } while (0);
04929    } else {
04930       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04931    }
04932 
04933    if (stmt) {
04934       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04935    }
04936    if (obj) {
04937       ast_odbc_release_obj(obj);
04938    }
04939 
04940    return x;
04941 }
04942 
04943 /*!
04944  * \brief Gets the number of messages that exist in a mailbox folder.
04945  * \param context
04946  * \param mailbox
04947  * \param folder
04948  * 
04949  * This method is used when ODBC backend is used.
04950  * \return The number of messages in this mailbox folder (zero or more).
04951  */
04952 static int messagecount(const char *context, const char *mailbox, const char *folder)
04953 {
04954    struct odbc_obj *obj = NULL;
04955    int nummsgs = 0;
04956    int res;
04957    SQLHSTMT stmt = NULL;
04958    char sql[PATH_MAX];
04959    char rowdata[20];
04960    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04961    if (!folder)
04962       folder = "INBOX";
04963    /* If no mailbox, return immediately */
04964    if (ast_strlen_zero(mailbox))
04965       return 0;
04966 
04967    obj = ast_odbc_request_obj(odbc_database, 0);
04968    if (obj) {
04969       if (!strcmp(folder, "INBOX")) {
04970          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);
04971       } else {
04972          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04973       }
04974       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04975       if (!stmt) {
04976          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04977          goto yuck;
04978       }
04979       res = SQLFetch(stmt);
04980       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04981          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04982          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04983          goto yuck;
04984       }
04985       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04986       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04987          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04988          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04989          goto yuck;
04990       }
04991       nummsgs = atoi(rowdata);
04992       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04993    } else
04994       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04995 
04996 yuck:
04997    if (obj)
04998       ast_odbc_release_obj(obj);
04999    return nummsgs;
05000 }
05001 
05002 /** 
05003  * \brief Determines if the given folder has messages.
05004  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05005  * 
05006  * This function is used when the mailbox is stored in an ODBC back end.
05007  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05008  * \return 1 if the folder has one or more messages. zero otherwise.
05009  */
05010 static int has_voicemail(const char *mailbox, const char *folder)
05011 {
05012    char tmp[256], *tmp2 = tmp, *box, *context;
05013    ast_copy_string(tmp, mailbox, sizeof(tmp));
05014    while ((context = box = strsep(&tmp2, ",&"))) {
05015       strsep(&context, "@");
05016       if (ast_strlen_zero(context))
05017          context = "default";
05018       if (messagecount(context, box, folder))
05019          return 1;
05020    }
05021    return 0;
05022 }
05023 #endif
05024 #ifndef IMAP_STORAGE
05025 /*! 
05026  * \brief Copies a message from one mailbox to another.
05027  * \param chan
05028  * \param vmu
05029  * \param imbox
05030  * \param msgnum
05031  * \param duration
05032  * \param recip
05033  * \param fmt
05034  * \param dir
05035  *
05036  * This is only used by file storage based mailboxes.
05037  *
05038  * \return zero on success, -1 on error.
05039  */
05040 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)
05041 {
05042    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05043    const char *frombox = mbox(imbox);
05044    int recipmsgnum;
05045    int res = 0;
05046 
05047    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05048 
05049    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05050       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05051    } else {
05052       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05053    }
05054    
05055    if (!dir)
05056       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05057    else
05058       ast_copy_string(fromdir, dir, sizeof(fromdir));
05059 
05060    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05061    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05062 
05063    if (vm_lock_path(todir))
05064       return ERROR_LOCK_PATH;
05065 
05066    recipmsgnum = last_message_index(recip, todir) + 1;
05067    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05068       make_file(topath, sizeof(topath), todir, recipmsgnum);
05069 #ifndef ODBC_STORAGE
05070       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05071          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05072       } else {
05073 #endif
05074          /* If we are prepending a message for ODBC, then the message already
05075           * exists in the database, but we want to force copying from the
05076           * filesystem (since only the FS contains the prepend). */
05077          copy_plain_file(frompath, topath);
05078          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05079          vm_delete(topath);
05080 #ifndef ODBC_STORAGE
05081       }
05082 #endif
05083    } else {
05084       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05085       res = -1;
05086    }
05087    ast_unlock_path(todir);
05088    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05089    
05090    return res;
05091 }
05092 #endif
05093 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05094 
05095 static int messagecount(const char *context, const char *mailbox, const char *folder)
05096 {
05097    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05098 }
05099 
05100 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05101 {
05102    DIR *dir;
05103    struct dirent *de;
05104    char fn[256];
05105    int ret = 0;
05106 
05107    /* If no mailbox, return immediately */
05108    if (ast_strlen_zero(mailbox))
05109       return 0;
05110 
05111    if (ast_strlen_zero(folder))
05112       folder = "INBOX";
05113    if (ast_strlen_zero(context))
05114       context = "default";
05115 
05116    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05117 
05118    if (!(dir = opendir(fn)))
05119       return 0;
05120 
05121    while ((de = readdir(dir))) {
05122       if (!strncasecmp(de->d_name, "msg", 3)) {
05123          if (shortcircuit) {
05124             ret = 1;
05125             break;
05126          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05127             ret++;
05128          }
05129       }
05130    }
05131 
05132    closedir(dir);
05133 
05134    return ret;
05135 }
05136 
05137 /** 
05138  * \brief Determines if the given folder has messages.
05139  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05140  * \param folder the folder to look in
05141  *
05142  * This function is used when the mailbox is stored in a filesystem back end.
05143  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05144  * \return 1 if the folder has one or more messages. zero otherwise.
05145  */
05146 static int has_voicemail(const char *mailbox, const char *folder)
05147 {
05148    char tmp[256], *tmp2 = tmp, *box, *context;
05149    ast_copy_string(tmp, mailbox, sizeof(tmp));
05150    if (ast_strlen_zero(folder)) {
05151       folder = "INBOX";
05152    }
05153    while ((box = strsep(&tmp2, ",&"))) {
05154       if ((context = strchr(box, '@')))
05155          *context++ = '\0';
05156       else
05157          context = "default";
05158       if (__has_voicemail(context, box, folder, 1))
05159          return 1;
05160       /* If we are checking INBOX, we should check Urgent as well */
05161       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05162          return 1;
05163       }
05164    }
05165    return 0;
05166 }
05167 
05168 
05169 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05170 {
05171    char tmp[256];
05172    char *context;
05173 
05174    /* If no mailbox, return immediately */
05175    if (ast_strlen_zero(mailbox))
05176       return 0;
05177 
05178    if (newmsgs)
05179       *newmsgs = 0;
05180    if (oldmsgs)
05181       *oldmsgs = 0;
05182    if (urgentmsgs)
05183       *urgentmsgs = 0;
05184 
05185    if (strchr(mailbox, ',')) {
05186       int tmpnew, tmpold, tmpurgent;
05187       char *mb, *cur;
05188 
05189       ast_copy_string(tmp, mailbox, sizeof(tmp));
05190       mb = tmp;
05191       while ((cur = strsep(&mb, ", "))) {
05192          if (!ast_strlen_zero(cur)) {
05193             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05194                return -1;
05195             else {
05196                if (newmsgs)
05197                   *newmsgs += tmpnew; 
05198                if (oldmsgs)
05199                   *oldmsgs += tmpold;
05200                if (urgentmsgs)
05201                   *urgentmsgs += tmpurgent;
05202             }
05203          }
05204       }
05205       return 0;
05206    }
05207 
05208    ast_copy_string(tmp, mailbox, sizeof(tmp));
05209    
05210    if ((context = strchr(tmp, '@')))
05211       *context++ = '\0';
05212    else
05213       context = "default";
05214 
05215    if (newmsgs)
05216       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05217    if (oldmsgs)
05218       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05219    if (urgentmsgs)
05220       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05221 
05222    return 0;
05223 }
05224 
05225 #endif
05226 
05227 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05228 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05229 {
05230    int urgentmsgs = 0;
05231    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05232    if (newmsgs) {
05233       *newmsgs += urgentmsgs;
05234    }
05235    return res;
05236 }
05237 
05238 static void run_externnotify(char *context, char *extension, const char *flag)
05239 {
05240    char arguments[255];
05241    char ext_context[256] = "";
05242    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05243    struct ast_smdi_mwi_message *mwi_msg;
05244 
05245    if (!ast_strlen_zero(context))
05246       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05247    else
05248       ast_copy_string(ext_context, extension, sizeof(ext_context));
05249 
05250    if (smdi_iface) {
05251       if (ast_app_has_voicemail(ext_context, NULL)) 
05252          ast_smdi_mwi_set(smdi_iface, extension);
05253       else
05254          ast_smdi_mwi_unset(smdi_iface, extension);
05255 
05256       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05257          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05258          if (!strncmp(mwi_msg->cause, "INV", 3))
05259             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05260          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05261             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05262          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05263          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05264       } else {
05265          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05266       }
05267    }
05268 
05269    if (!ast_strlen_zero(externnotify)) {
05270       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05271          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05272       } else {
05273          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05274          ast_debug(1, "Executing %s\n", arguments);
05275          ast_safe_system(arguments);
05276       }
05277    }
05278 }
05279 
05280 /*!
05281  * \brief Variables used for saving a voicemail.
05282  *
05283  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05284  */
05285 struct leave_vm_options {
05286    unsigned int flags;
05287    signed char record_gain;
05288    char *exitcontext;
05289 };
05290 
05291 /*!
05292  * \brief Prompts the user and records a voicemail to a mailbox.
05293  * \param chan
05294  * \param ext
05295  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05296  * 
05297  * 
05298  * 
05299  * \return zero on success, -1 on error.
05300  */
05301 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05302 {
05303 #ifdef IMAP_STORAGE
05304    int newmsgs, oldmsgs;
05305 #else
05306    char urgdir[PATH_MAX];
05307 #endif
05308    char txtfile[PATH_MAX];
05309    char tmptxtfile[PATH_MAX];
05310    struct vm_state *vms = NULL;
05311    char callerid[256];
05312    FILE *txt;
05313    char date[256];
05314    int txtdes;
05315    int res = 0;
05316    int msgnum;
05317    int duration = 0;
05318    int ausemacro = 0;
05319    int ousemacro = 0;
05320    int ouseexten = 0;
05321    char tmpdur[16];
05322    char priority[16];
05323    char origtime[16];
05324    char dir[PATH_MAX];
05325    char tmpdir[PATH_MAX];
05326    char fn[PATH_MAX];
05327    char prefile[PATH_MAX] = "";
05328    char tempfile[PATH_MAX] = "";
05329    char ext_context[256] = "";
05330    char fmt[80];
05331    char *context;
05332    char ecodes[17] = "#";
05333    struct ast_str *tmp = ast_str_create(16);
05334    char *tmpptr;
05335    struct ast_vm_user *vmu;
05336    struct ast_vm_user svm;
05337    const char *category = NULL;
05338    const char *code;
05339    const char *alldtmf = "0123456789ABCD*#";
05340    char flag[80];
05341 
05342    ast_str_set(&tmp, 0, "%s", ext);
05343    ext = ast_str_buffer(tmp);
05344    if ((context = strchr(ext, '@'))) {
05345       *context++ = '\0';
05346       tmpptr = strchr(context, '&');
05347    } else {
05348       tmpptr = strchr(ext, '&');
05349    }
05350 
05351    if (tmpptr)
05352       *tmpptr++ = '\0';
05353 
05354    ast_channel_lock(chan);
05355    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05356       category = ast_strdupa(category);
05357    }
05358    ast_channel_unlock(chan);
05359 
05360    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05361       ast_copy_string(flag, "Urgent", sizeof(flag));
05362    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05363       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05364    } else {
05365       flag[0] = '\0';
05366    }
05367 
05368    ast_debug(3, "Before find_user\n");
05369    if (!(vmu = find_user(&svm, context, ext))) {
05370       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05371       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05372       ast_free(tmp);
05373       return res;
05374    }
05375    /* Setup pre-file if appropriate */
05376    if (strcmp(vmu->context, "default"))
05377       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05378    else
05379       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05380 
05381    /* Set the path to the prefile. Will be one of 
05382       VM_SPOOL_DIRcontext/ext/busy
05383       VM_SPOOL_DIRcontext/ext/unavail
05384       Depending on the flag set in options.
05385    */
05386    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05387       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05388    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05389       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05390    }
05391    /* Set the path to the tmpfile as
05392       VM_SPOOL_DIR/context/ext/temp
05393       and attempt to create the folder structure.
05394    */
05395    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05396    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05397       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05398       ast_free(tmp);
05399       return -1;
05400    }
05401    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05402    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05403       ast_copy_string(prefile, tempfile, sizeof(prefile));
05404 
05405    DISPOSE(tempfile, -1);
05406    /* It's easier just to try to make it than to check for its existence */
05407 #ifndef IMAP_STORAGE
05408    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05409 #else
05410    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05411    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05412       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05413    }
05414 #endif
05415 
05416    /* Check current or macro-calling context for special extensions */
05417    if (ast_test_flag(vmu, VM_OPERATOR)) {
05418       if (!ast_strlen_zero(vmu->exit)) {
05419          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05420             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05421             ouseexten = 1;
05422          }
05423       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05424          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05425          ouseexten = 1;
05426       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05427          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05428          ousemacro = 1;
05429       }
05430    }
05431 
05432    if (!ast_strlen_zero(vmu->exit)) {
05433       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05434          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05435    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05436       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05437    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05438       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05439       ausemacro = 1;
05440    }
05441 
05442    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05443       for (code = alldtmf; *code; code++) {
05444          char e[2] = "";
05445          e[0] = *code;
05446          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05447             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05448       }
05449    }
05450 
05451    /* Play the beginning intro if desired */
05452    if (!ast_strlen_zero(prefile)) {
05453 #ifdef ODBC_STORAGE
05454       int success = 
05455 #endif
05456          RETRIEVE(prefile, -1, ext, context);
05457       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05458          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05459             res = ast_waitstream(chan, ecodes);
05460 #ifdef ODBC_STORAGE
05461          if (success == -1) {
05462             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05463             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05464             store_file(prefile, vmu->mailbox, vmu->context, -1);
05465          }
05466 #endif
05467       } else {
05468          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05469          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05470       }
05471       DISPOSE(prefile, -1);
05472       if (res < 0) {
05473          ast_debug(1, "Hang up during prefile playback\n");
05474          free_user(vmu);
05475          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05476          ast_free(tmp);
05477          return -1;
05478       }
05479    }
05480    if (res == '#') {
05481       /* On a '#' we skip the instructions */
05482       ast_set_flag(options, OPT_SILENT);
05483       res = 0;
05484    }
05485    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05486       res = ast_stream_and_wait(chan, INTRO, ecodes);
05487       if (res == '#') {
05488          ast_set_flag(options, OPT_SILENT);
05489          res = 0;
05490       }
05491    }
05492    if (res > 0)
05493       ast_stopstream(chan);
05494    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05495     other than the operator -- an automated attendant or mailbox login for example */
05496    if (res == '*') {
05497       chan->exten[0] = 'a';
05498       chan->exten[1] = '\0';
05499       if (!ast_strlen_zero(vmu->exit)) {
05500          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05501       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05502          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05503       }
05504       chan->priority = 0;
05505       free_user(vmu);
05506       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05507       ast_free(tmp);
05508       return 0;
05509    }
05510 
05511    /* Check for a '0' here */
05512    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05513    transfer:
05514       if (ouseexten || ousemacro) {
05515          chan->exten[0] = 'o';
05516          chan->exten[1] = '\0';
05517          if (!ast_strlen_zero(vmu->exit)) {
05518             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05519          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05520             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05521          }
05522          ast_play_and_wait(chan, "transfer");
05523          chan->priority = 0;
05524          free_user(vmu);
05525          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05526       }
05527       ast_free(tmp);
05528       return OPERATOR_EXIT;
05529    }
05530 
05531    /* Allow all other digits to exit Voicemail and return to the dialplan */
05532    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05533       if (!ast_strlen_zero(options->exitcontext))
05534          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05535       free_user(vmu);
05536       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05537       ast_free(tmp);
05538       return res;
05539    }
05540 
05541    if (res < 0) {
05542       free_user(vmu);
05543       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05544       ast_free(tmp);
05545       return -1;
05546    }
05547    /* The meat of recording the message...  All the announcements and beeps have been played*/
05548    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05549    if (!ast_strlen_zero(fmt)) {
05550       msgnum = 0;
05551 
05552 #ifdef IMAP_STORAGE
05553       /* Is ext a mailbox? */
05554       /* must open stream for this user to get info! */
05555       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05556       if (res < 0) {
05557          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05558          ast_free(tmp);
05559          return -1;
05560       }
05561       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05562       /* It is possible under certain circumstances that inboxcount did not
05563        * create a vm_state when it was needed. This is a catchall which will
05564        * rarely be used.
05565        */
05566          if (!(vms = create_vm_state_from_user(vmu))) {
05567             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05568             ast_free(tmp);
05569             return -1;
05570          }
05571       }
05572       vms->newmessages++;
05573       
05574       /* here is a big difference! We add one to it later */
05575       msgnum = newmsgs + oldmsgs;
05576       ast_debug(3, "Messagecount set to %d\n",msgnum);
05577       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05578       /* set variable for compatibility */
05579       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05580 
05581       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05582          goto leave_vm_out;
05583       }
05584 #else
05585       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05586          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05587          if (!res)
05588             res = ast_waitstream(chan, "");
05589          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05590          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05591          inprocess_count(vmu->mailbox, vmu->context, -1);
05592          goto leave_vm_out;
05593       }
05594 
05595 #endif
05596       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05597       txtdes = mkstemp(tmptxtfile);
05598       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05599       if (txtdes < 0) {
05600          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05601          if (!res)
05602             res = ast_waitstream(chan, "");
05603          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05604          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05605          inprocess_count(vmu->mailbox, vmu->context, -1);
05606          goto leave_vm_out;
05607       }
05608 
05609       /* Now play the beep once we have the message number for our next message. */
05610       if (res >= 0) {
05611          /* Unless we're *really* silent, try to send the beep */
05612          res = ast_stream_and_wait(chan, "beep", "");
05613       }
05614             
05615       /* Store information in real-time storage */
05616       if (ast_check_realtime("voicemail_data")) {
05617          snprintf(priority, sizeof(priority), "%d", chan->priority);
05618          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05619          get_date(date, sizeof(date));
05620          ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), "filename", tmptxtfile, SENTINEL);
05621       }
05622 
05623       /* Store information */
05624       txt = fdopen(txtdes, "w+");
05625       if (txt) {
05626          get_date(date, sizeof(date));
05627          fprintf(txt, 
05628             ";\n"
05629             "; Message Information file\n"
05630             ";\n"
05631             "[message]\n"
05632             "origmailbox=%s\n"
05633             "context=%s\n"
05634             "macrocontext=%s\n"
05635             "exten=%s\n"
05636             "priority=%d\n"
05637             "callerchan=%s\n"
05638             "callerid=%s\n"
05639             "origdate=%s\n"
05640             "origtime=%ld\n"
05641             "category=%s\n",
05642             ext,
05643             chan->context,
05644             chan->macrocontext, 
05645             chan->exten,
05646             chan->priority,
05647             chan->name,
05648             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05649             date, (long)time(NULL),
05650             category ? category : "");
05651       } else {
05652          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05653          inprocess_count(vmu->mailbox, vmu->context, -1);
05654          if (ast_check_realtime("voicemail_data")) {
05655             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05656          }
05657          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05658          goto leave_vm_out;
05659       }
05660       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05661 
05662       if (txt) {
05663          fprintf(txt, "flag=%s\n", flag);
05664          if (duration < vmminsecs) {
05665             fclose(txt);
05666             if (option_verbose > 2) 
05667                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05668             ast_filedelete(tmptxtfile, NULL);
05669             unlink(tmptxtfile);
05670             if (ast_check_realtime("voicemail_data")) {
05671                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05672             }
05673             inprocess_count(vmu->mailbox, vmu->context, -1);
05674          } else {
05675             fprintf(txt, "duration=%d\n", duration);
05676             fclose(txt);
05677             if (vm_lock_path(dir)) {
05678                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05679                /* Delete files */
05680                ast_filedelete(tmptxtfile, NULL);
05681                unlink(tmptxtfile);
05682                inprocess_count(vmu->mailbox, vmu->context, -1);
05683             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05684                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05685                unlink(tmptxtfile);
05686                ast_unlock_path(dir);
05687                inprocess_count(vmu->mailbox, vmu->context, -1);
05688                if (ast_check_realtime("voicemail_data")) {
05689                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05690                }
05691             } else {
05692 #ifndef IMAP_STORAGE
05693                msgnum = last_message_index(vmu, dir) + 1;
05694 #endif
05695                make_file(fn, sizeof(fn), dir, msgnum);
05696 
05697                /* assign a variable with the name of the voicemail file */ 
05698 #ifndef IMAP_STORAGE
05699                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05700 #else
05701                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05702 #endif
05703 
05704                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05705                ast_filerename(tmptxtfile, fn, NULL);
05706                rename(tmptxtfile, txtfile);
05707                inprocess_count(vmu->mailbox, vmu->context, -1);
05708 
05709                /* Properly set permissions on voicemail text descriptor file.
05710                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05711                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05712                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05713 
05714                ast_unlock_path(dir);
05715                if (ast_check_realtime("voicemail_data")) {
05716                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05717                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05718                }
05719                /* We must store the file first, before copying the message, because
05720                 * ODBC storage does the entire copy with SQL.
05721                 */
05722                if (ast_fileexists(fn, NULL, NULL) > 0) {
05723                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05724                }
05725 
05726                /* Are there to be more recipients of this message? */
05727                while (tmpptr) {
05728                   struct ast_vm_user recipu, *recip;
05729                   char *exten, *cntx;
05730                
05731                   exten = strsep(&tmpptr, "&");
05732                   cntx = strchr(exten, '@');
05733                   if (cntx) {
05734                      *cntx = '\0';
05735                      cntx++;
05736                   }
05737                   if ((recip = find_user(&recipu, cntx, exten))) {
05738                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05739                      free_user(recip);
05740                   }
05741                }
05742 #ifndef IMAP_STORAGE
05743                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05744                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05745                   char sfn[PATH_MAX];
05746                   char dfn[PATH_MAX];
05747                   int x;
05748                   /* It's easier just to try to make it than to check for its existence */
05749                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05750                   x = last_message_index(vmu, urgdir) + 1;
05751                   make_file(sfn, sizeof(sfn), dir, msgnum);
05752                   make_file(dfn, sizeof(dfn), urgdir, x);
05753                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05754                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05755                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05756                   ast_copy_string(fn, dfn, sizeof(fn));
05757                   msgnum = x;
05758                }
05759 #endif
05760                /* Notification needs to happen after the copy, though. */
05761                if (ast_fileexists(fn, NULL, NULL)) {
05762 #ifdef IMAP_STORAGE
05763                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05764 #else
05765                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05766 #endif
05767                }
05768 
05769                /* Disposal needs to happen after the optional move and copy */
05770                if (ast_fileexists(fn, NULL, NULL)) {
05771                   DISPOSE(dir, msgnum);
05772                }
05773             }
05774          }
05775       } else {
05776          inprocess_count(vmu->mailbox, vmu->context, -1);
05777       }
05778       if (res == '0') {
05779          goto transfer;
05780       } else if (res > 0 && res != 't')
05781          res = 0;
05782 
05783       if (duration < vmminsecs)
05784          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05785          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05786       else
05787          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05788    } else
05789       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05790 leave_vm_out:
05791    free_user(vmu);
05792 
05793 #ifdef IMAP_STORAGE
05794    /* expunge message - use UID Expunge if supported on IMAP server*/
05795    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05796    if (expungeonhangup == 1) {
05797       ast_mutex_lock(&vms->lock);
05798 #ifdef HAVE_IMAP_TK2006
05799       if (LEVELUIDPLUS (vms->mailstream)) {
05800          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05801       } else 
05802 #endif
05803          mail_expunge(vms->mailstream);
05804       ast_mutex_unlock(&vms->lock);
05805    }
05806 #endif
05807 
05808    ast_free(tmp);
05809    return res;
05810 }
05811 
05812 #if !defined(IMAP_STORAGE)
05813 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
05814 {
05815     /* we know the actual number of messages, so stop process when number is hit */
05816 
05817     int x, dest;
05818     char sfn[PATH_MAX];
05819     char dfn[PATH_MAX];
05820 
05821     if (vm_lock_path(dir))
05822         return ERROR_LOCK_PATH;
05823 
05824     for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
05825         make_file(sfn, sizeof(sfn), dir, x);
05826         if (EXISTS(dir, x, sfn, NULL)) {
05827 
05828             if (x != dest) {
05829                 make_file(dfn, sizeof(dfn), dir, dest);
05830                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
05831             }
05832 
05833             dest++;
05834         }
05835     }
05836     ast_unlock_path(dir);
05837 
05838     return dest;
05839 }
05840 #endif
05841 
05842 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05843 {
05844    int d;
05845    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05846    return d;
05847 }
05848 
05849 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05850 {
05851 #ifdef IMAP_STORAGE
05852    /* we must use mbox(x) folder names, and copy the message there */
05853    /* simple. huh? */
05854    char sequence[10];
05855    char mailbox[256];
05856    int res;
05857 
05858    /* get the real IMAP message number for this message */
05859    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05860    
05861    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05862    ast_mutex_lock(&vms->lock);
05863    /* if save to Old folder, put in INBOX as read */
05864    if (box == OLD_FOLDER) {
05865       mail_setflag(vms->mailstream, sequence, "\\Seen");
05866       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05867    } else if (box == NEW_FOLDER) {
05868       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05869       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05870    }
05871    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05872       ast_mutex_unlock(&vms->lock);
05873       return 0;
05874    }
05875    /* Create the folder if it don't exist */
05876    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05877    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05878    if (mail_create(vms->mailstream, mailbox) == NIL) 
05879       ast_debug(5, "Folder exists.\n");
05880    else
05881       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05882    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05883    ast_mutex_unlock(&vms->lock);
05884    return res;
05885 #else
05886    char *dir = vms->curdir;
05887    char *username = vms->username;
05888    char *context = vmu->context;
05889    char sfn[PATH_MAX];
05890    char dfn[PATH_MAX];
05891    char ddir[PATH_MAX];
05892    const char *dbox = mbox(box);
05893    int x, i;
05894    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05895 
05896    if (vm_lock_path(ddir))
05897       return ERROR_LOCK_PATH;
05898 
05899    x = last_message_index(vmu, ddir) + 1;
05900 
05901    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05902       x--;
05903       for (i = 1; i <= x; i++) {
05904          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05905          make_file(sfn, sizeof(sfn), ddir, i);
05906          make_file(dfn, sizeof(dfn), ddir, i - 1);
05907          if (EXISTS(ddir, i, sfn, NULL)) {
05908             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05909          } else
05910             break;
05911       }
05912    } else {
05913       if (x >= vmu->maxmsg) {
05914          ast_unlock_path(ddir);
05915          return -1;
05916       }
05917    }
05918    make_file(sfn, sizeof(sfn), dir, msg);
05919    make_file(dfn, sizeof(dfn), ddir, x);
05920    if (strcmp(sfn, dfn)) {
05921       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05922    }
05923    ast_unlock_path(ddir);
05924 #endif
05925    return 0;
05926 }
05927 
05928 static int adsi_logo(unsigned char *buf)
05929 {
05930    int bytes = 0;
05931    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05932    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05933    return bytes;
05934 }
05935 
05936 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05937 {
05938    unsigned char buf[256];
05939    int bytes=0;
05940    int x;
05941    char num[5];
05942 
05943    *useadsi = 0;
05944    bytes += ast_adsi_data_mode(buf + bytes);
05945    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05946 
05947    bytes = 0;
05948    bytes += adsi_logo(buf);
05949    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05950 #ifdef DISPLAY
05951    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05952 #endif
05953    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05954    bytes += ast_adsi_data_mode(buf + bytes);
05955    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05956 
05957    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05958       bytes = 0;
05959       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05960       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05961       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05962       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05963       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05964       return 0;
05965    }
05966 
05967 #ifdef DISPLAY
05968    /* Add a dot */
05969    bytes = 0;
05970    bytes += ast_adsi_logo(buf);
05971    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05972    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05973    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05974    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05975 #endif
05976    bytes = 0;
05977    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05978    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05979    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05980    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05981    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05982    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05983    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05984 
05985 #ifdef DISPLAY
05986    /* Add another dot */
05987    bytes = 0;
05988    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05989    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05990 
05991    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05992    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05993 #endif
05994 
05995    bytes = 0;
05996    /* These buttons we load but don't use yet */
05997    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05998    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05999    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06000    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06001    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06002    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06003    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06004 
06005 #ifdef DISPLAY
06006    /* Add another dot */
06007    bytes = 0;
06008    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06009    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06010    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06011 #endif
06012 
06013    bytes = 0;
06014    for (x=0;x<5;x++) {
06015       snprintf(num, sizeof(num), "%d", x);
06016       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
06017    }
06018    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06019    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06020 
06021 #ifdef DISPLAY
06022    /* Add another dot */
06023    bytes = 0;
06024    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06025    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06026    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06027 #endif
06028 
06029    if (ast_adsi_end_download(chan)) {
06030       bytes = 0;
06031       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06032       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06033       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06034       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06035       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06036       return 0;
06037    }
06038    bytes = 0;
06039    bytes += ast_adsi_download_disconnect(buf + bytes);
06040    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06041    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06042 
06043    ast_debug(1, "Done downloading scripts...\n");
06044 
06045 #ifdef DISPLAY
06046    /* Add last dot */
06047    bytes = 0;
06048    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06049    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06050 #endif
06051    ast_debug(1, "Restarting session...\n");
06052 
06053    bytes = 0;
06054    /* Load the session now */
06055    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06056       *useadsi = 1;
06057       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06058    } else
06059       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06060 
06061    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06062    return 0;
06063 }
06064 
06065 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06066 {
06067    int x;
06068    if (!ast_adsi_available(chan))
06069       return;
06070    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06071    if (x < 0)
06072       return;
06073    if (!x) {
06074       if (adsi_load_vmail(chan, useadsi)) {
06075          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06076          return;
06077       }
06078    } else
06079       *useadsi = 1;
06080 }
06081 
06082 static void adsi_login(struct ast_channel *chan)
06083 {
06084    unsigned char buf[256];
06085    int bytes=0;
06086    unsigned char keys[8];
06087    int x;
06088    if (!ast_adsi_available(chan))
06089       return;
06090 
06091    for (x=0;x<8;x++)
06092       keys[x] = 0;
06093    /* Set one key for next */
06094    keys[3] = ADSI_KEY_APPS + 3;
06095 
06096    bytes += adsi_logo(buf + bytes);
06097    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06098    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06099    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06100    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06101    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06102    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06103    bytes += ast_adsi_set_keys(buf + bytes, keys);
06104    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06105    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06106 }
06107 
06108 static void adsi_password(struct ast_channel *chan)
06109 {
06110    unsigned char buf[256];
06111    int bytes=0;
06112    unsigned char keys[8];
06113    int x;
06114    if (!ast_adsi_available(chan))
06115       return;
06116 
06117    for (x=0;x<8;x++)
06118       keys[x] = 0;
06119    /* Set one key for next */
06120    keys[3] = ADSI_KEY_APPS + 3;
06121 
06122    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06123    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06124    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06125    bytes += ast_adsi_set_keys(buf + bytes, keys);
06126    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06127    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06128 }
06129 
06130 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06131 {
06132    unsigned char buf[256];
06133    int bytes=0;
06134    unsigned char keys[8];
06135    int x,y;
06136 
06137    if (!ast_adsi_available(chan))
06138       return;
06139 
06140    for (x=0;x<5;x++) {
06141       y = ADSI_KEY_APPS + 12 + start + x;
06142       if (y > ADSI_KEY_APPS + 12 + 4)
06143          y = 0;
06144       keys[x] = ADSI_KEY_SKT | y;
06145    }
06146    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06147    keys[6] = 0;
06148    keys[7] = 0;
06149 
06150    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06151    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06152    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06153    bytes += ast_adsi_set_keys(buf + bytes, keys);
06154    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06155 
06156    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06157 }
06158 
06159 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06160 {
06161    int bytes=0;
06162    unsigned char buf[256]; 
06163    char buf1[256], buf2[256];
06164    char fn2[PATH_MAX];
06165 
06166    char cid[256]="";
06167    char *val;
06168    char *name, *num;
06169    char datetime[21]="";
06170    FILE *f;
06171 
06172    unsigned char keys[8];
06173 
06174    int x;
06175 
06176    if (!ast_adsi_available(chan))
06177       return;
06178 
06179    /* Retrieve important info */
06180    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06181    f = fopen(fn2, "r");
06182    if (f) {
06183       while (!feof(f)) {   
06184          if (!fgets((char *)buf, sizeof(buf), f)) {
06185             continue;
06186          }
06187          if (!feof(f)) {
06188             char *stringp=NULL;
06189             stringp = (char *)buf;
06190             strsep(&stringp, "=");
06191             val = strsep(&stringp, "=");
06192             if (!ast_strlen_zero(val)) {
06193                if (!strcmp((char *)buf, "callerid"))
06194                   ast_copy_string(cid, val, sizeof(cid));
06195                if (!strcmp((char *)buf, "origdate"))
06196                   ast_copy_string(datetime, val, sizeof(datetime));
06197             }
06198          }
06199       }
06200       fclose(f);
06201    }
06202    /* New meaning for keys */
06203    for (x=0;x<5;x++)
06204       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06205    keys[6] = 0x0;
06206    keys[7] = 0x0;
06207 
06208    if (!vms->curmsg) {
06209       /* No prev key, provide "Folder" instead */
06210       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06211    }
06212    if (vms->curmsg >= vms->lastmsg) {
06213       /* If last message ... */
06214       if (vms->curmsg) {
06215          /* but not only message, provide "Folder" instead */
06216          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06217          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06218 
06219       } else {
06220          /* Otherwise if only message, leave blank */
06221          keys[3] = 1;
06222       }
06223    }
06224 
06225    if (!ast_strlen_zero(cid)) {
06226       ast_callerid_parse(cid, &name, &num);
06227       if (!name)
06228          name = num;
06229    } else
06230       name = "Unknown Caller";
06231 
06232    /* If deleted, show "undeleted" */
06233 
06234    if (vms->deleted[vms->curmsg])
06235       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06236 
06237    /* Except "Exit" */
06238    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06239    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06240       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06241    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06242 
06243    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06244    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06245    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06246    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06247    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06248    bytes += ast_adsi_set_keys(buf + bytes, keys);
06249    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06250 
06251    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06252 }
06253 
06254 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06255 {
06256    int bytes=0;
06257    unsigned char buf[256];
06258    unsigned char keys[8];
06259 
06260    int x;
06261 
06262    if (!ast_adsi_available(chan))
06263       return;
06264 
06265    /* New meaning for keys */
06266    for (x=0;x<5;x++)
06267       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06268 
06269    keys[6] = 0x0;
06270    keys[7] = 0x0;
06271 
06272    if (!vms->curmsg) {
06273       /* No prev key, provide "Folder" instead */
06274       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06275    }
06276    if (vms->curmsg >= vms->lastmsg) {
06277       /* If last message ... */
06278       if (vms->curmsg) {
06279          /* but not only message, provide "Folder" instead */
06280          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06281       } else {
06282          /* Otherwise if only message, leave blank */
06283          keys[3] = 1;
06284       }
06285    }
06286 
06287    /* If deleted, show "undeleted" */
06288    if (vms->deleted[vms->curmsg]) 
06289       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06290 
06291    /* Except "Exit" */
06292    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06293    bytes += ast_adsi_set_keys(buf + bytes, keys);
06294    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06295 
06296    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06297 }
06298 
06299 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06300 {
06301    unsigned char buf[256] = "";
06302    char buf1[256] = "", buf2[256] = "";
06303    int bytes=0;
06304    unsigned char keys[8];
06305    int x;
06306 
06307    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06308    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06309    if (!ast_adsi_available(chan))
06310       return;
06311    if (vms->newmessages) {
06312       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06313       if (vms->oldmessages) {
06314          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06315          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06316       } else {
06317          snprintf(buf2, sizeof(buf2), "%s.", newm);
06318       }
06319    } else if (vms->oldmessages) {
06320       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06321       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06322    } else {
06323       strcpy(buf1, "You have no messages.");
06324       buf2[0] = ' ';
06325       buf2[1] = '\0';
06326    }
06327    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06328    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06329    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06330 
06331    for (x=0;x<6;x++)
06332       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06333    keys[6] = 0;
06334    keys[7] = 0;
06335 
06336    /* Don't let them listen if there are none */
06337    if (vms->lastmsg < 0)
06338       keys[0] = 1;
06339    bytes += ast_adsi_set_keys(buf + bytes, keys);
06340 
06341    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06342 
06343    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06344 }
06345 
06346 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06347 {
06348    unsigned char buf[256] = "";
06349    char buf1[256] = "", buf2[256] = "";
06350    int bytes=0;
06351    unsigned char keys[8];
06352    int x;
06353 
06354    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06355 
06356    if (!ast_adsi_available(chan))
06357       return;
06358 
06359    /* Original command keys */
06360    for (x=0;x<6;x++)
06361       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06362 
06363    keys[6] = 0;
06364    keys[7] = 0;
06365 
06366    if ((vms->lastmsg + 1) < 1)
06367       keys[0] = 0;
06368 
06369    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06370       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06371 
06372    if (vms->lastmsg + 1)
06373       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06374    else
06375       strcpy(buf2, "no messages.");
06376    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06377    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06378    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06379    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06380    bytes += ast_adsi_set_keys(buf + bytes, keys);
06381 
06382    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06383 
06384    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06385    
06386 }
06387 
06388 /*
06389 static void adsi_clear(struct ast_channel *chan)
06390 {
06391    char buf[256];
06392    int bytes=0;
06393    if (!ast_adsi_available(chan))
06394       return;
06395    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06396    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06397 
06398    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06399 }
06400 */
06401 
06402 static void adsi_goodbye(struct ast_channel *chan)
06403 {
06404    unsigned char buf[256];
06405    int bytes=0;
06406 
06407    if (!ast_adsi_available(chan))
06408       return;
06409    bytes += adsi_logo(buf + bytes);
06410    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06411    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06412    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06413    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06414 
06415    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06416 }
06417 
06418 /*!\brief get_folder: Folder menu
06419  * Plays "press 1 for INBOX messages" etc.
06420  * Should possibly be internationalized
06421  */
06422 static int get_folder(struct ast_channel *chan, int start)
06423 {
06424    int x;
06425    int d;
06426    char fn[PATH_MAX];
06427    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06428    if (d)
06429       return d;
06430    for (x = start; x< 5; x++) {  /* For all folders */
06431       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06432          return d;
06433       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06434       if (d)
06435          return d;
06436       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06437       d = vm_play_folder_name(chan, fn);
06438       if (d)
06439          return d;
06440       d = ast_waitfordigit(chan, 500);
06441       if (d)
06442          return d;
06443    }
06444    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06445    if (d)
06446       return d;
06447    d = ast_waitfordigit(chan, 4000);
06448    return d;
06449 }
06450 
06451 /*!
06452  * \brief plays a prompt and waits for a keypress.
06453  * \param chan
06454  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06455  * \param start Does not appear to be used at this time.
06456  *
06457  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06458  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06459  * prompting for the number inputs that correspond to the available folders.
06460  * 
06461  * \return zero on success, or -1 on error.
06462  */
06463 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06464 {
06465    int res = 0;
06466    int loops = 0;
06467    res = ast_play_and_wait(chan, fn);  /* Folder name */
06468    while (((res < '0') || (res > '9')) &&
06469          (res != '#') && (res >= 0) &&
06470          loops < 4) {
06471       res = get_folder(chan, 0);
06472       loops++;
06473    }
06474    if (loops == 4) { /* give up */
06475       return '#';
06476    }
06477    return res;
06478 }
06479 
06480 /*!
06481  * \brief presents the option to prepend to an existing message when forwarding it.
06482  * \param chan
06483  * \param vmu
06484  * \param curdir
06485  * \param curmsg
06486  * \param vmfmts
06487  * \param context
06488  * \param record_gain
06489  * \param duration
06490  * \param vms
06491  *
06492  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06493  *
06494  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06495  * \return zero on success, -1 on error.
06496  */
06497 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06498          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06499 {
06500 #ifdef IMAP_STORAGE
06501    int res;
06502 #endif
06503    int cmd = 0;
06504    int retries = 0, prepend_duration = 0, already_recorded = 0;
06505    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06506    char textfile[PATH_MAX];
06507    struct ast_config *msg_cfg;
06508    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06509 #ifndef IMAP_STORAGE
06510    signed char zero_gain = 0;
06511 #endif
06512    const char *duration_str;
06513 
06514    /* Must always populate duration correctly */
06515    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06516    strcpy(textfile, msgfile);
06517    strcpy(backup, msgfile);
06518    strcpy(backup_textfile, msgfile);
06519    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06520    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06521    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06522 
06523    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06524       *duration = atoi(duration_str);
06525    } else {
06526       *duration = 0;
06527    }
06528 
06529    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06530       if (cmd)
06531          retries = 0;
06532       switch (cmd) {
06533       case '1': 
06534 
06535 #ifdef IMAP_STORAGE
06536          /* Record new intro file */
06537          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06538          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06539          res = ast_play_and_wait(chan, INTRO);
06540          res = ast_play_and_wait(chan, "beep");
06541          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06542          cmd = 't';
06543 #else
06544 
06545          /* prepend a message to the current message, update the metadata and return */
06546 
06547          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06548          strcpy(textfile, msgfile);
06549          strncat(textfile, ".txt", sizeof(textfile) - 1);
06550          *duration = 0;
06551 
06552          /* if we can't read the message metadata, stop now */
06553          if (!msg_cfg) {
06554             cmd = 0;
06555             break;
06556          }
06557          
06558          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06559 #ifndef IMAP_STORAGE
06560          if (already_recorded) {
06561             ast_filecopy(backup, msgfile, NULL);
06562             copy(backup_textfile, textfile);
06563          }
06564          else {
06565             ast_filecopy(msgfile, backup, NULL);
06566             copy(textfile, backup_textfile);
06567          }
06568 #endif
06569          already_recorded = 1;
06570 
06571          if (record_gain)
06572             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06573 
06574          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06575          if (cmd == 'S') {
06576             ast_filerename(backup, msgfile, NULL);
06577          }
06578 
06579          if (record_gain)
06580             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06581 
06582          
06583          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06584             *duration = atoi(duration_str);
06585 
06586          if (prepend_duration) {
06587             struct ast_category *msg_cat;
06588             /* need enough space for a maximum-length message duration */
06589             char duration_buf[12];
06590 
06591             *duration += prepend_duration;
06592             msg_cat = ast_category_get(msg_cfg, "message");
06593             snprintf(duration_buf, 11, "%ld", *duration);
06594             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06595                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06596             }
06597          }
06598 
06599 #endif
06600          break;
06601       case '2': 
06602          /* NULL out introfile so we know there is no intro! */
06603 #ifdef IMAP_STORAGE
06604          *vms->introfn = '\0';
06605 #endif
06606          cmd = 't';
06607          break;
06608       case '*':
06609          cmd = '*';
06610          break;
06611       default: 
06612          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06613             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06614          if (!cmd)
06615             cmd = ast_play_and_wait(chan,"vm-starmain");
06616             /* "press star to return to the main menu" */
06617          if (!cmd)
06618             cmd = ast_waitfordigit(chan,6000);
06619          if (!cmd)
06620             retries++;
06621          if (retries > 3)
06622             cmd = 't';
06623       }
06624    }
06625 
06626    if (msg_cfg)
06627       ast_config_destroy(msg_cfg);
06628    if (prepend_duration)
06629       *duration = prepend_duration;
06630 
06631    if (already_recorded && cmd == -1) {
06632       /* restore original message if prepention cancelled */
06633       ast_filerename(backup, msgfile, NULL);
06634       rename(backup_textfile, textfile);
06635    }
06636 
06637    if (cmd == 't' || cmd == 'S')
06638       cmd = 0;
06639    return cmd;
06640 }
06641 
06642 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06643 {
06644    struct ast_event *event;
06645    char *mailbox, *context;
06646 
06647    /* Strip off @default */
06648    context = mailbox = ast_strdupa(box);
06649    strsep(&context, "@");
06650    if (ast_strlen_zero(context))
06651       context = "default";
06652 
06653    if (!(event = ast_event_new(AST_EVENT_MWI,
06654          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06655          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06656          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06657          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06658          AST_EVENT_IE_END))) {
06659       return;
06660    }
06661 
06662    ast_event_queue_and_cache(event);
06663 }
06664 
06665 /*!
06666  * \brief Sends email notification that a user has a new voicemail waiting for them.
06667  * \param chan
06668  * \param vmu
06669  * \param vms
06670  * \param msgnum
06671  * \param duration
06672  * \param fmt
06673  * \param cidnum The Caller ID phone number value.
06674  * \param cidname The Caller ID name value.
06675  *
06676  * \return zero on success, -1 on error.
06677  */
06678 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)
06679 {
06680    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06681    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06682    const char *category;
06683    char *myserveremail = serveremail;
06684 
06685    ast_channel_lock(chan);
06686    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06687       category = ast_strdupa(category);
06688    }
06689    ast_channel_unlock(chan);
06690 
06691 #ifndef IMAP_STORAGE
06692    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06693 #else
06694    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06695 #endif
06696    make_file(fn, sizeof(fn), todir, msgnum);
06697    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06698 
06699    if (!ast_strlen_zero(vmu->attachfmt)) {
06700       if (strstr(fmt, vmu->attachfmt))
06701          fmt = vmu->attachfmt;
06702       else
06703          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);
06704    }
06705 
06706    /* Attach only the first format */
06707    fmt = ast_strdupa(fmt);
06708    stringp = fmt;
06709    strsep(&stringp, "|");
06710 
06711    if (!ast_strlen_zero(vmu->serveremail))
06712       myserveremail = vmu->serveremail;
06713 
06714    if (!ast_strlen_zero(vmu->email)) {
06715       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06716 
06717       if (attach_user_voicemail)
06718          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06719 
06720       /* XXX possible imap issue, should category be NULL XXX */
06721       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06722 
06723       if (attach_user_voicemail)
06724          DISPOSE(todir, msgnum);
06725    }
06726 
06727    if (!ast_strlen_zero(vmu->pager)) {
06728       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06729    }
06730 
06731    if (ast_test_flag(vmu, VM_DELETE))
06732       DELETE(todir, msgnum, fn, vmu);
06733 
06734    /* Leave voicemail for someone */
06735    if (ast_app_has_voicemail(ext_context, NULL)) 
06736       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06737 
06738    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06739 
06740    manager_event(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);
06741    run_externnotify(vmu->context, vmu->mailbox, flag);
06742 
06743 #ifdef IMAP_STORAGE
06744    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06745    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06746       vm_imap_delete(NULL, vms->curmsg, vmu);
06747       vms->newmessages--;  /* Fix new message count */
06748    }
06749 #endif
06750 
06751    return 0;
06752 }
06753 
06754 /*!
06755  * \brief Sends a voicemail message to a mailbox recipient.
06756  * \param ast_channel
06757  * \param context
06758  * \param vms
06759  * \param sender
06760  * \param fmt
06761  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06762  *             Will be 0 when called to forward an existing message (option 8)
06763  *             Will be 1 when called to leave a message (option 3->5)
06764  * \param record_gain 
06765  *
06766  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06767  * 
06768  * When in the leave message mode (is_new_message == 1):
06769  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06770  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06771  *
06772  * When in the forward message mode (is_new_message == 0):
06773  *   - retreives the current message to be forwarded
06774  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06775  *   - determines the target mailbox and folders
06776  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06777  *
06778  * \return zero on success, -1 on error.
06779  */
06780 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)
06781 {
06782 #ifdef IMAP_STORAGE
06783    int todircount=0;
06784    struct vm_state *dstvms;
06785 #endif
06786    char username[70]="";
06787    char fn[PATH_MAX]; /* for playback of name greeting */
06788    char ecodes[16] = "#";
06789    int res = 0, cmd = 0;
06790    struct ast_vm_user *receiver = NULL, *vmtmp;
06791    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06792    char *stringp;
06793    const char *s;
06794    int saved_messages = 0;
06795    int valid_extensions = 0;
06796    char *dir;
06797    int curmsg;
06798    char urgent_str[7] = "";
06799    char tmptxtfile[PATH_MAX];
06800    int prompt_played = 0;
06801 #ifndef IMAP_STORAGE
06802    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06803 #endif
06804    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06805       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06806    }
06807 
06808    if (vms == NULL) return -1;
06809    dir = vms->curdir;
06810    curmsg = vms->curmsg;
06811 
06812    tmptxtfile[0] = '\0';
06813    while (!res && !valid_extensions) {
06814       int use_directory = 0;
06815       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06816          int done = 0;
06817          int retries = 0;
06818          cmd=0;
06819          while ((cmd >= 0) && !done ){
06820             if (cmd)
06821                retries = 0;
06822             switch (cmd) {
06823             case '1': 
06824                use_directory = 0;
06825                done = 1;
06826                break;
06827             case '2': 
06828                use_directory = 1;
06829                done=1;
06830                break;
06831             case '*': 
06832                cmd = 't';
06833                done = 1;
06834                break;
06835             default: 
06836                /* Press 1 to enter an extension press 2 to use the directory */
06837                cmd = ast_play_and_wait(chan,"vm-forward");
06838                if (!cmd)
06839                   cmd = ast_waitfordigit(chan,3000);
06840                if (!cmd)
06841                   retries++;
06842                if (retries > 3) {
06843                   cmd = 't';
06844                   done = 1;
06845                }
06846                
06847             }
06848          }
06849          if (cmd < 0 || cmd == 't')
06850             break;
06851       }
06852       
06853       if (use_directory) {
06854          /* use app_directory */
06855          
06856          char old_context[sizeof(chan->context)];
06857          char old_exten[sizeof(chan->exten)];
06858          int old_priority;
06859          struct ast_app* directory_app;
06860 
06861          directory_app = pbx_findapp("Directory");
06862          if (directory_app) {
06863             char vmcontext[256];
06864             /* make backup copies */
06865             memcpy(old_context, chan->context, sizeof(chan->context));
06866             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06867             old_priority = chan->priority;
06868             
06869             /* call the the Directory, changes the channel */
06870             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06871             res = pbx_exec(chan, directory_app, vmcontext);
06872             
06873             ast_copy_string(username, chan->exten, sizeof(username));
06874             
06875             /* restore the old context, exten, and priority */
06876             memcpy(chan->context, old_context, sizeof(chan->context));
06877             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06878             chan->priority = old_priority;
06879          } else {
06880             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06881             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06882          }
06883       } else {
06884          /* Ask for an extension */
06885          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06886          prompt_played++;
06887          if (res || prompt_played > 4)
06888             break;
06889          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06890             break;
06891       }
06892       
06893       /* start all over if no username */
06894       if (ast_strlen_zero(username))
06895          continue;
06896       stringp = username;
06897       s = strsep(&stringp, "*");
06898       /* start optimistic */
06899       valid_extensions = 1;
06900       while (s) {
06901          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06902             int oldmsgs;
06903             int newmsgs;
06904             int capacity;
06905             if (inboxcount(s, &newmsgs, &oldmsgs)) {
06906                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
06907                /* Shouldn't happen, but allow trying another extension if it does */
06908                res = ast_play_and_wait(chan, "pbx-invalid");
06909                valid_extensions = 0;
06910                break;
06911             }
06912             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
06913             if ((newmsgs + oldmsgs) >= capacity) {
06914                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
06915                res = ast_play_and_wait(chan, "vm-mailboxfull");
06916                valid_extensions = 0;
06917                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06918                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
06919                   free_user(vmtmp);
06920                }
06921                inprocess_count(receiver->mailbox, receiver->context, -1);
06922                break;
06923             }
06924             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06925          } else {
06926             /* XXX Optimization for the future.  When we encounter a single bad extension,
06927              * bailing out on all of the extensions may not be the way to go.  We should
06928              * probably just bail on that single extension, then allow the user to enter
06929              * several more. XXX
06930              */
06931             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06932                free_user(receiver);
06933             }
06934             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06935             /* "I am sorry, that's not a valid extension.  Please try again." */
06936             res = ast_play_and_wait(chan, "pbx-invalid");
06937             valid_extensions = 0;
06938             break;
06939          }
06940 
06941          /* play name if available, else play extension number */
06942          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06943          RETRIEVE(fn, -1, s, receiver->context);
06944          if (ast_fileexists(fn, NULL, NULL) > 0) {
06945             res = ast_stream_and_wait(chan, fn, ecodes);
06946             if (res) {
06947                DISPOSE(fn, -1);
06948                return res;
06949             }
06950          } else {
06951             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06952          }
06953          DISPOSE(fn, -1);
06954 
06955          s = strsep(&stringp, "*");
06956       }
06957       /* break from the loop of reading the extensions */
06958       if (valid_extensions)
06959          break;
06960    }
06961    /* check if we're clear to proceed */
06962    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06963       return res;
06964    if (is_new_message == 1) {
06965       struct leave_vm_options leave_options;
06966       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06967       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06968 
06969       /* Send VoiceMail */
06970       memset(&leave_options, 0, sizeof(leave_options));
06971       leave_options.record_gain = record_gain;
06972       cmd = leave_voicemail(chan, mailbox, &leave_options);
06973    } else {
06974       /* Forward VoiceMail */
06975       long duration = 0;
06976       struct vm_state vmstmp;
06977       int copy_msg_result = 0;
06978       memcpy(&vmstmp, vms, sizeof(vmstmp));
06979 
06980       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06981 
06982       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06983       if (!cmd) {
06984          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06985 #ifdef IMAP_STORAGE
06986             int attach_user_voicemail;
06987             char *myserveremail = serveremail;
06988             
06989             /* get destination mailbox */
06990             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06991             if (!dstvms) {
06992                dstvms = create_vm_state_from_user(vmtmp);
06993             }
06994             if (dstvms) {
06995                init_mailstream(dstvms, 0);
06996                if (!dstvms->mailstream) {
06997                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06998                } else {
06999                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07000                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07001                }
07002             } else {
07003                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07004             }
07005             if (!ast_strlen_zero(vmtmp->serveremail))
07006                myserveremail = vmtmp->serveremail;
07007             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07008             /* NULL category for IMAP storage */
07009             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan, NULL, urgent_str);
07010 #else
07011             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07012 #endif
07013             saved_messages++;
07014             AST_LIST_REMOVE_CURRENT(list);
07015             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07016             free_user(vmtmp);
07017             if (res)
07018                break;
07019          }
07020          AST_LIST_TRAVERSE_SAFE_END;
07021          if (saved_messages > 0 && !copy_msg_result) {
07022             /* give confirmation that the message was saved */
07023             /* commented out since we can't forward batches yet
07024             if (saved_messages == 1)
07025                res = ast_play_and_wait(chan, "vm-message");
07026             else
07027                res = ast_play_and_wait(chan, "vm-messages");
07028             if (!res)
07029                res = ast_play_and_wait(chan, "vm-saved"); */
07030 #ifdef IMAP_STORAGE
07031             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07032             if (ast_strlen_zero(vmstmp.introfn))
07033 #endif
07034             res = ast_play_and_wait(chan, "vm-msgsaved");
07035          }
07036 #ifndef IMAP_STORAGE
07037          else {
07038             /* with IMAP, mailbox full warning played by imap_check_limits */
07039             res = ast_play_and_wait(chan, "vm-mailboxfull");
07040          }
07041          /* Restore original message without prepended message if backup exists */
07042          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07043          strcpy(textfile, msgfile);
07044          strcpy(backup, msgfile);
07045          strcpy(backup_textfile, msgfile);
07046          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07047          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07048          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07049          if (ast_fileexists(backup, NULL, NULL) > 0) {
07050             ast_filerename(backup, msgfile, NULL);
07051             rename(backup_textfile, textfile);
07052          }
07053 #endif
07054       }
07055       DISPOSE(dir, curmsg);
07056 #ifndef IMAP_STORAGE
07057       if (cmd) { /* assuming hangup, cleanup backup file */
07058          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07059          strcpy(textfile, msgfile);
07060          strcpy(backup_textfile, msgfile);
07061          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07062          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07063          rename(backup_textfile, textfile);
07064       }
07065 #endif
07066    }
07067 
07068    /* If anything failed above, we still have this list to free */
07069    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07070       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07071       free_user(vmtmp);
07072    }
07073    return res ? res : cmd;
07074 }
07075 
07076 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07077 {
07078    int res;
07079    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07080       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07081    return res;
07082 }
07083 
07084 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07085 {
07086    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);
07087 }
07088 
07089 static int play_message_category(struct ast_channel *chan, const char *category)
07090 {
07091    int res = 0;
07092 
07093    if (!ast_strlen_zero(category))
07094       res = ast_play_and_wait(chan, category);
07095 
07096    if (res) {
07097       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07098       res = 0;
07099    }
07100 
07101    return res;
07102 }
07103 
07104 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07105 {
07106    int res = 0;
07107    struct vm_zone *the_zone = NULL;
07108    time_t t;
07109 
07110    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07111       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07112       return 0;
07113    }
07114 
07115    /* Does this user have a timezone specified? */
07116    if (!ast_strlen_zero(vmu->zonetag)) {
07117       /* Find the zone in the list */
07118       struct vm_zone *z;
07119       AST_LIST_LOCK(&zones);
07120       AST_LIST_TRAVERSE(&zones, z, list) {
07121          if (!strcmp(z->name, vmu->zonetag)) {
07122             the_zone = z;
07123             break;
07124          }
07125       }
07126       AST_LIST_UNLOCK(&zones);
07127    }
07128 
07129 /* No internal variable parsing for now, so we'll comment it out for the time being */
07130 #if 0
07131    /* Set the DIFF_* variables */
07132    ast_localtime(&t, &time_now, NULL);
07133    tv_now = ast_tvnow();
07134    ast_localtime(&tv_now, &time_then, NULL);
07135 
07136    /* Day difference */
07137    if (time_now.tm_year == time_then.tm_year)
07138       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
07139    else
07140       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07141    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07142 
07143    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07144 #endif
07145    if (the_zone) {
07146       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07147    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07148       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07149    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07150       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07151    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07152       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);
07153    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07154       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07155    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07156       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07157    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07158       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07159    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07160       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);
07161    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07162       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07163    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07164       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07165    } else {
07166       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07167    }
07168 #if 0
07169    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07170 #endif
07171    return res;
07172 }
07173 
07174 
07175 
07176 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07177 {
07178    int res = 0;
07179    int i;
07180    char *callerid, *name;
07181    char prefile[PATH_MAX] = "";
07182    
07183 
07184    /* If voicemail cid is not enabled, or we didn't get cid or context from
07185     * the attribute file, leave now.
07186     *
07187     * TODO Still need to change this so that if this function is called by the
07188     * message envelope (and someone is explicitly requesting to hear the CID),
07189     * it does not check to see if CID is enabled in the config file.
07190     */
07191    if ((cid == NULL)||(context == NULL))
07192       return res;
07193 
07194    /* Strip off caller ID number from name */
07195    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07196    ast_callerid_parse(cid, &name, &callerid);
07197    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07198       /* Check for internal contexts and only */
07199       /* say extension when the call didn't come from an internal context in the list */
07200       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07201          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07202          if ((strcmp(cidinternalcontexts[i], context) == 0))
07203             break;
07204       }
07205       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07206          if (!res) {
07207             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07208             if (!ast_strlen_zero(prefile)) {
07209             /* See if we can find a recorded name for this person instead of their extension number */
07210                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07211                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07212                   if (!callback)
07213                      res = wait_file2(chan, vms, "vm-from");
07214                   res = ast_stream_and_wait(chan, prefile, "");
07215                } else {
07216                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07217                   /* Say "from extension" as one saying to sound smoother */
07218                   if (!callback)
07219                      res = wait_file2(chan, vms, "vm-from-extension");
07220                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07221                }
07222             }
07223          }
07224       } else if (!res) {
07225          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07226          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07227          if (!callback)
07228             res = wait_file2(chan, vms, "vm-from-phonenumber");
07229          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07230       }
07231    } else {
07232       /* Number unknown */
07233       ast_debug(1, "VM-CID: From an unknown number\n");
07234       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07235       res = wait_file2(chan, vms, "vm-unknown-caller");
07236    }
07237    return res;
07238 }
07239 
07240 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07241 {
07242    int res = 0;
07243    int durationm;
07244    int durations;
07245    /* Verify that we have a duration for the message */
07246    if (duration == NULL)
07247       return res;
07248 
07249    /* Convert from seconds to minutes */
07250    durations=atoi(duration);
07251    durationm=(durations / 60);
07252 
07253    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07254 
07255    if ((!res) && (durationm >= minduration)) {
07256       res = wait_file2(chan, vms, "vm-duration");
07257 
07258       /* POLISH syntax */
07259       if (!strncasecmp(chan->language, "pl", 2)) {
07260          div_t num = div(durationm, 10);
07261 
07262          if (durationm == 1) {
07263             res = ast_play_and_wait(chan, "digits/1z");
07264             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07265          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07266             if (num.rem == 2) {
07267                if (!num.quot) {
07268                   res = ast_play_and_wait(chan, "digits/2-ie");
07269                } else {
07270                   res = say_and_wait(chan, durationm - 2 , chan->language);
07271                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07272                }
07273             } else {
07274                res = say_and_wait(chan, durationm, chan->language);
07275             }
07276             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07277          } else {
07278             res = say_and_wait(chan, durationm, chan->language);
07279             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07280          }
07281       /* DEFAULT syntax */
07282       } else {
07283          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07284          res = wait_file2(chan, vms, "vm-minutes");
07285       }
07286    }
07287    return res;
07288 }
07289 
07290 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07291 {
07292    int res = 0;
07293    char filename[256], *cid;
07294    const char *origtime, *context, *category, *duration, *flag;
07295    struct ast_config *msg_cfg;
07296    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07297 
07298    vms->starting = 0;
07299    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07300    adsi_message(chan, vms);
07301    if (!vms->curmsg)
07302       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07303    else if (vms->curmsg == vms->lastmsg)
07304       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07305 
07306    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07307    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07308    msg_cfg = ast_config_load(filename, config_flags);
07309    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07310       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07311       return 0;
07312    }
07313    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07314 
07315    /* Play the word urgent if we are listening to urgent messages */
07316    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07317       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07318    }
07319 
07320    if (!res) {
07321       /* POLISH syntax */
07322       if (!strncasecmp(chan->language, "pl", 2)) {
07323          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07324             int ten, one;
07325             char nextmsg[256];
07326             ten = (vms->curmsg + 1) / 10;
07327             one = (vms->curmsg + 1) % 10;
07328 
07329             if (vms->curmsg < 20) {
07330                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07331                res = wait_file2(chan, vms, nextmsg);
07332             } else {
07333                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07334                res = wait_file2(chan, vms, nextmsg);
07335                if (one > 0) {
07336                   if (!res) {
07337                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07338                      res = wait_file2(chan, vms, nextmsg);
07339                   }
07340                }
07341             }
07342          }
07343          if (!res)
07344             res = wait_file2(chan, vms, "vm-message");
07345       /* HEBREW syntax */
07346       } else if (!strncasecmp(chan->language, "he", 2)) {
07347          if (!vms->curmsg) {
07348             res = wait_file2(chan, vms, "vm-message");
07349             res = wait_file2(chan, vms, "vm-first");
07350          } else if (vms->curmsg == vms->lastmsg) {
07351             res = wait_file2(chan, vms, "vm-message");
07352             res = wait_file2(chan, vms, "vm-last");
07353          } else {
07354             res = wait_file2(chan, vms, "vm-message");
07355             res = wait_file2(chan, vms, "vm-number");
07356             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07357          }
07358       } else {
07359          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07360             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07361          } else { /* DEFAULT syntax */
07362             res = wait_file2(chan, vms, "vm-message");
07363          }
07364          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07365             if (!res) {
07366                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07367             }
07368          }
07369       }
07370    }
07371 
07372    if (!msg_cfg) {
07373       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07374       return 0;
07375    }
07376 
07377    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07378       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07379       DISPOSE(vms->curdir, vms->curmsg);
07380       ast_config_destroy(msg_cfg);
07381       return 0;
07382    }
07383 
07384    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07385    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07386    category = ast_variable_retrieve(msg_cfg, "message", "category");
07387 
07388    context = ast_variable_retrieve(msg_cfg, "message", "context");
07389    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07390       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07391    if (!res) {
07392       res = play_message_category(chan, category);
07393    }
07394    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07395       res = play_message_datetime(chan, vmu, origtime, filename);
07396    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07397       res = play_message_callerid(chan, vms, cid, context, 0);
07398    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07399       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07400    /* Allow pressing '1' to skip envelope / callerid */
07401    if (res == '1')
07402       res = 0;
07403    ast_config_destroy(msg_cfg);
07404 
07405    if (!res) {
07406       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07407       vms->heard[vms->curmsg] = 1;
07408 #ifdef IMAP_STORAGE
07409       /*IMAP storage stores any prepended message from a forward
07410        * as a separate file from the rest of the message
07411        */
07412       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07413          wait_file(chan, vms, vms->introfn);
07414       }
07415 #endif
07416       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07417          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07418          res = 0;
07419       }
07420    }
07421    DISPOSE(vms->curdir, vms->curmsg);
07422    return res;
07423 }
07424 
07425 #ifdef IMAP_STORAGE
07426 static int imap_remove_file(char *dir, int msgnum)
07427 {
07428    char fn[PATH_MAX];
07429    char full_fn[PATH_MAX];
07430    char intro[PATH_MAX] = {0,};
07431    
07432    if (msgnum > -1) {
07433       make_file(fn, sizeof(fn), dir, msgnum);
07434       snprintf(intro, sizeof(intro), "%sintro", fn);
07435    } else
07436       ast_copy_string(fn, dir, sizeof(fn));
07437    
07438    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07439       ast_filedelete(fn, NULL);
07440       if (!ast_strlen_zero(intro)) {
07441          ast_filedelete(intro, NULL);
07442       }
07443       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07444       unlink(full_fn);
07445    }
07446    return 0;
07447 }
07448 
07449 
07450 
07451 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07452 {
07453    char *file, *filename;
07454    char *attachment;
07455    char arg[10];
07456    int i;
07457    BODY* body;
07458 
07459    file = strrchr(ast_strdupa(dir), '/');
07460    if (file) {
07461       *file++ = '\0';
07462    } else {
07463       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07464       return -1;
07465    }
07466 
07467    ast_mutex_lock(&vms->lock);
07468    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07469       mail_fetchstructure(vms->mailstream, i + 1, &body);
07470       /* We have the body, now we extract the file name of the first attachment. */
07471       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07472          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07473       } else {
07474          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07475          ast_mutex_unlock(&vms->lock);
07476          return -1;
07477       }
07478       filename = strsep(&attachment, ".");
07479       if (!strcmp(filename, file)) {
07480          sprintf (arg,"%d", i+1);
07481          mail_setflag (vms->mailstream,arg,"\\DELETED");
07482       }
07483    }
07484    mail_expunge(vms->mailstream);
07485    ast_mutex_unlock(&vms->lock);
07486    return 0;
07487 }
07488 
07489 #elif !defined(IMAP_STORAGE)
07490 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07491 {
07492    int count_msg, last_msg;
07493 
07494    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07495 
07496    /* Rename the member vmbox HERE so that we don't try to return before
07497     * we know what's going on.
07498     */
07499    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07500 
07501    /* Faster to make the directory than to check if it exists. */
07502    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07503 
07504    /* traverses directory using readdir (or select query for ODBC) */
07505    count_msg = count_messages(vmu, vms->curdir);
07506    if (count_msg < 0) {
07507       return count_msg;
07508    } else {
07509       vms->lastmsg = count_msg - 1;
07510    }
07511 
07512    if (vm_allocate_dh(vms, vmu, count_msg)) {
07513       return -1;
07514    }
07515 
07516    /*
07517    The following test is needed in case sequencing gets messed up.
07518    There appears to be more than one way to mess up sequence, so
07519    we will not try to find all of the root causes--just fix it when
07520    detected.
07521    */
07522 
07523    if (vm_lock_path(vms->curdir)) {
07524       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07525       return ERROR_LOCK_PATH;
07526    }
07527 
07528    /* for local storage, checks directory for messages up to maxmsg limit */
07529    last_msg = last_message_index(vmu, vms->curdir);
07530    ast_unlock_path(vms->curdir);
07531 
07532    if (last_msg < -1) {
07533       return last_msg;
07534    } else if (vms->lastmsg != last_msg) {
07535       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07536       resequence_mailbox(vmu, vms->curdir, count_msg);
07537    }
07538 
07539    return 0;
07540 }
07541 #endif
07542 
07543 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07544 {
07545    int x = 0;
07546 
07547 #ifndef IMAP_STORAGE
07548    int last_msg_idx;
07549    int res = 0, nummsg;
07550    char fn2[PATH_MAX];
07551 #endif
07552 
07553    if (vms->lastmsg <= -1) {
07554       goto done;
07555    }
07556 
07557    vms->curmsg = -1;
07558 #ifndef IMAP_STORAGE
07559    /* Get the deleted messages fixed */
07560    if (vm_lock_path(vms->curdir)) {
07561       return ERROR_LOCK_PATH;
07562    }
07563 
07564    /* update count as message may have arrived while we've got mailbox open */
07565    last_msg_idx = last_message_index(vmu, vms->curdir);
07566    if (last_msg_idx != vms->lastmsg) {
07567       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07568    }
07569 
07570    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07571    for (x = 0; x < last_msg_idx + 1; x++) {
07572       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07573          /* Save this message.  It's not in INBOX or hasn't been heard */
07574          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07575          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07576             break;
07577          }
07578          vms->curmsg++;
07579          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07580          if (strcmp(vms->fn, fn2)) {
07581             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07582          }
07583       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07584          /* Move to old folder before deleting */
07585          res = save_to_folder(vmu, vms, x, 1);
07586          if (res == ERROR_LOCK_PATH) {
07587             /* If save failed do not delete the message */
07588             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07589             vms->deleted[x] = 0;
07590             vms->heard[x] = 0;
07591             --x;
07592          }
07593       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07594          /* Move to deleted folder */
07595          res = save_to_folder(vmu, vms, x, 10);
07596          if (res == ERROR_LOCK_PATH) {
07597             /* If save failed do not delete the message */
07598             vms->deleted[x] = 0;
07599             vms->heard[x] = 0;
07600             --x;
07601          }
07602       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07603          /* If realtime storage enabled - we should explicitly delete this message,
07604          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07605          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07606          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07607             DELETE(vms->curdir, x, vms->fn, vmu);
07608          }
07609       }
07610    }
07611 
07612    /* Delete ALL remaining messages */
07613    nummsg = x - 1;
07614    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07615       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07616       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07617          DELETE(vms->curdir, x, vms->fn, vmu);
07618       }
07619    }
07620    ast_unlock_path(vms->curdir);
07621 #else /* defined(IMAP_STORAGE) */
07622    if (vms->deleted) {
07623       /* Since we now expunge after each delete, deleting in reverse order
07624        * ensures that no reordering occurs between each step. */
07625       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07626          if (vms->deleted[x]) {
07627             ast_debug(3, "IMAP delete of %d\n", x);
07628             DELETE(vms->curdir, x, vms->fn, vmu);
07629          }
07630       }
07631    }
07632 #endif
07633 
07634 done:
07635    if (vms->deleted) {
07636       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07637    }
07638    if (vms->heard) {
07639       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07640    }
07641 
07642    return 0;
07643 }
07644 
07645 /* In Greek even though we CAN use a syntax like "friends messages"
07646  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07647  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07648  * syntax for the above three categories which is more elegant.
07649  */
07650 
07651 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07652 {
07653    int cmd;
07654    char *buf;
07655 
07656    buf = alloca(strlen(box) + 2);
07657    strcpy(buf, box);
07658    strcat(buf,"s");
07659 
07660    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07661       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07662       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07663    } else {
07664       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07665       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07666    }
07667 }
07668 
07669 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07670 {
07671    int cmd;
07672 
07673    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07674       if (!strcasecmp(box, "vm-INBOX"))
07675          cmd = ast_play_and_wait(chan, "vm-new-e");
07676       else
07677          cmd = ast_play_and_wait(chan, "vm-old-e");
07678       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07679    } else {
07680       cmd = ast_play_and_wait(chan, "vm-messages");
07681       return cmd ? cmd : ast_play_and_wait(chan, box);
07682    }
07683 }
07684 
07685 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07686 {
07687    int cmd;
07688 
07689    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07690       cmd = ast_play_and_wait(chan, "vm-messages");
07691       return cmd ? cmd : ast_play_and_wait(chan, box);
07692    } else {
07693       cmd = ast_play_and_wait(chan, box);
07694       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07695    }
07696 }
07697 
07698 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07699 {
07700    int cmd;
07701 
07702    if (  !strncasecmp(chan->language, "it", 2) ||
07703         !strncasecmp(chan->language, "es", 2) ||
07704         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07705       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07706       return cmd ? cmd : ast_play_and_wait(chan, box);
07707    } else if (!strncasecmp(chan->language, "gr", 2)) {
07708       return vm_play_folder_name_gr(chan, box);
07709    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07710       return ast_play_and_wait(chan, box);
07711    } else if (!strncasecmp(chan->language, "pl", 2)) {
07712       return vm_play_folder_name_pl(chan, box);
07713    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07714       return vm_play_folder_name_ua(chan, box);
07715    } else {  /* Default English */
07716       cmd = ast_play_and_wait(chan, box);
07717       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07718    }
07719 }
07720 
07721 /* GREEK SYNTAX
07722    In greek the plural for old/new is
07723    different so we need the following files
07724    We also need vm-denExeteMynhmata because
07725    this syntax is different.
07726 
07727    -> vm-Olds.wav : "Palia"
07728    -> vm-INBOXs.wav : "Nea"
07729    -> vm-denExeteMynhmata : "den exete mynhmata"
07730 */
07731 
07732 
07733 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07734 {
07735    int res = 0;
07736 
07737    if (vms->newmessages) {
07738       res = ast_play_and_wait(chan, "vm-youhave");
07739       if (!res) 
07740          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07741       if (!res) {
07742          if ((vms->newmessages == 1)) {
07743             res = ast_play_and_wait(chan, "vm-INBOX");
07744             if (!res)
07745                res = ast_play_and_wait(chan, "vm-message");
07746          } else {
07747             res = ast_play_and_wait(chan, "vm-INBOXs");
07748             if (!res)
07749                res = ast_play_and_wait(chan, "vm-messages");
07750          }
07751       }
07752    } else if (vms->oldmessages){
07753       res = ast_play_and_wait(chan, "vm-youhave");
07754       if (!res)
07755          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07756       if ((vms->oldmessages == 1)){
07757          res = ast_play_and_wait(chan, "vm-Old");
07758          if (!res)
07759             res = ast_play_and_wait(chan, "vm-message");
07760       } else {
07761          res = ast_play_and_wait(chan, "vm-Olds");
07762          if (!res)
07763             res = ast_play_and_wait(chan, "vm-messages");
07764       }
07765    } else if (!vms->oldmessages && !vms->newmessages) 
07766       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07767    return res;
07768 }
07769 
07770 /* Version of vm_intro() designed to work for many languages.
07771  *
07772  * It is hoped that this function can prevent the proliferation of 
07773  * language-specific vm_intro() functions and in time replace the language-
07774  * specific functions which already exist.  An examination of the language-
07775  * specific functions revealed that they all corrected the same deficiencies
07776  * in vm_intro_en() (which was the default function). Namely:
07777  *
07778  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07779  *     wording of the voicemail greeting hides this problem.  For example,
07780  *     vm-INBOX contains only the word "new".  This means that both of these
07781  *     sequences produce valid utterances:
07782  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07783  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07784  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07785  *     in many languages) the first utterance becomes "you have 1 the new message".
07786  *  2) The function contains hardcoded rules for pluralizing the word "message".
07787  *     These rules are correct for English, but not for many other languages.
07788  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07789  *     required in many languages.
07790  *  4) The gender of the word for "message" is not specified. This is a problem
07791  *     because in many languages the gender of the number in phrases such
07792  *     as "you have one new message" must match the gender of the word
07793  *     meaning "message".
07794  *
07795  * Fixing these problems for each new language has meant duplication of effort.
07796  * This new function solves the problems in the following general ways:
07797  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07798  *     and vm-Old respectively for those languages where it makes sense.
07799  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07800  *     on vm-message.
07801  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07802  *     prefix on vm-new and vm-old (none for English).
07803  *  4) Pass the gender of the language's word for "message" as an agument to
07804  *     this function which is can in turn pass on to the functions which 
07805  *     say numbers and put endings on nounds and adjectives.
07806  *
07807  * All languages require these messages:
07808  *  vm-youhave    "You have..."
07809  *  vm-and     "and"
07810  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07811  *
07812  * To use it for English, you will need these additional sound files:
07813  *  vm-new     "new"
07814  *  vm-message    "message", singular
07815  *  vm-messages      "messages", plural
07816  *
07817  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07818  *
07819  *  vm-newn    "novoye" (singular, neuter)
07820  *  vm-newx    "novikh" (counting plural form, genative plural)
07821  *  vm-message    "sobsheniye" (singular form)
07822  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07823  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07824  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07825  *  digits/2n     "dva" (neuter singular)
07826  */
07827 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07828 {
07829    int res;
07830    int lastnum = 0;
07831 
07832    res = ast_play_and_wait(chan, "vm-youhave");
07833 
07834    if (!res && vms->newmessages) {
07835       lastnum = vms->newmessages;
07836 
07837       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07838          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07839       }
07840 
07841       if (!res && vms->oldmessages) {
07842          res = ast_play_and_wait(chan, "vm-and");
07843       }
07844    }
07845 
07846    if (!res && vms->oldmessages) {
07847       lastnum = vms->oldmessages;
07848 
07849       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07850          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07851       }
07852    }
07853 
07854    if (!res) {
07855       if (lastnum == 0) {
07856          res = ast_play_and_wait(chan, "vm-no");
07857       }
07858       if (!res) {
07859          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07860       }
07861    }
07862 
07863    return res;
07864 }
07865 
07866 /* Default Hebrew syntax */
07867 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07868 {
07869    int res = 0;
07870 
07871    /* Introduce messages they have */
07872    if (!res) {
07873       if ((vms->newmessages) || (vms->oldmessages)) {
07874          res = ast_play_and_wait(chan, "vm-youhave");
07875       }
07876       /*
07877        * The word "shtei" refers to the number 2 in hebrew when performing a count
07878        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07879        * an element, this is one of them.
07880        */
07881       if (vms->newmessages) {
07882          if (!res) {
07883             if (vms->newmessages == 1) {
07884                res = ast_play_and_wait(chan, "vm-INBOX1");
07885             } else {
07886                if (vms->newmessages == 2) {
07887                   res = ast_play_and_wait(chan, "vm-shtei");
07888                } else {
07889                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07890                }
07891                res = ast_play_and_wait(chan, "vm-INBOX");
07892             }
07893          }
07894          if (vms->oldmessages && !res) {
07895             res = ast_play_and_wait(chan, "vm-and");
07896             if (vms->oldmessages == 1) {
07897                res = ast_play_and_wait(chan, "vm-Old1");
07898             } else {
07899                if (vms->oldmessages == 2) {
07900                   res = ast_play_and_wait(chan, "vm-shtei");
07901                } else {
07902                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07903                }
07904                res = ast_play_and_wait(chan, "vm-Old");
07905             }
07906          }
07907       }
07908       if (!res && vms->oldmessages && !vms->newmessages) {
07909          if (!res) {
07910             if (vms->oldmessages == 1) {
07911                res = ast_play_and_wait(chan, "vm-Old1");
07912             } else {
07913                if (vms->oldmessages == 2) {
07914                   res = ast_play_and_wait(chan, "vm-shtei");
07915                } else {
07916                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07917                }
07918                res = ast_play_and_wait(chan, "vm-Old");
07919             }
07920          }
07921       }
07922       if (!res) {
07923          if (!vms->oldmessages && !vms->newmessages) {
07924             if (!res) {
07925                res = ast_play_and_wait(chan, "vm-nomessages");
07926             }
07927          }
07928       }
07929    }
07930    return res;
07931 }
07932    
07933 /* Default English syntax */
07934 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07935 {
07936    int res;
07937 
07938    /* Introduce messages they have */
07939    res = ast_play_and_wait(chan, "vm-youhave");
07940    if (!res) {
07941       if (vms->urgentmessages) {
07942          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07943          if (!res)
07944             res = ast_play_and_wait(chan, "vm-Urgent");
07945          if ((vms->oldmessages || vms->newmessages) && !res) {
07946             res = ast_play_and_wait(chan, "vm-and");
07947          } else if (!res) {
07948             if ((vms->urgentmessages == 1))
07949                res = ast_play_and_wait(chan, "vm-message");
07950             else
07951                res = ast_play_and_wait(chan, "vm-messages");
07952          }
07953       }
07954       if (vms->newmessages) {
07955          res = say_and_wait(chan, vms->newmessages, chan->language);
07956          if (!res)
07957             res = ast_play_and_wait(chan, "vm-INBOX");
07958          if (vms->oldmessages && !res)
07959             res = ast_play_and_wait(chan, "vm-and");
07960          else if (!res) {
07961             if ((vms->newmessages == 1))
07962                res = ast_play_and_wait(chan, "vm-message");
07963             else
07964                res = ast_play_and_wait(chan, "vm-messages");
07965          }
07966             
07967       }
07968       if (!res && vms->oldmessages) {
07969          res = say_and_wait(chan, vms->oldmessages, chan->language);
07970          if (!res)
07971             res = ast_play_and_wait(chan, "vm-Old");
07972          if (!res) {
07973             if (vms->oldmessages == 1)
07974                res = ast_play_and_wait(chan, "vm-message");
07975             else
07976                res = ast_play_and_wait(chan, "vm-messages");
07977          }
07978       }
07979       if (!res) {
07980          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07981             res = ast_play_and_wait(chan, "vm-no");
07982             if (!res)
07983                res = ast_play_and_wait(chan, "vm-messages");
07984          }
07985       }
07986    }
07987    return res;
07988 }
07989 
07990 /* ITALIAN syntax */
07991 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07992 {
07993    /* Introduce messages they have */
07994    int res;
07995    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07996       res = ast_play_and_wait(chan, "vm-no") ||
07997          ast_play_and_wait(chan, "vm-message");
07998    else
07999       res = ast_play_and_wait(chan, "vm-youhave");
08000    if (!res && vms->newmessages) {
08001       res = (vms->newmessages == 1) ?
08002          ast_play_and_wait(chan, "digits/un") ||
08003          ast_play_and_wait(chan, "vm-nuovo") ||
08004          ast_play_and_wait(chan, "vm-message") :
08005          /* 2 or more new messages */
08006          say_and_wait(chan, vms->newmessages, chan->language) ||
08007          ast_play_and_wait(chan, "vm-nuovi") ||
08008          ast_play_and_wait(chan, "vm-messages");
08009       if (!res && vms->oldmessages)
08010          res = ast_play_and_wait(chan, "vm-and");
08011    }
08012    if (!res && vms->oldmessages) {
08013       res = (vms->oldmessages == 1) ?
08014          ast_play_and_wait(chan, "digits/un") ||
08015          ast_play_and_wait(chan, "vm-vecchio") ||
08016          ast_play_and_wait(chan, "vm-message") :
08017          /* 2 or more old messages */
08018          say_and_wait(chan, vms->oldmessages, chan->language) ||
08019          ast_play_and_wait(chan, "vm-vecchi") ||
08020          ast_play_and_wait(chan, "vm-messages");
08021    }
08022    return res;
08023 }
08024 
08025 /* POLISH syntax */
08026 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08027 {
08028    /* Introduce messages they have */
08029    int res;
08030    div_t num;
08031 
08032    if (!vms->oldmessages && !vms->newmessages) {
08033       res = ast_play_and_wait(chan, "vm-no");
08034       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08035       return res;
08036    } else {
08037       res = ast_play_and_wait(chan, "vm-youhave");
08038    }
08039 
08040    if (vms->newmessages) {
08041       num = div(vms->newmessages, 10);
08042       if (vms->newmessages == 1) {
08043          res = ast_play_and_wait(chan, "digits/1-a");
08044          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08045          res = res ? res : ast_play_and_wait(chan, "vm-message");
08046       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08047          if (num.rem == 2) {
08048             if (!num.quot) {
08049                res = ast_play_and_wait(chan, "digits/2-ie");
08050             } else {
08051                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08052                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08053             }
08054          } else {
08055             res = say_and_wait(chan, vms->newmessages, chan->language);
08056          }
08057          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08058          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08059       } else {
08060          res = say_and_wait(chan, vms->newmessages, chan->language);
08061          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08062          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08063       }
08064       if (!res && vms->oldmessages)
08065          res = ast_play_and_wait(chan, "vm-and");
08066    }
08067    if (!res && vms->oldmessages) {
08068       num = div(vms->oldmessages, 10);
08069       if (vms->oldmessages == 1) {
08070          res = ast_play_and_wait(chan, "digits/1-a");
08071          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08072          res = res ? res : ast_play_and_wait(chan, "vm-message");
08073       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08074          if (num.rem == 2) {
08075             if (!num.quot) {
08076                res = ast_play_and_wait(chan, "digits/2-ie");
08077             } else {
08078                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08079                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08080             }
08081          } else {
08082             res = say_and_wait(chan, vms->oldmessages, chan->language);
08083          }
08084          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08085          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08086       } else {
08087          res = say_and_wait(chan, vms->oldmessages, chan->language);
08088          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08089          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08090       }
08091    }
08092 
08093    return res;
08094 }
08095 
08096 /* SWEDISH syntax */
08097 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08098 {
08099    /* Introduce messages they have */
08100    int res;
08101 
08102    res = ast_play_and_wait(chan, "vm-youhave");
08103    if (res)
08104       return res;
08105 
08106    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08107       res = ast_play_and_wait(chan, "vm-no");
08108       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08109       return res;
08110    }
08111 
08112    if (vms->newmessages) {
08113       if ((vms->newmessages == 1)) {
08114          res = ast_play_and_wait(chan, "digits/ett");
08115          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08116          res = res ? res : ast_play_and_wait(chan, "vm-message");
08117       } else {
08118          res = say_and_wait(chan, vms->newmessages, chan->language);
08119          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08120          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08121       }
08122       if (!res && vms->oldmessages)
08123          res = ast_play_and_wait(chan, "vm-and");
08124    }
08125    if (!res && vms->oldmessages) {
08126       if (vms->oldmessages == 1) {
08127          res = ast_play_and_wait(chan, "digits/ett");
08128          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08129          res = res ? res : ast_play_and_wait(chan, "vm-message");
08130       } else {
08131          res = say_and_wait(chan, vms->oldmessages, chan->language);
08132          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08133          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08134       }
08135    }
08136 
08137    return res;
08138 }
08139 
08140 /* NORWEGIAN syntax */
08141 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
08142 {
08143    /* Introduce messages they have */
08144    int res;
08145 
08146    res = ast_play_and_wait(chan, "vm-youhave");
08147    if (res)
08148       return res;
08149 
08150    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08151       res = ast_play_and_wait(chan, "vm-no");
08152       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08153       return res;
08154    }
08155 
08156    if (vms->newmessages) {
08157       if ((vms->newmessages == 1)) {
08158          res = ast_play_and_wait(chan, "digits/1");
08159          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08160          res = res ? res : ast_play_and_wait(chan, "vm-message");
08161       } else {
08162          res = say_and_wait(chan, vms->newmessages, chan->language);
08163          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08164          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08165       }
08166       if (!res && vms->oldmessages)
08167          res = ast_play_and_wait(chan, "vm-and");
08168    }
08169    if (!res && vms->oldmessages) {
08170       if (vms->oldmessages == 1) {
08171          res = ast_play_and_wait(chan, "digits/1");
08172          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08173          res = res ? res : ast_play_and_wait(chan, "vm-message");
08174       } else {
08175          res = say_and_wait(chan, vms->oldmessages, chan->language);
08176          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08177          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08178       }
08179    }
08180 
08181    return res;
08182 }
08183 
08184 /* GERMAN syntax */
08185 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
08186 {
08187    /* Introduce messages they have */
08188    int res;
08189    res = ast_play_and_wait(chan, "vm-youhave");
08190    if (!res) {
08191       if (vms->newmessages) {
08192          if ((vms->newmessages == 1))
08193             res = ast_play_and_wait(chan, "digits/1F");
08194          else
08195             res = say_and_wait(chan, vms->newmessages, chan->language);
08196          if (!res)
08197             res = ast_play_and_wait(chan, "vm-INBOX");
08198          if (vms->oldmessages && !res)
08199             res = ast_play_and_wait(chan, "vm-and");
08200          else if (!res) {
08201             if ((vms->newmessages == 1))
08202                res = ast_play_and_wait(chan, "vm-message");
08203             else
08204                res = ast_play_and_wait(chan, "vm-messages");
08205          }
08206             
08207       }
08208       if (!res && vms->oldmessages) {
08209          if (vms->oldmessages == 1)
08210             res = ast_play_and_wait(chan, "digits/1F");
08211          else
08212             res = say_and_wait(chan, vms->oldmessages, chan->language);
08213          if (!res)
08214             res = ast_play_and_wait(chan, "vm-Old");
08215          if (!res) {
08216             if (vms->oldmessages == 1)
08217                res = ast_play_and_wait(chan, "vm-message");
08218             else
08219                res = ast_play_and_wait(chan, "vm-messages");
08220          }
08221       }
08222       if (!res) {
08223          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08224             res = ast_play_and_wait(chan, "vm-no");
08225             if (!res)
08226                res = ast_play_and_wait(chan, "vm-messages");
08227          }
08228       }
08229    }
08230    return res;
08231 }
08232 
08233 /* SPANISH syntax */
08234 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
08235 {
08236    /* Introduce messages they have */
08237    int res;
08238    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08239       res = ast_play_and_wait(chan, "vm-youhaveno");
08240       if (!res)
08241          res = ast_play_and_wait(chan, "vm-messages");
08242    } else {
08243       res = ast_play_and_wait(chan, "vm-youhave");
08244    }
08245    if (!res) {
08246       if (vms->newmessages) {
08247          if (!res) {
08248             if ((vms->newmessages == 1)) {
08249                res = ast_play_and_wait(chan, "digits/1M");
08250                if (!res)
08251                   res = ast_play_and_wait(chan, "vm-message");
08252                if (!res)
08253                   res = ast_play_and_wait(chan, "vm-INBOXs");
08254             } else {
08255                res = say_and_wait(chan, vms->newmessages, chan->language);
08256                if (!res)
08257                   res = ast_play_and_wait(chan, "vm-messages");
08258                if (!res)
08259                   res = ast_play_and_wait(chan, "vm-INBOX");
08260             }
08261          }
08262          if (vms->oldmessages && !res)
08263             res = ast_play_and_wait(chan, "vm-and");
08264       }
08265       if (vms->oldmessages) {
08266          if (!res) {
08267             if (vms->oldmessages == 1) {
08268                res = ast_play_and_wait(chan, "digits/1M");
08269                if (!res)
08270                   res = ast_play_and_wait(chan, "vm-message");
08271                if (!res)
08272                   res = ast_play_and_wait(chan, "vm-Olds");
08273             } else {
08274                res = say_and_wait(chan, vms->oldmessages, chan->language);
08275                if (!res)
08276                   res = ast_play_and_wait(chan, "vm-messages");
08277                if (!res)
08278                   res = ast_play_and_wait(chan, "vm-Old");
08279             }
08280          }
08281       }
08282    }
08283 return res;
08284 }
08285 
08286 /* BRAZILIAN PORTUGUESE syntax */
08287 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
08288    /* Introduce messages they have */
08289    int res;
08290    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08291       res = ast_play_and_wait(chan, "vm-nomessages");
08292       return res;
08293    } else {
08294       res = ast_play_and_wait(chan, "vm-youhave");
08295    }
08296    if (vms->newmessages) {
08297       if (!res)
08298          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08299       if ((vms->newmessages == 1)) {
08300          if (!res)
08301             res = ast_play_and_wait(chan, "vm-message");
08302          if (!res)
08303             res = ast_play_and_wait(chan, "vm-INBOXs");
08304       } else {
08305          if (!res)
08306             res = ast_play_and_wait(chan, "vm-messages");
08307          if (!res)
08308             res = ast_play_and_wait(chan, "vm-INBOX");
08309       }
08310       if (vms->oldmessages && !res)
08311          res = ast_play_and_wait(chan, "vm-and");
08312    }
08313    if (vms->oldmessages) {
08314       if (!res)
08315          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08316       if (vms->oldmessages == 1) {
08317          if (!res)
08318             res = ast_play_and_wait(chan, "vm-message");
08319          if (!res)
08320             res = ast_play_and_wait(chan, "vm-Olds");
08321       } else {
08322          if (!res)
08323             res = ast_play_and_wait(chan, "vm-messages");
08324          if (!res)
08325             res = ast_play_and_wait(chan, "vm-Old");
08326       }
08327    }
08328    return res;
08329 }
08330 
08331 /* FRENCH syntax */
08332 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
08333 {
08334    /* Introduce messages they have */
08335    int res;
08336    res = ast_play_and_wait(chan, "vm-youhave");
08337    if (!res) {
08338       if (vms->newmessages) {
08339          res = say_and_wait(chan, vms->newmessages, chan->language);
08340          if (!res)
08341             res = ast_play_and_wait(chan, "vm-INBOX");
08342          if (vms->oldmessages && !res)
08343             res = ast_play_and_wait(chan, "vm-and");
08344          else if (!res) {
08345             if ((vms->newmessages == 1))
08346                res = ast_play_and_wait(chan, "vm-message");
08347             else
08348                res = ast_play_and_wait(chan, "vm-messages");
08349          }
08350             
08351       }
08352       if (!res && vms->oldmessages) {
08353          res = say_and_wait(chan, vms->oldmessages, chan->language);
08354          if (!res)
08355             res = ast_play_and_wait(chan, "vm-Old");
08356          if (!res) {
08357             if (vms->oldmessages == 1)
08358                res = ast_play_and_wait(chan, "vm-message");
08359             else
08360                res = ast_play_and_wait(chan, "vm-messages");
08361          }
08362       }
08363       if (!res) {
08364          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08365             res = ast_play_and_wait(chan, "vm-no");
08366             if (!res)
08367                res = ast_play_and_wait(chan, "vm-messages");
08368          }
08369       }
08370    }
08371    return res;
08372 }
08373 
08374 /* DUTCH syntax */
08375 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
08376 {
08377    /* Introduce messages they have */
08378    int res;
08379    res = ast_play_and_wait(chan, "vm-youhave");
08380    if (!res) {
08381       if (vms->newmessages) {
08382          res = say_and_wait(chan, vms->newmessages, chan->language);
08383          if (!res) {
08384             if (vms->newmessages == 1)
08385                res = ast_play_and_wait(chan, "vm-INBOXs");
08386             else
08387                res = ast_play_and_wait(chan, "vm-INBOX");
08388          }
08389          if (vms->oldmessages && !res)
08390             res = ast_play_and_wait(chan, "vm-and");
08391          else if (!res) {
08392             if ((vms->newmessages == 1))
08393                res = ast_play_and_wait(chan, "vm-message");
08394             else
08395                res = ast_play_and_wait(chan, "vm-messages");
08396          }
08397             
08398       }
08399       if (!res && vms->oldmessages) {
08400          res = say_and_wait(chan, vms->oldmessages, chan->language);
08401          if (!res) {
08402             if (vms->oldmessages == 1)
08403                res = ast_play_and_wait(chan, "vm-Olds");
08404             else
08405                res = ast_play_and_wait(chan, "vm-Old");
08406          }
08407          if (!res) {
08408             if (vms->oldmessages == 1)
08409                res = ast_play_and_wait(chan, "vm-message");
08410             else
08411                res = ast_play_and_wait(chan, "vm-messages");
08412          }
08413       }
08414       if (!res) {
08415          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08416             res = ast_play_and_wait(chan, "vm-no");
08417             if (!res)
08418                res = ast_play_and_wait(chan, "vm-messages");
08419          }
08420       }
08421    }
08422    return res;
08423 }
08424 
08425 /* PORTUGUESE syntax */
08426 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08427 {
08428    /* Introduce messages they have */
08429    int res;
08430    res = ast_play_and_wait(chan, "vm-youhave");
08431    if (!res) {
08432       if (vms->newmessages) {
08433          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08434          if (!res) {
08435             if ((vms->newmessages == 1)) {
08436                res = ast_play_and_wait(chan, "vm-message");
08437                if (!res)
08438                   res = ast_play_and_wait(chan, "vm-INBOXs");
08439             } else {
08440                res = ast_play_and_wait(chan, "vm-messages");
08441                if (!res)
08442                   res = ast_play_and_wait(chan, "vm-INBOX");
08443             }
08444          }
08445          if (vms->oldmessages && !res)
08446             res = ast_play_and_wait(chan, "vm-and");
08447       }
08448       if (!res && vms->oldmessages) {
08449          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08450          if (!res) {
08451             if (vms->oldmessages == 1) {
08452                res = ast_play_and_wait(chan, "vm-message");
08453                if (!res)
08454                   res = ast_play_and_wait(chan, "vm-Olds");
08455             } else {
08456                res = ast_play_and_wait(chan, "vm-messages");
08457                if (!res)
08458                   res = ast_play_and_wait(chan, "vm-Old");
08459             }
08460          }
08461       }
08462       if (!res) {
08463          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08464             res = ast_play_and_wait(chan, "vm-no");
08465             if (!res)
08466                res = ast_play_and_wait(chan, "vm-messages");
08467          }
08468       }
08469    }
08470    return res;
08471 }
08472 
08473 
08474 /* CZECH syntax */
08475 /* in czech there must be declension of word new and message
08476  * czech        : english        : czech      : english
08477  * --------------------------------------------------------
08478  * vm-youhave   : you have 
08479  * vm-novou     : one new        : vm-zpravu  : message
08480  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08481  * vm-novych    : 5-infinite new : vm-zprav   : messages
08482  * vm-starou   : one old
08483  * vm-stare     : 2-4 old 
08484  * vm-starych   : 5-infinite old
08485  * jednu        : one   - falling 4. 
08486  * vm-no        : no  ( no messages )
08487  */
08488 
08489 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08490 {
08491    int res;
08492    res = ast_play_and_wait(chan, "vm-youhave");
08493    if (!res) {
08494       if (vms->newmessages) {
08495          if (vms->newmessages == 1) {
08496             res = ast_play_and_wait(chan, "digits/jednu");
08497          } else {
08498             res = say_and_wait(chan, vms->newmessages, chan->language);
08499          }
08500          if (!res) {
08501             if ((vms->newmessages == 1))
08502                res = ast_play_and_wait(chan, "vm-novou");
08503             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08504                res = ast_play_and_wait(chan, "vm-nove");
08505             if (vms->newmessages > 4)
08506                res = ast_play_and_wait(chan, "vm-novych");
08507          }
08508          if (vms->oldmessages && !res)
08509             res = ast_play_and_wait(chan, "vm-and");
08510          else if (!res) {
08511             if ((vms->newmessages == 1))
08512                res = ast_play_and_wait(chan, "vm-zpravu");
08513             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08514                res = ast_play_and_wait(chan, "vm-zpravy");
08515             if (vms->newmessages > 4)
08516                res = ast_play_and_wait(chan, "vm-zprav");
08517          }
08518       }
08519       if (!res && vms->oldmessages) {
08520          res = say_and_wait(chan, vms->oldmessages, chan->language);
08521          if (!res) {
08522             if ((vms->oldmessages == 1))
08523                res = ast_play_and_wait(chan, "vm-starou");
08524             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08525                res = ast_play_and_wait(chan, "vm-stare");
08526             if (vms->oldmessages > 4)
08527                res = ast_play_and_wait(chan, "vm-starych");
08528          }
08529          if (!res) {
08530             if ((vms->oldmessages == 1))
08531                res = ast_play_and_wait(chan, "vm-zpravu");
08532             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08533                res = ast_play_and_wait(chan, "vm-zpravy");
08534             if (vms->oldmessages > 4)
08535                res = ast_play_and_wait(chan, "vm-zprav");
08536          }
08537       }
08538       if (!res) {
08539          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08540             res = ast_play_and_wait(chan, "vm-no");
08541             if (!res)
08542                res = ast_play_and_wait(chan, "vm-zpravy");
08543          }
08544       }
08545    }
08546    return res;
08547 }
08548 
08549 /* CHINESE (Taiwan) syntax */
08550 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08551 {
08552    int res;
08553    /* Introduce messages they have */
08554    res = ast_play_and_wait(chan, "vm-you");
08555 
08556    if (!res && vms->newmessages) {
08557       res = ast_play_and_wait(chan, "vm-have");
08558       if (!res)
08559          res = say_and_wait(chan, vms->newmessages, chan->language);
08560       if (!res)
08561          res = ast_play_and_wait(chan, "vm-tong");
08562       if (!res)
08563          res = ast_play_and_wait(chan, "vm-INBOX");
08564       if (vms->oldmessages && !res)
08565          res = ast_play_and_wait(chan, "vm-and");
08566       else if (!res) 
08567          res = ast_play_and_wait(chan, "vm-messages");
08568    }
08569    if (!res && vms->oldmessages) {
08570       res = ast_play_and_wait(chan, "vm-have");
08571       if (!res)
08572          res = say_and_wait(chan, vms->oldmessages, chan->language);
08573       if (!res)
08574          res = ast_play_and_wait(chan, "vm-tong");
08575       if (!res)
08576          res = ast_play_and_wait(chan, "vm-Old");
08577       if (!res)
08578          res = ast_play_and_wait(chan, "vm-messages");
08579    }
08580    if (!res && !vms->oldmessages && !vms->newmessages) {
08581       res = ast_play_and_wait(chan, "vm-haveno");
08582       if (!res)
08583          res = ast_play_and_wait(chan, "vm-messages");
08584    }
08585    return res;
08586 }
08587 
08588 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08589 {
08590    char prefile[256];
08591    
08592    /* Notify the user that the temp greeting is set and give them the option to remove it */
08593    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08594    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08595       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08596       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08597          ast_play_and_wait(chan, "vm-tempgreetactive");
08598       }
08599       DISPOSE(prefile, -1);
08600    }
08601 
08602    /* Play voicemail intro - syntax is different for different languages */
08603    if (0) {
08604       return 0;
08605    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08606       return vm_intro_cs(chan, vms);
08607    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08608       static int deprecation_warning = 0;
08609       if (deprecation_warning++ % 10 == 0) {
08610          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08611       }
08612       return vm_intro_cs(chan, vms);
08613    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08614       return vm_intro_de(chan, vms);
08615    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08616       return vm_intro_es(chan, vms);
08617    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08618       return vm_intro_fr(chan, vms);
08619    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08620       return vm_intro_gr(chan, vms);
08621    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08622       return vm_intro_he(chan, vms);
08623    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08624       return vm_intro_it(chan, vms);
08625    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08626       return vm_intro_nl(chan, vms);
08627    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08628       return vm_intro_no(chan, vms);
08629    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08630       return vm_intro_pl(chan, vms);
08631    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08632       return vm_intro_pt_BR(chan, vms);
08633    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08634       return vm_intro_pt(chan, vms);
08635    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08636       return vm_intro_multilang(chan, vms, "n");
08637    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08638       return vm_intro_se(chan, vms);
08639    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08640       return vm_intro_multilang(chan, vms, "n");
08641    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08642       return vm_intro_zh(chan, vms);
08643    } else {                                             /* Default to ENGLISH */
08644       return vm_intro_en(chan, vms);
08645    }
08646 }
08647 
08648 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08649 {
08650    int res = 0;
08651    /* Play instructions and wait for new command */
08652    while (!res) {
08653       if (vms->starting) {
08654          if (vms->lastmsg > -1) {
08655             if (skipadvanced)
08656                res = ast_play_and_wait(chan, "vm-onefor-full");
08657             else
08658                res = ast_play_and_wait(chan, "vm-onefor");
08659             if (!res)
08660                res = vm_play_folder_name(chan, vms->vmbox);
08661          }
08662          if (!res) {
08663             if (skipadvanced)
08664                res = ast_play_and_wait(chan, "vm-opts-full");
08665             else
08666                res = ast_play_and_wait(chan, "vm-opts");
08667          }
08668       } else {
08669          /* Added for additional help */
08670          if (skipadvanced) {
08671             res = ast_play_and_wait(chan, "vm-onefor-full");
08672             if (!res)
08673                res = vm_play_folder_name(chan, vms->vmbox);
08674             res = ast_play_and_wait(chan, "vm-opts-full");
08675          }
08676          /* Logic:
08677           * If the current message is not the first OR
08678           * if we're listening to the first new message and there are
08679           * also urgent messages, then prompt for navigation to the
08680           * previous message
08681           */
08682          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08683             res = ast_play_and_wait(chan, "vm-prev");
08684          }
08685          if (!res && !skipadvanced)
08686             res = ast_play_and_wait(chan, "vm-advopts");
08687          if (!res)
08688             res = ast_play_and_wait(chan, "vm-repeat");
08689          /* Logic:
08690           * If we're not listening to the last message OR
08691           * we're listening to the last urgent message and there are
08692           * also new non-urgent messages, then prompt for navigation
08693           * to the next message
08694           */
08695          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08696             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08697             res = ast_play_and_wait(chan, "vm-next");
08698          }
08699          if (!res) {
08700             if (!vms->deleted[vms->curmsg])
08701                res = ast_play_and_wait(chan, "vm-delete");
08702             else
08703                res = ast_play_and_wait(chan, "vm-undelete");
08704             if (!res)
08705                res = ast_play_and_wait(chan, "vm-toforward");
08706             if (!res)
08707                res = ast_play_and_wait(chan, "vm-savemessage");
08708          }
08709       }
08710       if (!res) {
08711          res = ast_play_and_wait(chan, "vm-helpexit");
08712       }
08713       if (!res)
08714          res = ast_waitfordigit(chan, 6000);
08715       if (!res) {
08716          vms->repeats++;
08717          if (vms->repeats > 2) {
08718             res = 't';
08719          }
08720       }
08721    }
08722    return res;
08723 }
08724 
08725 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08726 {
08727    int res = 0;
08728    /* Play instructions and wait for new command */
08729    while (!res) {
08730       if (vms->lastmsg > -1) {
08731          res = ast_play_and_wait(chan, "vm-listen");
08732          if (!res)
08733             res = vm_play_folder_name(chan, vms->vmbox);
08734          if (!res)
08735             res = ast_play_and_wait(chan, "press");
08736          if (!res)
08737             res = ast_play_and_wait(chan, "digits/1");
08738       }
08739       if (!res)
08740          res = ast_play_and_wait(chan, "vm-opts");
08741       if (!res) {
08742          vms->starting = 0;
08743          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08744       }
08745    }
08746    return res;
08747 }
08748 
08749 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08750 {
08751    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08752       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08753    } else {             /* Default to ENGLISH */
08754       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08755    }
08756 }
08757 
08758 
08759 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08760 {
08761    int cmd = 0;
08762    int duration = 0;
08763    int tries = 0;
08764    char newpassword[80] = "";
08765    char newpassword2[80] = "";
08766    char prefile[PATH_MAX] = "";
08767    unsigned char buf[256];
08768    int bytes=0;
08769 
08770    if (ast_adsi_available(chan)) {
08771       bytes += adsi_logo(buf + bytes);
08772       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08773       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08774       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08775       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08776       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08777    }
08778 
08779    /* First, have the user change their password 
08780       so they won't get here again */
08781    for (;;) {
08782       newpassword[1] = '\0';
08783       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08784       if (cmd == '#')
08785          newpassword[0] = '\0';
08786       if (cmd < 0 || cmd == 't' || cmd == '#')
08787          return cmd;
08788       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08789       if (cmd < 0 || cmd == 't' || cmd == '#')
08790          return cmd;
08791       cmd = check_password(vmu, newpassword); /* perform password validation */
08792       if (cmd != 0) {
08793          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08794          cmd = ast_play_and_wait(chan, vm_invalid_password);
08795       } else {
08796          newpassword2[1] = '\0';
08797          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08798          if (cmd == '#')
08799             newpassword2[0] = '\0';
08800          if (cmd < 0 || cmd == 't' || cmd == '#')
08801             return cmd;
08802          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08803          if (cmd < 0 || cmd == 't' || cmd == '#')
08804             return cmd;
08805          if (!strcmp(newpassword, newpassword2))
08806             break;
08807          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08808          cmd = ast_play_and_wait(chan, vm_mismatch);
08809       }
08810       if (++tries == 3)
08811          return -1;
08812       if (cmd != 0) {
08813          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08814       }
08815    }
08816    if (pwdchange & PWDCHANGE_INTERNAL)
08817       vm_change_password(vmu, newpassword);
08818    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08819       vm_change_password_shell(vmu, newpassword);
08820 
08821    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08822    cmd = ast_play_and_wait(chan, vm_passchanged);
08823 
08824    /* If forcename is set, have the user record their name */  
08825    if (ast_test_flag(vmu, VM_FORCENAME)) {
08826       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08827       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08828          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08829          if (cmd < 0 || cmd == 't' || cmd == '#')
08830             return cmd;
08831       }
08832    }
08833 
08834    /* If forcegreetings is set, have the user record their greetings */
08835    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08836       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08837       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08838          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08839          if (cmd < 0 || cmd == 't' || cmd == '#')
08840             return cmd;
08841       }
08842 
08843       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08844       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08845          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08846          if (cmd < 0 || cmd == 't' || cmd == '#')
08847             return cmd;
08848       }
08849    }
08850 
08851    return cmd;
08852 }
08853 
08854 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08855 {
08856    int cmd = 0;
08857    int retries = 0;
08858    int duration = 0;
08859    char newpassword[80] = "";
08860    char newpassword2[80] = "";
08861    char prefile[PATH_MAX] = "";
08862    unsigned char buf[256];
08863    int bytes=0;
08864 
08865    if (ast_adsi_available(chan)) {
08866       bytes += adsi_logo(buf + bytes);
08867       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08868       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08869       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08870       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08871       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08872    }
08873    while ((cmd >= 0) && (cmd != 't')) {
08874       if (cmd)
08875          retries = 0;
08876       switch (cmd) {
08877       case '1': /* Record your unavailable message */
08878          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08879          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08880          break;
08881       case '2':  /* Record your busy message */
08882          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08883          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08884          break;
08885       case '3': /* Record greeting */
08886          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08887          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08888          break;
08889       case '4':  /* manage the temporary greeting */
08890          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08891          break;
08892       case '5': /* change password */
08893          if (vmu->password[0] == '-') {
08894             cmd = ast_play_and_wait(chan, "vm-no");
08895             break;
08896          }
08897          newpassword[1] = '\0';
08898          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08899          if (cmd == '#')
08900             newpassword[0] = '\0';
08901          else {
08902             if (cmd < 0)
08903                break;
08904             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08905                break;
08906             }
08907          }
08908          cmd = check_password(vmu, newpassword); /* perform password validation */
08909          if (cmd != 0) {
08910             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08911             cmd = ast_play_and_wait(chan, vm_invalid_password);
08912             if (!cmd) {
08913                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08914             }
08915             break;
08916          }
08917          newpassword2[1] = '\0';
08918          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08919          if (cmd == '#')
08920             newpassword2[0] = '\0';
08921          else {
08922             if (cmd < 0)
08923                break;
08924 
08925             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08926                break;
08927             }
08928          }
08929          if (strcmp(newpassword, newpassword2)) {
08930             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08931             cmd = ast_play_and_wait(chan, vm_mismatch);
08932             if (!cmd) {
08933                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08934             }
08935             break;
08936          }
08937          if (pwdchange & PWDCHANGE_INTERNAL)
08938             vm_change_password(vmu, newpassword);
08939          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08940             vm_change_password_shell(vmu, newpassword);
08941 
08942          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08943          cmd = ast_play_and_wait(chan, vm_passchanged);
08944          break;
08945       case '*': 
08946          cmd = 't';
08947          break;
08948       default: 
08949          cmd = 0;
08950          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08951          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08952          if (ast_fileexists(prefile, NULL, NULL)) {
08953             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08954          }
08955          DISPOSE(prefile, -1);
08956          if (!cmd) {
08957             cmd = ast_play_and_wait(chan, "vm-options");
08958          }
08959          if (!cmd) {
08960             cmd = ast_waitfordigit(chan,6000);
08961          }
08962          if (!cmd) {
08963             retries++;
08964          }
08965          if (retries > 3) {
08966             cmd = 't';
08967          }
08968       }
08969    }
08970    if (cmd == 't')
08971       cmd = 0;
08972    return cmd;
08973 }
08974 
08975 /*!
08976  * \brief The handler for 'record a temporary greeting'. 
08977  * \param chan
08978  * \param vmu
08979  * \param vms
08980  * \param fmtc
08981  * \param record_gain
08982  *
08983  * This is option 4 from the mailbox options menu.
08984  * This function manages the following promptings:
08985  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08986  * 2: remove (delete) the temporary greeting.
08987  * *: return to the main menu.
08988  *
08989  * \return zero on success, -1 on error.
08990  */
08991 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08992 {
08993    int cmd = 0;
08994    int retries = 0;
08995    int duration = 0;
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, "Temp Greeting Menu", "");
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    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09010    while ((cmd >= 0) && (cmd != 't')) {
09011       if (cmd)
09012          retries = 0;
09013       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09014       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09015          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09016          cmd = 't';  
09017       } else {
09018          switch (cmd) {
09019          case '1':
09020             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09021             break;
09022          case '2':
09023             DELETE(prefile, -1, prefile, vmu);
09024             ast_play_and_wait(chan, "vm-tempremoved");
09025             cmd = 't';  
09026             break;
09027          case '*': 
09028             cmd = 't';
09029             break;
09030          default:
09031             cmd = ast_play_and_wait(chan,
09032                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09033                   "vm-tempgreeting2" : "vm-tempgreeting");
09034             if (!cmd)
09035                cmd = ast_waitfordigit(chan,6000);
09036             if (!cmd)
09037                retries++;
09038             if (retries > 3)
09039                cmd = 't';
09040          }
09041       }
09042       DISPOSE(prefile, -1);
09043    }
09044    if (cmd == 't')
09045       cmd = 0;
09046    return cmd;
09047 }
09048 
09049 /*!
09050  * \brief Greek syntax for 'You have N messages' greeting.
09051  * \param chan
09052  * \param vms
09053  * \param vmu
09054  *
09055  * \return zero on success, -1 on error.
09056  */   
09057 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09058 {
09059    int cmd=0;
09060 
09061    if (vms->lastmsg > -1) {
09062       cmd = play_message(chan, vmu, vms);
09063    } else {
09064       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09065       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09066          if (!cmd) {
09067             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09068             cmd = ast_play_and_wait(chan, vms->fn);
09069          }
09070          if (!cmd)
09071             cmd = ast_play_and_wait(chan, "vm-messages");
09072       } else {
09073          if (!cmd)
09074             cmd = ast_play_and_wait(chan, "vm-messages");
09075          if (!cmd) {
09076             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09077             cmd = ast_play_and_wait(chan, vms->fn);
09078          }
09079       }
09080    } 
09081    return cmd;
09082 }
09083 
09084 /* Hebrew Syntax */
09085 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09086 {
09087    int cmd = 0;
09088 
09089    if (vms->lastmsg > -1) {
09090       cmd = play_message(chan, vmu, vms);
09091    } else {
09092       if (!strcasecmp(vms->fn, "INBOX")) {
09093          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09094       } else {
09095          cmd = ast_play_and_wait(chan, "vm-nomessages");
09096       }
09097    }
09098    return cmd;
09099 }
09100 
09101 /*! 
09102  * \brief Default English syntax for 'You have N messages' greeting.
09103  * \param chan
09104  * \param vms
09105  * \param vmu
09106  *
09107  * \return zero on success, -1 on error.
09108  */
09109 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09110 {
09111    int cmd=0;
09112 
09113    if (vms->lastmsg > -1) {
09114       cmd = play_message(chan, vmu, vms);
09115    } else {
09116       cmd = ast_play_and_wait(chan, "vm-youhave");
09117       if (!cmd) 
09118          cmd = ast_play_and_wait(chan, "vm-no");
09119       if (!cmd) {
09120          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09121          cmd = ast_play_and_wait(chan, vms->fn);
09122       }
09123       if (!cmd)
09124          cmd = ast_play_and_wait(chan, "vm-messages");
09125    }
09126    return cmd;
09127 }
09128 
09129 /*! 
09130  *\brief Italian syntax for 'You have N messages' greeting.
09131  * \param chan
09132  * \param vms
09133  * \param vmu
09134  *
09135  * \return zero on success, -1 on error.
09136  */
09137 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09138 {
09139    int cmd=0;
09140 
09141    if (vms->lastmsg > -1) {
09142       cmd = play_message(chan, vmu, vms);
09143    } else {
09144       cmd = ast_play_and_wait(chan, "vm-no");
09145       if (!cmd)
09146          cmd = ast_play_and_wait(chan, "vm-message");
09147       if (!cmd) {
09148          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09149          cmd = ast_play_and_wait(chan, vms->fn);
09150       }
09151    }
09152    return cmd;
09153 }
09154 
09155 /*! 
09156  * \brief Spanish syntax for 'You have N messages' greeting.
09157  * \param chan
09158  * \param vms
09159  * \param vmu
09160  *
09161  * \return zero on success, -1 on error.
09162  */
09163 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09164 {
09165    int cmd=0;
09166 
09167    if (vms->lastmsg > -1) {
09168       cmd = play_message(chan, vmu, vms);
09169    } else {
09170       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09171       if (!cmd)
09172          cmd = ast_play_and_wait(chan, "vm-messages");
09173       if (!cmd) {
09174          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09175          cmd = ast_play_and_wait(chan, vms->fn);
09176       }
09177    }
09178    return cmd;
09179 }
09180 
09181 /*! 
09182  * \brief Portuguese syntax for 'You have N messages' greeting.
09183  * \param chan
09184  * \param vms
09185  * \param vmu
09186  *
09187  * \return zero on success, -1 on error.
09188  */
09189 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09190 {
09191    int cmd=0;
09192 
09193    if (vms->lastmsg > -1) {
09194       cmd = play_message(chan, vmu, vms);
09195    } else {
09196       cmd = ast_play_and_wait(chan, "vm-no");
09197       if (!cmd) {
09198          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09199          cmd = ast_play_and_wait(chan, vms->fn);
09200       }
09201       if (!cmd)
09202          cmd = ast_play_and_wait(chan, "vm-messages");
09203    }
09204    return cmd;
09205 }
09206 
09207 /*! 
09208  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09209  * \param chan
09210  * \param vms
09211  * \param vmu
09212  *
09213  * \return zero on success, -1 on error.
09214  */
09215 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09216 {
09217    int cmd=0;
09218 
09219    if (vms->lastmsg > -1) {
09220       cmd = play_message(chan, vmu, vms);
09221    } else {
09222       cmd = ast_play_and_wait(chan, "vm-you");
09223       if (!cmd) 
09224          cmd = ast_play_and_wait(chan, "vm-haveno");
09225       if (!cmd)
09226          cmd = ast_play_and_wait(chan, "vm-messages");
09227       if (!cmd) {
09228          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09229          cmd = ast_play_and_wait(chan, vms->fn);
09230       }
09231    }
09232    return cmd;
09233 }
09234 
09235 /*!
09236  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09237  * \param chan The channel for the current user. We read the language property from this.
09238  * \param vms passed into the language-specific vm_browse_messages function.
09239  * \param vmu passed into the language-specific vm_browse_messages function.
09240  * 
09241  * The method to be invoked is determined by the value of language code property in the user's channel.
09242  * The default (when unable to match) is to use english.
09243  *
09244  * \return zero on success, -1 on error.
09245  */
09246 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09247 {
09248    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09249       return vm_browse_messages_es(chan, vms, vmu);
09250    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09251       return vm_browse_messages_gr(chan, vms, vmu);
09252    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09253       return vm_browse_messages_he(chan, vms, vmu);
09254    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09255       return vm_browse_messages_it(chan, vms, vmu);
09256    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09257       return vm_browse_messages_pt(chan, vms, vmu);
09258    } else if (!strncasecmp(chan->language, "zh", 2)) {
09259       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
09260    } else {                                             /* Default to English syntax */
09261       return vm_browse_messages_en(chan, vms, vmu);
09262    }
09263 }
09264 
09265 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09266          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09267          int skipuser, int max_logins, int silent)
09268 {
09269    int useadsi=0, valid=0, logretries=0;
09270    char password[AST_MAX_EXTENSION]="", *passptr;
09271    struct ast_vm_user vmus, *vmu = NULL;
09272 
09273    /* If ADSI is supported, setup login screen */
09274    adsi_begin(chan, &useadsi);
09275    if (!skipuser && useadsi)
09276       adsi_login(chan);
09277    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09278       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09279       return -1;
09280    }
09281    
09282    /* Authenticate them and get their mailbox/password */
09283    
09284    while (!valid && (logretries < max_logins)) {
09285       /* Prompt for, and read in the username */
09286       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09287          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09288          return -1;
09289       }
09290       if (ast_strlen_zero(mailbox)) {
09291          if (chan->cid.cid_num) {
09292             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
09293          } else {
09294             ast_verb(3,"Username not entered\n");  
09295             return -1;
09296          }
09297       }
09298       if (useadsi)
09299          adsi_password(chan);
09300 
09301       if (!ast_strlen_zero(prefix)) {
09302          char fullusername[80] = "";
09303          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09304          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09305          ast_copy_string(mailbox, fullusername, mailbox_size);
09306       }
09307 
09308       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
09309       vmu = find_user(&vmus, context, mailbox);
09310       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09311          /* saved password is blank, so don't bother asking */
09312          password[0] = '\0';
09313       } else {
09314          if (ast_streamfile(chan, vm_password, chan->language)) {
09315             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09316             return -1;
09317          }
09318          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09319             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09320             return -1;
09321          }
09322       }
09323 
09324       if (vmu) {
09325          passptr = vmu->password;
09326          if (passptr[0] == '-') passptr++;
09327       }
09328       if (vmu && !strcmp(passptr, password))
09329          valid++;
09330       else {
09331          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09332          if (!ast_strlen_zero(prefix))
09333             mailbox[0] = '\0';
09334       }
09335       logretries++;
09336       if (!valid) {
09337          if (skipuser || logretries >= max_logins) {
09338             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09339                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09340                return -1;
09341             }
09342          } else {
09343             if (useadsi)
09344                adsi_login(chan);
09345             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09346                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09347                return -1;
09348             }
09349          }
09350          if (ast_waitstream(chan, "")) /* Channel is hung up */
09351             return -1;
09352       }
09353    }
09354    if (!valid && (logretries >= max_logins)) {
09355       ast_stopstream(chan);
09356       ast_play_and_wait(chan, "vm-goodbye");
09357       return -1;
09358    }
09359    if (vmu && !skipuser) {
09360       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09361    }
09362    return 0;
09363 }
09364 
09365 static int vm_execmain(struct ast_channel *chan, void *data)
09366 {
09367    /* XXX This is, admittedly, some pretty horrendous code.  For some
09368       reason it just seemed a lot easier to do with GOTO's.  I feel
09369       like I'm back in my GWBASIC days. XXX */
09370    int res=-1;
09371    int cmd=0;
09372    int valid = 0;
09373    char prefixstr[80] ="";
09374    char ext_context[256]="";
09375    int box;
09376    int useadsi = 0;
09377    int skipuser = 0;
09378    struct vm_state vms;
09379    struct ast_vm_user *vmu = NULL, vmus;
09380    char *context=NULL;
09381    int silentexit = 0;
09382    struct ast_flags flags = { 0 };
09383    signed char record_gain = 0;
09384    int play_auto = 0;
09385    int play_folder = 0;
09386    int in_urgent = 0;
09387 #ifdef IMAP_STORAGE
09388    int deleted = 0;
09389 #endif
09390 
09391    /* Add the vm_state to the active list and keep it active */
09392    memset(&vms, 0, sizeof(vms));
09393 
09394    vms.lastmsg = -1;
09395 
09396    memset(&vmus, 0, sizeof(vmus));
09397 
09398    if (chan->_state != AST_STATE_UP) {
09399       ast_debug(1, "Before ast_answer\n");
09400       ast_answer(chan);
09401    }
09402 
09403    if (!ast_strlen_zero(data)) {
09404       char *opts[OPT_ARG_ARRAY_SIZE];
09405       char *parse;
09406       AST_DECLARE_APP_ARGS(args,
09407          AST_APP_ARG(argv0);
09408          AST_APP_ARG(argv1);
09409       );
09410 
09411       parse = ast_strdupa(data);
09412 
09413       AST_STANDARD_APP_ARGS(args, parse);
09414 
09415       if (args.argc == 2) {
09416          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09417             return -1;
09418          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09419             int gain;
09420             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09421                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09422                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09423                   return -1;
09424                } else {
09425                   record_gain = (signed char) gain;
09426                }
09427             } else {
09428                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09429             }
09430          }
09431          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09432             play_auto = 1;
09433             if (opts[OPT_ARG_PLAYFOLDER]) {
09434                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09435                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09436                }
09437             } else {
09438                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09439             }  
09440             if ( play_folder > 9 || play_folder < 0) {
09441                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09442                play_folder = 0;
09443             }
09444          }
09445       } else {
09446          /* old style options parsing */
09447          while (*(args.argv0)) {
09448             if (*(args.argv0) == 's')
09449                ast_set_flag(&flags, OPT_SILENT);
09450             else if (*(args.argv0) == 'p')
09451                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09452             else 
09453                break;
09454             (args.argv0)++;
09455          }
09456 
09457       }
09458 
09459       valid = ast_test_flag(&flags, OPT_SILENT);
09460 
09461       if ((context = strchr(args.argv0, '@')))
09462          *context++ = '\0';
09463 
09464       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09465          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09466       else
09467          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09468 
09469       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09470          skipuser++;
09471       else
09472          valid = 0;
09473    }
09474 
09475    if (!valid)
09476       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09477 
09478    ast_debug(1, "After vm_authenticate\n");
09479    if (!res) {
09480       valid = 1;
09481       if (!skipuser)
09482          vmu = &vmus;
09483    } else {
09484       res = 0;
09485    }
09486 
09487    /* If ADSI is supported, setup login screen */
09488    adsi_begin(chan, &useadsi);
09489 
09490    if (!valid) {
09491       goto out;
09492    }
09493 
09494 #ifdef IMAP_STORAGE
09495    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09496    pthread_setspecific(ts_vmstate.key, &vms);
09497 
09498    vms.interactive = 1;
09499    vms.updated = 1;
09500    if (vmu)
09501       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09502    vmstate_insert(&vms);
09503    init_vm_state(&vms);
09504 #endif
09505    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09506       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09507       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09508       return -1;
09509    }
09510    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09511       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09512       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09513       return -1;
09514    }
09515    
09516    /* Set language from config to override channel language */
09517    if (!ast_strlen_zero(vmu->language))
09518       ast_string_field_set(chan, language, vmu->language);
09519 
09520    /* Retrieve urgent, old and new message counts */
09521    ast_debug(1, "Before open_mailbox\n");
09522    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09523    if (res < 0)
09524       goto out;
09525    vms.oldmessages = vms.lastmsg + 1;
09526    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09527    /* check INBOX */
09528    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09529    if (res < 0)
09530       goto out;
09531    vms.newmessages = vms.lastmsg + 1;
09532    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09533    /* Start in Urgent */
09534    in_urgent = 1;
09535    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09536    if (res < 0)
09537       goto out;
09538    vms.urgentmessages = vms.lastmsg + 1;
09539    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09540 
09541    /* Select proper mailbox FIRST!! */
09542    if (play_auto) {
09543       if (vms.urgentmessages) {
09544          in_urgent = 1;
09545          res = open_mailbox(&vms, vmu, 11);
09546       } else {
09547          in_urgent = 0;
09548          res = open_mailbox(&vms, vmu, play_folder);
09549       }
09550       if (res < 0)
09551          goto out;
09552 
09553       /* If there are no new messages, inform the user and hangup */
09554       if (vms.lastmsg == -1) {
09555          in_urgent = 0;
09556          cmd = vm_browse_messages(chan, &vms, vmu);
09557          res = 0;
09558          goto out;
09559       }
09560    } else {
09561       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09562          /* If we only have old messages start here */
09563          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09564          in_urgent = 0;
09565          play_folder = 1;
09566          if (res < 0)
09567             goto out;
09568       } else if (!vms.urgentmessages && vms.newmessages) {
09569          /* If we have new messages but none are urgent */
09570          in_urgent = 0;
09571          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09572          if (res < 0)
09573             goto out;
09574       }
09575    }
09576 
09577    if (useadsi)
09578       adsi_status(chan, &vms);
09579    res = 0;
09580 
09581    /* Check to see if this is a new user */
09582    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09583       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09584       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09585          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09586       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09587       if ((cmd == 't') || (cmd == '#')) {
09588          /* Timeout */
09589          res = 0;
09590          goto out;
09591       } else if (cmd < 0) {
09592          /* Hangup */
09593          res = -1;
09594          goto out;
09595       }
09596    }
09597 #ifdef IMAP_STORAGE
09598       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09599       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09600          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09601          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09602       }
09603       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09604       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09605          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09606          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09607       }
09608 #endif
09609    if (play_auto) {
09610       cmd = '1';
09611    } else {
09612       cmd = vm_intro(chan, vmu, &vms);
09613    }
09614 
09615    vms.repeats = 0;
09616    vms.starting = 1;
09617    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09618       /* Run main menu */
09619       switch (cmd) {
09620       case '1': /* First message */
09621          vms.curmsg = 0;
09622          /* Fall through */
09623       case '5': /* Play current message */
09624          cmd = vm_browse_messages(chan, &vms, vmu);
09625          break;
09626       case '2': /* Change folders */
09627          if (useadsi)
09628             adsi_folders(chan, 0, "Change to folder...");
09629          cmd = get_folder2(chan, "vm-changeto", 0);
09630          if (cmd == '#') {
09631             cmd = 0;
09632          } else if (cmd > 0) {
09633             cmd = cmd - '0';
09634             res = close_mailbox(&vms, vmu);
09635             if (res == ERROR_LOCK_PATH)
09636                goto out;
09637             /* If folder is not urgent, set in_urgent to zero! */
09638             if (cmd != 11) in_urgent = 0;
09639             res = open_mailbox(&vms, vmu, cmd);
09640             if (res < 0)
09641                goto out;
09642             play_folder = cmd;
09643             cmd = 0;
09644          }
09645          if (useadsi)
09646             adsi_status2(chan, &vms);
09647             
09648          if (!cmd)
09649             cmd = vm_play_folder_name(chan, vms.vmbox);
09650 
09651          vms.starting = 1;
09652          break;
09653       case '3': /* Advanced options */
09654          cmd = 0;
09655          vms.repeats = 0;
09656          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09657             switch (cmd) {
09658             case '1': /* Reply */
09659                if (vms.lastmsg > -1 && !vms.starting) {
09660                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09661                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09662                      res = cmd;
09663                      goto out;
09664                   }
09665                } else
09666                   cmd = ast_play_and_wait(chan, "vm-sorry");
09667                cmd = 't';
09668                break;
09669             case '2': /* Callback */
09670                if (!vms.starting)
09671                   ast_verb(3, "Callback Requested\n");
09672                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09673                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09674                   if (cmd == 9) {
09675                      silentexit = 1;
09676                      goto out;
09677                   } else if (cmd == ERROR_LOCK_PATH) {
09678                      res = cmd;
09679                      goto out;
09680                   }
09681                } else 
09682                   cmd = ast_play_and_wait(chan, "vm-sorry");
09683                cmd = 't';
09684                break;
09685             case '3': /* Envelope */
09686                if (vms.lastmsg > -1 && !vms.starting) {
09687                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09688                   if (cmd == ERROR_LOCK_PATH) {
09689                      res = cmd;
09690                      goto out;
09691                   }
09692                } else
09693                   cmd = ast_play_and_wait(chan, "vm-sorry");
09694                cmd = 't';
09695                break;
09696             case '4': /* Dialout */
09697                if (!ast_strlen_zero(vmu->dialout)) {
09698                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09699                   if (cmd == 9) {
09700                      silentexit = 1;
09701                      goto out;
09702                   }
09703                } else 
09704                   cmd = ast_play_and_wait(chan, "vm-sorry");
09705                cmd = 't';
09706                break;
09707 
09708             case '5': /* Leave VoiceMail */
09709                if (ast_test_flag(vmu, VM_SVMAIL)) {
09710                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09711                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09712                      res = cmd;
09713                      goto out;
09714                   }
09715                } else
09716                   cmd = ast_play_and_wait(chan,"vm-sorry");
09717                cmd='t';
09718                break;
09719                
09720             case '*': /* Return to main menu */
09721                cmd = 't';
09722                break;
09723 
09724             default:
09725                cmd = 0;
09726                if (!vms.starting) {
09727                   cmd = ast_play_and_wait(chan, "vm-toreply");
09728                }
09729                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09730                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09731                }
09732                if (!cmd && !vms.starting) {
09733                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09734                }
09735                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09736                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09737                }
09738                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09739                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09740                if (!cmd)
09741                   cmd = ast_play_and_wait(chan, "vm-starmain");
09742                if (!cmd)
09743                   cmd = ast_waitfordigit(chan,6000);
09744                if (!cmd)
09745                   vms.repeats++;
09746                if (vms.repeats > 3)
09747                   cmd = 't';
09748             }
09749          }
09750          if (cmd == 't') {
09751             cmd = 0;
09752             vms.repeats = 0;
09753          }
09754          break;
09755       case '4': /* Go to the previous message */
09756          if (vms.curmsg > 0) {
09757             vms.curmsg--;
09758             cmd = play_message(chan, vmu, &vms);
09759          } else {
09760             /* Check if we were listening to new
09761                messages.  If so, go to Urgent messages
09762                instead of saying "no more messages"
09763             */
09764             if (in_urgent == 0 && vms.urgentmessages > 0) {
09765                /* Check for Urgent messages */
09766                in_urgent = 1;
09767                res = close_mailbox(&vms, vmu);
09768                if (res == ERROR_LOCK_PATH)
09769                   goto out;
09770                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09771                if (res < 0)
09772                   goto out;
09773                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09774                vms.curmsg = vms.lastmsg;
09775                if (vms.lastmsg < 0)
09776                   cmd = ast_play_and_wait(chan, "vm-nomore");
09777             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09778                vms.curmsg = vms.lastmsg;
09779                cmd = play_message(chan, vmu, &vms);
09780             } else {
09781                cmd = ast_play_and_wait(chan, "vm-nomore");
09782             }
09783          }
09784          break;
09785       case '6': /* Go to the next message */
09786          if (vms.curmsg < vms.lastmsg) {
09787             vms.curmsg++;
09788             cmd = play_message(chan, vmu, &vms);
09789          } else {
09790             if (in_urgent && vms.newmessages > 0) {
09791                /* Check if we were listening to urgent
09792                 * messages.  If so, go to regular new messages
09793                 * instead of saying "no more messages"
09794                 */
09795                in_urgent = 0;
09796                res = close_mailbox(&vms, vmu);
09797                if (res == ERROR_LOCK_PATH)
09798                   goto out;
09799                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09800                if (res < 0)
09801                   goto out;
09802                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09803                vms.curmsg = -1;
09804                if (vms.lastmsg < 0) {
09805                   cmd = ast_play_and_wait(chan, "vm-nomore");
09806                }
09807             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09808                vms.curmsg = 0;
09809                cmd = play_message(chan, vmu, &vms);
09810             } else {
09811                cmd = ast_play_and_wait(chan, "vm-nomore");
09812             }
09813          }
09814          break;
09815       case '7': /* Delete the current message */
09816          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09817             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09818             if (useadsi)
09819                adsi_delete(chan, &vms);
09820             if (vms.deleted[vms.curmsg]) {
09821                if (play_folder == 0) {
09822                   if (in_urgent) {
09823                      vms.urgentmessages--;
09824                   } else {
09825                      vms.newmessages--;
09826                   }
09827                }
09828                else if (play_folder == 1)
09829                   vms.oldmessages--;
09830                cmd = ast_play_and_wait(chan, "vm-deleted");
09831             } else {
09832                if (play_folder == 0) {
09833                   if (in_urgent) {
09834                      vms.urgentmessages++;
09835                   } else {
09836                      vms.newmessages++;
09837                   }
09838                }
09839                else if (play_folder == 1)
09840                   vms.oldmessages++;
09841                cmd = ast_play_and_wait(chan, "vm-undeleted");
09842             }
09843             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09844                if (vms.curmsg < vms.lastmsg) {
09845                   vms.curmsg++;
09846                   cmd = play_message(chan, vmu, &vms);
09847                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09848                   vms.curmsg = 0;
09849                   cmd = play_message(chan, vmu, &vms);
09850                } else {
09851                   /* Check if we were listening to urgent
09852                      messages.  If so, go to regular new messages
09853                      instead of saying "no more messages"
09854                   */
09855                   if (in_urgent == 1) {
09856                      /* Check for new messages */
09857                      in_urgent = 0;
09858                      res = close_mailbox(&vms, vmu);
09859                      if (res == ERROR_LOCK_PATH)
09860                         goto out;
09861                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09862                      if (res < 0)
09863                         goto out;
09864                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09865                      vms.curmsg = -1;
09866                      if (vms.lastmsg < 0)
09867                         cmd = ast_play_and_wait(chan, "vm-nomore");
09868                   } else {
09869                      cmd = ast_play_and_wait(chan, "vm-nomore");
09870                   }
09871                }
09872             }
09873          } else /* Delete not valid if we haven't selected a message */
09874             cmd = 0;
09875 #ifdef IMAP_STORAGE
09876          deleted = 1;
09877 #endif
09878          break;
09879    
09880       case '8': /* Forward the current messgae */
09881          if (vms.lastmsg > -1) {
09882             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09883             if (cmd == ERROR_LOCK_PATH) {
09884                res = cmd;
09885                goto out;
09886             }
09887          } else {
09888             /* Check if we were listening to urgent
09889                messages.  If so, go to regular new messages
09890                instead of saying "no more messages"
09891             */
09892             if (in_urgent == 1 && vms.newmessages > 0) {
09893                /* Check for new messages */
09894                in_urgent = 0;
09895                res = close_mailbox(&vms, vmu);
09896                if (res == ERROR_LOCK_PATH)
09897                   goto out;
09898                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09899                if (res < 0)
09900                   goto out;
09901                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09902                vms.curmsg = -1;
09903                if (vms.lastmsg < 0)
09904                   cmd = ast_play_and_wait(chan, "vm-nomore");
09905             } else {
09906                cmd = ast_play_and_wait(chan, "vm-nomore");
09907             }
09908          }
09909          break;
09910       case '9': /* Save message to folder */
09911          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09912             /* No message selected */
09913             cmd = 0;
09914             break;
09915          }
09916          if (useadsi)
09917             adsi_folders(chan, 1, "Save to folder...");
09918          cmd = get_folder2(chan, "vm-savefolder", 1);
09919          box = 0; /* Shut up compiler */
09920          if (cmd == '#') {
09921             cmd = 0;
09922             break;
09923          } else if (cmd > 0) {
09924             box = cmd = cmd - '0';
09925             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09926             if (cmd == ERROR_LOCK_PATH) {
09927                res = cmd;
09928                goto out;
09929 #ifndef IMAP_STORAGE
09930             } else if (!cmd) {
09931                vms.deleted[vms.curmsg] = 1;
09932 #endif
09933             } else {
09934                vms.deleted[vms.curmsg] = 0;
09935                vms.heard[vms.curmsg] = 0;
09936             }
09937          }
09938          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09939          if (useadsi)
09940             adsi_message(chan, &vms);
09941          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09942          if (!cmd) {
09943             cmd = ast_play_and_wait(chan, "vm-message");
09944             if (!cmd) 
09945                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09946             if (!cmd)
09947                cmd = ast_play_and_wait(chan, "vm-savedto");
09948             if (!cmd)
09949                cmd = vm_play_folder_name(chan, vms.fn);
09950          } else {
09951             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09952          }
09953          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09954             if (vms.curmsg < vms.lastmsg) {
09955                vms.curmsg++;
09956                cmd = play_message(chan, vmu, &vms);
09957             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09958                vms.curmsg = 0;
09959                cmd = play_message(chan, vmu, &vms);
09960             } else {
09961                /* Check if we were listening to urgent
09962                   messages.  If so, go to regular new messages
09963                   instead of saying "no more messages"
09964                */
09965                if (in_urgent == 1 && vms.newmessages > 0) {
09966                   /* Check for new messages */
09967                   in_urgent = 0;
09968                   res = close_mailbox(&vms, vmu);
09969                   if (res == ERROR_LOCK_PATH)
09970                      goto out;
09971                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09972                   if (res < 0)
09973                      goto out;
09974                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09975                   vms.curmsg = -1;
09976                   if (vms.lastmsg < 0)
09977                      cmd = ast_play_and_wait(chan, "vm-nomore");
09978                } else {
09979                   cmd = ast_play_and_wait(chan, "vm-nomore");
09980                }
09981             }
09982          }
09983          break;
09984       case '*': /* Help */
09985          if (!vms.starting) {
09986             cmd = ast_play_and_wait(chan, "vm-onefor");
09987             if (!strncasecmp(chan->language, "he", 2)) {
09988                cmd = ast_play_and_wait(chan, "vm-for");
09989             }
09990             if (!cmd)
09991                cmd = vm_play_folder_name(chan, vms.vmbox);
09992             if (!cmd)
09993                cmd = ast_play_and_wait(chan, "vm-opts");
09994             if (!cmd)
09995                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09996          } else
09997             cmd = 0;
09998          break;
09999       case '0': /* Mailbox options */
10000          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10001          if (useadsi)
10002             adsi_status(chan, &vms);
10003          break;
10004       default: /* Nothing */
10005          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10006          break;
10007       }
10008    }
10009    if ((cmd == 't') || (cmd == '#')) {
10010       /* Timeout */
10011       res = 0;
10012    } else {
10013       /* Hangup */
10014       res = -1;
10015    }
10016 
10017 out:
10018    if (res > -1) {
10019       ast_stopstream(chan);
10020       adsi_goodbye(chan);
10021       if (valid && res != OPERATOR_EXIT) {
10022          if (silentexit)
10023             res = ast_play_and_wait(chan, "vm-dialout");
10024          else 
10025             res = ast_play_and_wait(chan, "vm-goodbye");
10026       }
10027       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10028          res = 0;
10029       }
10030       if (useadsi)
10031          ast_adsi_unload_session(chan);
10032    }
10033    if (vmu)
10034       close_mailbox(&vms, vmu);
10035    if (valid) {
10036       int new = 0, old = 0, urgent = 0;
10037       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10038       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10039       /* Urgent flag not passwd to externnotify here */
10040       run_externnotify(vmu->context, vmu->mailbox, NULL);
10041       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10042       queue_mwi_event(ext_context, urgent, new, old);
10043    }
10044 #ifdef IMAP_STORAGE
10045    /* expunge message - use UID Expunge if supported on IMAP server*/
10046    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
10047    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10048       ast_mutex_lock(&vms.lock);
10049 #ifdef HAVE_IMAP_TK2006
10050       if (LEVELUIDPLUS (vms.mailstream)) {
10051          mail_expunge_full(vms.mailstream,NIL,EX_UID);
10052       } else 
10053 #endif
10054          mail_expunge(vms.mailstream);
10055       ast_mutex_unlock(&vms.lock);
10056    }
10057    /*  before we delete the state, we should copy pertinent info
10058     *  back to the persistent model */
10059    if (vmu) {
10060       vmstate_delete(&vms);
10061    }
10062 #endif
10063    if (vmu)
10064       free_user(vmu);
10065    if (vms.deleted)
10066       ast_free(vms.deleted);
10067    if (vms.heard)
10068       ast_free(vms.heard);
10069 
10070 #ifdef IMAP_STORAGE
10071    pthread_setspecific(ts_vmstate.key, NULL);
10072 #endif
10073    return res;
10074 }
10075 
10076 static int vm_exec(struct ast_channel *chan, void *data)
10077 {
10078    int res = 0;
10079    char *tmp;
10080    struct leave_vm_options leave_options;
10081    struct ast_flags flags = { 0 };
10082    char *opts[OPT_ARG_ARRAY_SIZE];
10083    AST_DECLARE_APP_ARGS(args,
10084       AST_APP_ARG(argv0);
10085       AST_APP_ARG(argv1);
10086    );
10087    
10088    memset(&leave_options, 0, sizeof(leave_options));
10089 
10090    if (chan->_state != AST_STATE_UP)
10091       ast_answer(chan);
10092 
10093    if (!ast_strlen_zero(data)) {
10094       tmp = ast_strdupa(data);
10095       AST_STANDARD_APP_ARGS(args, tmp);
10096       if (args.argc == 2) {
10097          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10098             return -1;
10099          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10100          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10101             int gain;
10102 
10103             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10104                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10105                return -1;
10106             } else {
10107                leave_options.record_gain = (signed char) gain;
10108             }
10109          }
10110          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10111             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10112                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10113          }
10114       }
10115    } else {
10116       char temp[256];
10117       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10118       if (res < 0)
10119          return res;
10120       if (ast_strlen_zero(temp))
10121          return 0;
10122       args.argv0 = ast_strdupa(temp);
10123    }
10124 
10125    res = leave_voicemail(chan, args.argv0, &leave_options);
10126    if (res == 't') {
10127       ast_play_and_wait(chan, "vm-goodbye");
10128       res = 0;
10129    }
10130 
10131    if (res == OPERATOR_EXIT) {
10132       res = 0;
10133    }
10134 
10135    if (res == ERROR_LOCK_PATH) {
10136       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10137       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10138       res = 0;
10139    }
10140 
10141    return res;
10142 }
10143 
10144 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10145 {
10146    struct ast_vm_user *vmu;
10147 
10148    AST_LIST_TRAVERSE(&users, vmu, list) {
10149       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10150          if (strcasecmp(vmu->context, context)) {
10151             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10152                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10153                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10154                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10155          }
10156          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10157          return NULL;
10158       }
10159       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10160          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10161          return NULL;
10162       }
10163    }
10164    
10165    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10166       return NULL;
10167    
10168    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10169    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10170 
10171    AST_LIST_INSERT_TAIL(&users, vmu, list);
10172    
10173    return vmu;
10174 }
10175 
10176 static int append_mailbox(const char *context, const char *box, const char *data)
10177 {
10178    /* Assumes lock is already held */
10179    char *tmp;
10180    char *stringp;
10181    char *s;
10182    struct ast_vm_user *vmu;
10183    char *mailbox_full;
10184    int new = 0, old = 0, urgent = 0;
10185 
10186    tmp = ast_strdupa(data);
10187 
10188    if (!(vmu = find_or_create(context, box)))
10189       return -1;
10190    
10191    populate_defaults(vmu);
10192 
10193    stringp = tmp;
10194    if ((s = strsep(&stringp, ","))) 
10195       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10196    if (stringp && (s = strsep(&stringp, ","))) 
10197       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10198    if (stringp && (s = strsep(&stringp, ","))) 
10199       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10200    if (stringp && (s = strsep(&stringp, ","))) 
10201       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10202    if (stringp && (s = strsep(&stringp, ","))) 
10203       apply_options(vmu, s);
10204 
10205    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10206    strcpy(mailbox_full, box);
10207    strcat(mailbox_full, "@");
10208    strcat(mailbox_full, context);
10209 
10210    inboxcount2(mailbox_full, &urgent, &new, &old);
10211    queue_mwi_event(mailbox_full, urgent, new, old);
10212 
10213    return 0;
10214 }
10215 
10216 static int vm_box_exists(struct ast_channel *chan, void *data) 
10217 {
10218    struct ast_vm_user svm;
10219    char *context, *box;
10220    AST_DECLARE_APP_ARGS(args,
10221       AST_APP_ARG(mbox);
10222       AST_APP_ARG(options);
10223    );
10224    static int dep_warning = 0;
10225 
10226    if (ast_strlen_zero(data)) {
10227       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10228       return -1;
10229    }
10230 
10231    if (!dep_warning) {
10232       dep_warning = 1;
10233       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
10234    }
10235 
10236    box = ast_strdupa(data);
10237 
10238    AST_STANDARD_APP_ARGS(args, box);
10239 
10240    if (args.options) {
10241    }
10242 
10243    if ((context = strchr(args.mbox, '@'))) {
10244       *context = '\0';
10245       context++;
10246    }
10247 
10248    if (find_user(&svm, context, args.mbox)) {
10249       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10250    } else
10251       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10252 
10253    return 0;
10254 }
10255 
10256 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10257 {
10258    struct ast_vm_user svm;
10259    AST_DECLARE_APP_ARGS(arg,
10260       AST_APP_ARG(mbox);
10261       AST_APP_ARG(context);
10262    );
10263 
10264    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10265 
10266    if (ast_strlen_zero(arg.mbox)) {
10267       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10268       return -1;
10269    }
10270 
10271    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10272    return 0;
10273 }
10274 
10275 static struct ast_custom_function mailbox_exists_acf = {
10276    .name = "MAILBOX_EXISTS",
10277    .read = acf_mailbox_exists,
10278 };
10279 
10280 static int vmauthenticate(struct ast_channel *chan, void *data)
10281 {
10282    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
10283    struct ast_vm_user vmus;
10284    char *options = NULL;
10285    int silent = 0, skipuser = 0;
10286    int res = -1;
10287    
10288    if (s) {
10289       s = ast_strdupa(s);
10290       user = strsep(&s, ",");
10291       options = strsep(&s, ",");
10292       if (user) {
10293          s = user;
10294          user = strsep(&s, "@");
10295          context = strsep(&s, "");
10296          if (!ast_strlen_zero(user))
10297             skipuser++;
10298          ast_copy_string(mailbox, user, sizeof(mailbox));
10299       }
10300    }
10301 
10302    if (options) {
10303       silent = (strchr(options, 's')) != NULL;
10304    }
10305 
10306    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10307       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10308       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10309       ast_play_and_wait(chan, "auth-thankyou");
10310       res = 0;
10311    }
10312 
10313    return res;
10314 }
10315 
10316 static char *show_users_realtime(int fd, const char *context)
10317 {
10318    struct ast_config *cfg;
10319    const char *cat = NULL;
10320 
10321    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10322       "context", context, SENTINEL))) {
10323       return CLI_FAILURE;
10324    }
10325 
10326    ast_cli(fd,
10327       "\n"
10328       "=============================================================\n"
10329       "=== Configured Voicemail Users ==============================\n"
10330       "=============================================================\n"
10331       "===\n");
10332 
10333    while ((cat = ast_category_browse(cfg, cat))) {
10334       struct ast_variable *var = NULL;
10335       ast_cli(fd,
10336          "=== Mailbox ...\n"
10337          "===\n");
10338       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10339          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10340       ast_cli(fd,
10341          "===\n"
10342          "=== ---------------------------------------------------------\n"
10343          "===\n");
10344    }
10345 
10346    ast_cli(fd,
10347       "=============================================================\n"
10348       "\n");
10349 
10350    ast_config_destroy(cfg);
10351 
10352    return CLI_SUCCESS;
10353 }
10354 
10355 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10356 {
10357    int which = 0;
10358    int wordlen;
10359    struct ast_vm_user *vmu;
10360    const char *context = "";
10361 
10362    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10363    if (pos > 4)
10364       return NULL;
10365    if (pos == 3)
10366       return (state == 0) ? ast_strdup("for") : NULL;
10367    wordlen = strlen(word);
10368    AST_LIST_TRAVERSE(&users, vmu, list) {
10369       if (!strncasecmp(word, vmu->context, wordlen)) {
10370          if (context && strcmp(context, vmu->context) && ++which > state)
10371             return ast_strdup(vmu->context);
10372          /* ignore repeated contexts ? */
10373          context = vmu->context;
10374       }
10375    }
10376    return NULL;
10377 }
10378 
10379 /*! \brief Show a list of voicemail users in the CLI */
10380 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10381 {
10382    struct ast_vm_user *vmu;
10383 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10384    const char *context = NULL;
10385    int users_counter = 0;
10386 
10387    switch (cmd) {
10388    case CLI_INIT:
10389       e->command = "voicemail show users";
10390       e->usage =
10391          "Usage: voicemail show users [for <context>]\n"
10392          "       Lists all mailboxes currently set up\n";
10393       return NULL;
10394    case CLI_GENERATE:
10395       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10396    }  
10397 
10398    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10399       return CLI_SHOWUSAGE;
10400    if (a->argc == 5) {
10401       if (strcmp(a->argv[3],"for"))
10402          return CLI_SHOWUSAGE;
10403       context = a->argv[4];
10404    }
10405 
10406    if (ast_check_realtime("voicemail")) {
10407       if (!context) {
10408          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10409          return CLI_SHOWUSAGE;
10410       }
10411       return show_users_realtime(a->fd, context);
10412    }
10413 
10414    AST_LIST_LOCK(&users);
10415    if (AST_LIST_EMPTY(&users)) {
10416       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10417       AST_LIST_UNLOCK(&users);
10418       return CLI_FAILURE;
10419    }
10420    if (a->argc == 3)
10421       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10422    else {
10423       int count = 0;
10424       AST_LIST_TRAVERSE(&users, vmu, list) {
10425          if (!strcmp(context, vmu->context))
10426             count++;
10427       }
10428       if (count) {
10429          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10430       } else {
10431          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10432          AST_LIST_UNLOCK(&users);
10433          return CLI_FAILURE;
10434       }
10435    }
10436    AST_LIST_TRAVERSE(&users, vmu, list) {
10437       int newmsgs = 0, oldmsgs = 0;
10438       char count[12], tmp[256] = "";
10439 
10440       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10441          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10442          inboxcount(tmp, &newmsgs, &oldmsgs);
10443          snprintf(count, sizeof(count), "%d", newmsgs);
10444          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10445          users_counter++;
10446       }
10447    }
10448    AST_LIST_UNLOCK(&users);
10449    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10450    return CLI_SUCCESS;
10451 }
10452 
10453 /*! \brief Show a list of voicemail zones in the CLI */
10454 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10455 {
10456    struct vm_zone *zone;
10457 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10458    char *res = CLI_SUCCESS;
10459 
10460    switch (cmd) {
10461    case CLI_INIT:
10462       e->command = "voicemail show zones";
10463       e->usage =
10464          "Usage: voicemail show zones\n"
10465          "       Lists zone message formats\n";
10466       return NULL;
10467    case CLI_GENERATE:
10468       return NULL;
10469    }
10470 
10471    if (a->argc != 3)
10472       return CLI_SHOWUSAGE;
10473 
10474    AST_LIST_LOCK(&zones);
10475    if (!AST_LIST_EMPTY(&zones)) {
10476       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10477       AST_LIST_TRAVERSE(&zones, zone, list) {
10478          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10479       }
10480    } else {
10481       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10482       res = CLI_FAILURE;
10483    }
10484    AST_LIST_UNLOCK(&zones);
10485 
10486    return res;
10487 }
10488 
10489 /*! \brief Reload voicemail configuration from the CLI */
10490 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10491 {
10492    switch (cmd) {
10493    case CLI_INIT:
10494       e->command = "voicemail reload";
10495       e->usage =
10496          "Usage: voicemail reload\n"
10497          "       Reload voicemail configuration\n";
10498       return NULL;
10499    case CLI_GENERATE:
10500       return NULL;
10501    }
10502 
10503    if (a->argc != 2)
10504       return CLI_SHOWUSAGE;
10505 
10506    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10507    load_config(1);
10508    
10509    return CLI_SUCCESS;
10510 }
10511 
10512 static struct ast_cli_entry cli_voicemail[] = {
10513    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10514    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10515    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10516 };
10517 
10518 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10519 {
10520    int new = 0, old = 0, urgent = 0;
10521 
10522    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10523 
10524    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10525       mwi_sub->old_urgent = urgent;
10526       mwi_sub->old_new = new;
10527       mwi_sub->old_old = old;
10528       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10529    }
10530 }
10531 
10532 static void poll_subscribed_mailboxes(void)
10533 {
10534    struct mwi_sub *mwi_sub;
10535 
10536    AST_RWLIST_RDLOCK(&mwi_subs);
10537    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10538       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10539          poll_subscribed_mailbox(mwi_sub);
10540       }
10541    }
10542    AST_RWLIST_UNLOCK(&mwi_subs);
10543 }
10544 
10545 static void *mb_poll_thread(void *data)
10546 {
10547    while (poll_thread_run) {
10548       struct timespec ts = { 0, };
10549       struct timeval wait;
10550 
10551       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10552       ts.tv_sec = wait.tv_sec;
10553       ts.tv_nsec = wait.tv_usec * 1000;
10554 
10555       ast_mutex_lock(&poll_lock);
10556       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10557       ast_mutex_unlock(&poll_lock);
10558 
10559       if (!poll_thread_run)
10560          break;
10561 
10562       poll_subscribed_mailboxes();
10563    }
10564 
10565    return NULL;
10566 }
10567 
10568 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10569 {
10570    ast_free(mwi_sub);
10571 }
10572 
10573 static int handle_unsubscribe(void *datap)
10574 {
10575    struct mwi_sub *mwi_sub;
10576    uint32_t *uniqueid = datap;
10577    
10578    AST_RWLIST_WRLOCK(&mwi_subs);
10579    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10580       if (mwi_sub->uniqueid == *uniqueid) {
10581          AST_LIST_REMOVE_CURRENT(entry);
10582          break;
10583       }
10584    }
10585    AST_RWLIST_TRAVERSE_SAFE_END
10586    AST_RWLIST_UNLOCK(&mwi_subs);
10587 
10588    if (mwi_sub)
10589       mwi_sub_destroy(mwi_sub);
10590 
10591    ast_free(uniqueid);  
10592    return 0;
10593 }
10594 
10595 static int handle_subscribe(void *datap)
10596 {
10597    unsigned int len;
10598    struct mwi_sub *mwi_sub;
10599    struct mwi_sub_task *p = datap;
10600 
10601    len = sizeof(*mwi_sub);
10602    if (!ast_strlen_zero(p->mailbox))
10603       len += strlen(p->mailbox);
10604 
10605    if (!ast_strlen_zero(p->context))
10606       len += strlen(p->context) + 1; /* Allow for seperator */
10607 
10608    if (!(mwi_sub = ast_calloc(1, len)))
10609       return -1;
10610 
10611    mwi_sub->uniqueid = p->uniqueid;
10612    if (!ast_strlen_zero(p->mailbox))
10613       strcpy(mwi_sub->mailbox, p->mailbox);
10614 
10615    if (!ast_strlen_zero(p->context)) {
10616       strcat(mwi_sub->mailbox, "@");
10617       strcat(mwi_sub->mailbox, p->context);
10618    }
10619 
10620    AST_RWLIST_WRLOCK(&mwi_subs);
10621    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10622    AST_RWLIST_UNLOCK(&mwi_subs);
10623    ast_free((void *) p->mailbox);
10624    ast_free((void *) p->context);
10625    ast_free(p);
10626    poll_subscribed_mailbox(mwi_sub);
10627    return 0;
10628 }
10629 
10630 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10631 {
10632    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10633    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10634       return;
10635 
10636    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10637       return;
10638 
10639    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10640    *uniqueid = u;
10641    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10642       ast_free(uniqueid);
10643    }
10644 }
10645 
10646 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10647 {
10648    struct mwi_sub_task *mwist;
10649    
10650    if (ast_event_get_type(event) != AST_EVENT_SUB)
10651       return;
10652 
10653    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10654       return;
10655 
10656    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10657       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10658       return;
10659    }
10660    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10661    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10662    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10663    
10664    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10665       ast_free(mwist);
10666    }
10667 }
10668 
10669 static void start_poll_thread(void)
10670 {
10671    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10672       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10673       AST_EVENT_IE_END);
10674 
10675    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10676       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10677       AST_EVENT_IE_END);
10678 
10679    if (mwi_sub_sub)
10680       ast_event_report_subs(mwi_sub_sub);
10681 
10682    poll_thread_run = 1;
10683 
10684    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10685 }
10686 
10687 static void stop_poll_thread(void)
10688 {
10689    poll_thread_run = 0;
10690 
10691    if (mwi_sub_sub) {
10692       ast_event_unsubscribe(mwi_sub_sub);
10693       mwi_sub_sub = NULL;
10694    }
10695 
10696    if (mwi_unsub_sub) {
10697       ast_event_unsubscribe(mwi_unsub_sub);
10698       mwi_unsub_sub = NULL;
10699    }
10700 
10701    ast_mutex_lock(&poll_lock);
10702    ast_cond_signal(&poll_cond);
10703    ast_mutex_unlock(&poll_lock);
10704 
10705    pthread_join(poll_thread, NULL);
10706 
10707    poll_thread = AST_PTHREADT_NULL;
10708 }
10709 
10710 /*! \brief Manager list voicemail users command */
10711 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10712 {
10713    struct ast_vm_user *vmu = NULL;
10714    const char *id = astman_get_header(m, "ActionID");
10715    char actionid[128] = "";
10716 
10717    if (!ast_strlen_zero(id))
10718       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10719 
10720    AST_LIST_LOCK(&users);
10721 
10722    if (AST_LIST_EMPTY(&users)) {
10723       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10724       AST_LIST_UNLOCK(&users);
10725       return RESULT_SUCCESS;
10726    }
10727    
10728    astman_send_ack(s, m, "Voicemail user list will follow");
10729    
10730    AST_LIST_TRAVERSE(&users, vmu, list) {
10731       char dirname[256];
10732 
10733 #ifdef IMAP_STORAGE
10734       int new, old;
10735       inboxcount(vmu->mailbox, &new, &old);
10736 #endif
10737       
10738       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10739       astman_append(s,
10740          "%s"
10741          "Event: VoicemailUserEntry\r\n"
10742          "VMContext: %s\r\n"
10743          "VoiceMailbox: %s\r\n"
10744          "Fullname: %s\r\n"
10745          "Email: %s\r\n"
10746          "Pager: %s\r\n"
10747          "ServerEmail: %s\r\n"
10748          "MailCommand: %s\r\n"
10749          "Language: %s\r\n"
10750          "TimeZone: %s\r\n"
10751          "Callback: %s\r\n"
10752          "Dialout: %s\r\n"
10753          "UniqueID: %s\r\n"
10754          "ExitContext: %s\r\n"
10755          "SayDurationMinimum: %d\r\n"
10756          "SayEnvelope: %s\r\n"
10757          "SayCID: %s\r\n"
10758          "AttachMessage: %s\r\n"
10759          "AttachmentFormat: %s\r\n"
10760          "DeleteMessage: %s\r\n"
10761          "VolumeGain: %.2f\r\n"
10762          "CanReview: %s\r\n"
10763          "CallOperator: %s\r\n"
10764          "MaxMessageCount: %d\r\n"
10765          "MaxMessageLength: %d\r\n"
10766          "NewMessageCount: %d\r\n"
10767 #ifdef IMAP_STORAGE
10768          "OldMessageCount: %d\r\n"
10769          "IMAPUser: %s\r\n"
10770 #endif
10771          "\r\n",
10772          actionid,
10773          vmu->context,
10774          vmu->mailbox,
10775          vmu->fullname,
10776          vmu->email,
10777          vmu->pager,
10778          vmu->serveremail,
10779          vmu->mailcmd,
10780          vmu->language,
10781          vmu->zonetag,
10782          vmu->callback,
10783          vmu->dialout,
10784          vmu->uniqueid,
10785          vmu->exit,
10786          vmu->saydurationm,
10787          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10788          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10789          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10790          vmu->attachfmt,
10791          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10792          vmu->volgain,
10793          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10794          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10795          vmu->maxmsg,
10796          vmu->maxsecs,
10797 #ifdef IMAP_STORAGE
10798          new, old, vmu->imapuser
10799 #else
10800          count_messages(vmu, dirname)
10801 #endif
10802          );
10803    }     
10804    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10805 
10806    AST_LIST_UNLOCK(&users);
10807 
10808    return RESULT_SUCCESS;
10809 }
10810 
10811 /*! \brief Free the users structure. */
10812 static void free_vm_users(void) 
10813 {
10814    struct ast_vm_user *current;
10815    AST_LIST_LOCK(&users);
10816    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10817       ast_set_flag(current, VM_ALLOCED);
10818       free_user(current);
10819    }
10820    AST_LIST_UNLOCK(&users);
10821 }
10822 
10823 /*! \brief Free the zones structure. */
10824 static void free_vm_zones(void)
10825 {
10826    struct vm_zone *zcur;
10827    AST_LIST_LOCK(&zones);
10828    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10829       free_zone(zcur);
10830    AST_LIST_UNLOCK(&zones);
10831 }
10832 
10833 static const char *substitute_escapes(const char *value)
10834 {
10835    char *current;
10836 
10837    /* Add 16 for fudge factor */
10838    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10839 
10840    ast_str_reset(str);
10841    
10842    /* Substitute strings \r, \n, and \t into the appropriate characters */
10843    for (current = (char *) value; *current; current++) {
10844       if (*current == '\\') {
10845          current++;
10846          if (!*current) {
10847             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10848             break;
10849          }
10850          switch (*current) {
10851          case 'r':
10852             ast_str_append(&str, 0, "\r");
10853             break;
10854          case 'n':
10855 #ifdef IMAP_STORAGE
10856             if (!str->used || str->str[str->used - 1] != '\r') {
10857                ast_str_append(&str, 0, "\r");
10858             }
10859 #endif
10860             ast_str_append(&str, 0, "\n");
10861             break;
10862          case 't':
10863             ast_str_append(&str, 0, "\t");
10864             break;
10865          default:
10866             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10867             break;
10868          }
10869       } else {
10870          ast_str_append(&str, 0, "%c", *current);
10871       }
10872    }
10873 
10874    return ast_str_buffer(str);
10875 }
10876 
10877 static int load_config(int reload)
10878 {
10879    struct ast_vm_user *current;
10880    struct ast_config *cfg, *ucfg;
10881    char *cat;
10882    struct ast_variable *var;
10883    const char *val;
10884    char *q, *stringp, *tmp;
10885    int x;
10886    int tmpadsi[4];
10887    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10888 
10889    ast_unload_realtime("voicemail");
10890    ast_unload_realtime("voicemail_data");
10891 
10892    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10893       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10894          return 0;
10895       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10896          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10897          ucfg = NULL;
10898       }
10899       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10900       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10901          ast_config_destroy(ucfg);
10902          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10903          return 0;
10904       }
10905    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10906       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10907       return 0;
10908    } else {
10909       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10910       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10911          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10912          ucfg = NULL;
10913       }
10914    }
10915 #ifdef IMAP_STORAGE
10916    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10917 #endif
10918    /* set audio control prompts */
10919    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10920    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10921    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10922    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10923    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10924 
10925    /* Free all the users structure */  
10926    free_vm_users();
10927 
10928    /* Free all the zones structure */
10929    free_vm_zones();
10930 
10931    AST_LIST_LOCK(&users);  
10932 
10933    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10934    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10935 
10936    if (cfg) {
10937       /* General settings */
10938 
10939       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10940          val = "default";
10941       ast_copy_string(userscontext, val, sizeof(userscontext));
10942       /* Attach voice message to mail message ? */
10943       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10944          val = "yes";
10945       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10946 
10947       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10948          val = "no";
10949       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10950 
10951       volgain = 0.0;
10952       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10953          sscanf(val, "%30lf", &volgain);
10954 
10955 #ifdef ODBC_STORAGE
10956       strcpy(odbc_database, "asterisk");
10957       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10958          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10959       }
10960       strcpy(odbc_table, "voicemessages");
10961       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10962          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10963       }
10964 #endif      
10965       /* Mail command */
10966       strcpy(mailcmd, SENDMAIL);
10967       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10968          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10969 
10970       maxsilence = 0;
10971       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10972          maxsilence = atoi(val);
10973          if (maxsilence > 0)
10974             maxsilence *= 1000;
10975       }
10976       
10977       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10978          maxmsg = MAXMSG;
10979       } else {
10980          maxmsg = atoi(val);
10981          if (maxmsg <= 0) {
10982             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10983             maxmsg = MAXMSG;
10984          } else if (maxmsg > MAXMSGLIMIT) {
10985             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10986             maxmsg = MAXMSGLIMIT;
10987          }
10988       }
10989 
10990       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10991          maxdeletedmsg = 0;
10992       } else {
10993          if (sscanf(val, "%30d", &x) == 1)
10994             maxdeletedmsg = x;
10995          else if (ast_true(val))
10996             maxdeletedmsg = MAXMSG;
10997          else
10998             maxdeletedmsg = 0;
10999 
11000          if (maxdeletedmsg < 0) {
11001             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11002             maxdeletedmsg = MAXMSG;
11003          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11004             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11005             maxdeletedmsg = MAXMSGLIMIT;
11006          }
11007       }
11008 
11009       /* Load date format config for voicemail mail */
11010       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11011          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11012       }
11013 
11014       /* External password changing command */
11015       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11016          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
11017          pwdchange = PWDCHANGE_EXTERNAL;
11018       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11019          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
11020          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11021       }
11022  
11023       /* External password validation command */
11024       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11025          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11026          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11027       }
11028 
11029 #ifdef IMAP_STORAGE
11030       /* IMAP server address */
11031       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11032          ast_copy_string(imapserver, val, sizeof(imapserver));
11033       } else {
11034          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
11035       }
11036       /* IMAP server port */
11037       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11038          ast_copy_string(imapport, val, sizeof(imapport));
11039       } else {
11040          ast_copy_string(imapport,"143", sizeof(imapport));
11041       }
11042       /* IMAP server flags */
11043       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11044          ast_copy_string(imapflags, val, sizeof(imapflags));
11045       }
11046       /* IMAP server master username */
11047       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11048          ast_copy_string(authuser, val, sizeof(authuser));
11049       }
11050       /* IMAP server master password */
11051       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11052          ast_copy_string(authpassword, val, sizeof(authpassword));
11053       }
11054       /* Expunge on exit */
11055       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11056          if (ast_false(val))
11057             expungeonhangup = 0;
11058          else
11059             expungeonhangup = 1;
11060       } else {
11061          expungeonhangup = 1;
11062       }
11063       /* IMAP voicemail folder */
11064       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11065          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11066       } else {
11067          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
11068       }
11069       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11070          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11071       }
11072       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11073          imapgreetings = ast_true(val);
11074       } else {
11075          imapgreetings = 0;
11076       }
11077       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11078          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11079       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11080          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11081          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11082       } else {
11083          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11084       }
11085 
11086       /* There is some very unorthodox casting done here. This is due
11087        * to the way c-client handles the argument passed in. It expects a 
11088        * void pointer and casts the pointer directly to a long without
11089        * first dereferencing it. */
11090       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11091          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11092       } else {
11093          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11094       }
11095 
11096       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11097          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11098       } else {
11099          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11100       }
11101 
11102       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11103          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11104       } else {
11105          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11106       }
11107 
11108       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11109          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11110       } else {
11111          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11112       }
11113 
11114       /* Increment configuration version */
11115       imapversion++;
11116 #endif
11117       /* External voicemail notify application */
11118       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11119          ast_copy_string(externnotify, val, sizeof(externnotify));
11120          ast_debug(1, "found externnotify: %s\n", externnotify);
11121       } else {
11122          externnotify[0] = '\0';
11123       }
11124 
11125       /* SMDI voicemail notification */
11126       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11127          ast_debug(1, "Enabled SMDI voicemail notification\n");
11128          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11129             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
11130          } else {
11131             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11132             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
11133          }
11134          if (!smdi_iface) {
11135             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11136          } 
11137       }
11138 
11139       /* Silence treshold */
11140       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11141       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11142          silencethreshold = atoi(val);
11143       
11144       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11145          val = ASTERISK_USERNAME;
11146       ast_copy_string(serveremail, val, sizeof(serveremail));
11147       
11148       vmmaxsecs = 0;
11149       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11150          if (sscanf(val, "%30d", &x) == 1) {
11151             vmmaxsecs = x;
11152          } else {
11153             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11154          }
11155       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11156          static int maxmessage_deprecate = 0;
11157          if (maxmessage_deprecate == 0) {
11158             maxmessage_deprecate = 1;
11159             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11160          }
11161          if (sscanf(val, "%30d", &x) == 1) {
11162             vmmaxsecs = x;
11163          } else {
11164             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11165          }
11166       }
11167 
11168       vmminsecs = 0;
11169       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11170          if (sscanf(val, "%30d", &x) == 1) {
11171             vmminsecs = x;
11172             if (maxsilence / 1000 >= vmminsecs) {
11173                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11174             }
11175          } else {
11176             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11177          }
11178       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11179          static int maxmessage_deprecate = 0;
11180          if (maxmessage_deprecate == 0) {
11181             maxmessage_deprecate = 1;
11182             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11183          }
11184          if (sscanf(val, "%30d", &x) == 1) {
11185             vmminsecs = x;
11186             if (maxsilence / 1000 >= vmminsecs) {
11187                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11188             }
11189          } else {
11190             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11191          }
11192       }
11193 
11194       val = ast_variable_retrieve(cfg, "general", "format");
11195       if (!val) {
11196          val = "wav";   
11197       } else {
11198          tmp = ast_strdupa(val);
11199          val = ast_format_str_reduce(tmp);
11200          if (!val) {
11201             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11202             val = "wav";
11203          }
11204       }
11205       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11206 
11207       skipms = 3000;
11208       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11209          if (sscanf(val, "%30d", &x) == 1) {
11210             maxgreet = x;
11211          } else {
11212             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11213          }
11214       }
11215 
11216       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11217          if (sscanf(val, "%30d", &x) == 1) {
11218             skipms = x;
11219          } else {
11220             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11221          }
11222       }
11223 
11224       maxlogins = 3;
11225       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11226          if (sscanf(val, "%30d", &x) == 1) {
11227             maxlogins = x;
11228          } else {
11229             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11230          }
11231       }
11232 
11233       minpassword = MINPASSWORD;
11234       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11235          if (sscanf(val, "%30d", &x) == 1) {
11236             minpassword = x;
11237          } else {
11238             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11239          }
11240       }
11241 
11242       /* Force new user to record name ? */
11243       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11244          val = "no";
11245       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11246 
11247       /* Force new user to record greetings ? */
11248       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11249          val = "no";
11250       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11251 
11252       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11253          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11254          stringp = ast_strdupa(val);
11255          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11256             if (!ast_strlen_zero(stringp)) {
11257                q = strsep(&stringp, ",");
11258                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11259                   q++;
11260                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11261                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11262             } else {
11263                cidinternalcontexts[x][0] = '\0';
11264             }
11265          }
11266       }
11267       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11268          ast_debug(1,"VM Review Option disabled globally\n");
11269          val = "no";
11270       }
11271       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11272 
11273       /* Temporary greeting reminder */
11274       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11275          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11276          val = "no";
11277       } else {
11278          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11279       }
11280       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11281       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11282          ast_debug(1, "VM next message wrap disabled globally\n");
11283          val = "no";
11284       }
11285       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11286 
11287       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11288          ast_debug(1,"VM Operator break disabled globally\n");
11289          val = "no";
11290       }
11291       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11292 
11293       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11294          ast_debug(1,"VM CID Info before msg disabled globally\n");
11295          val = "no";
11296       } 
11297       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11298 
11299       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
11300          ast_debug(1,"Send Voicemail msg disabled globally\n");
11301          val = "no";
11302       }
11303       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11304    
11305       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11306          ast_debug(1,"ENVELOPE before msg enabled globally\n");
11307          val = "yes";
11308       }
11309       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11310 
11311       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11312          ast_debug(1,"Move Heard enabled globally\n");
11313          val = "yes";
11314       }
11315       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11316 
11317       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11318          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11319          val = "no";
11320       }
11321       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11322 
11323       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11324          ast_debug(1,"Duration info before msg enabled globally\n");
11325          val = "yes";
11326       }
11327       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11328 
11329       saydurationminfo = 2;
11330       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11331          if (sscanf(val, "%30d", &x) == 1) {
11332             saydurationminfo = x;
11333          } else {
11334             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11335          }
11336       }
11337 
11338       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11339          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
11340          val = "no";
11341       }
11342       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11343 
11344       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11345          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11346          ast_debug(1, "found dialout context: %s\n", dialcontext);
11347       } else {
11348          dialcontext[0] = '\0';  
11349       }
11350       
11351       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11352          ast_copy_string(callcontext, val, sizeof(callcontext));
11353          ast_debug(1, "found callback context: %s\n", callcontext);
11354       } else {
11355          callcontext[0] = '\0';
11356       }
11357 
11358       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11359          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11360          ast_debug(1, "found operator context: %s\n", exitcontext);
11361       } else {
11362          exitcontext[0] = '\0';
11363       }
11364       
11365       /* load password sounds configuration */
11366       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11367          ast_copy_string(vm_password, val, sizeof(vm_password));
11368       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11369          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11370       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11371          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11372       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11373          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11374       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11375          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11376       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11377          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11378       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11379          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11380       }
11381       /* load configurable audio prompts */
11382       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11383          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11384       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11385          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11386       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11387          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11388       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11389          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11390       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11391          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11392 
11393       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11394          val = "no";
11395       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11396 
11397       poll_freq = DEFAULT_POLL_FREQ;
11398       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11399          if (sscanf(val, "%30u", &poll_freq) != 1) {
11400             poll_freq = DEFAULT_POLL_FREQ;
11401             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11402          }
11403       }
11404 
11405       poll_mailboxes = 0;
11406       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11407          poll_mailboxes = ast_true(val);
11408 
11409       if (ucfg) { 
11410          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11411             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11412                continue;
11413             if ((current = find_or_create(userscontext, cat))) {
11414                populate_defaults(current);
11415                apply_options_full(current, ast_variable_browse(ucfg, cat));
11416                ast_copy_string(current->context, userscontext, sizeof(current->context));
11417             }
11418          }
11419          ast_config_destroy(ucfg);
11420       }
11421       cat = ast_category_browse(cfg, NULL);
11422       while (cat) {
11423          if (strcasecmp(cat, "general")) {
11424             var = ast_variable_browse(cfg, cat);
11425             if (strcasecmp(cat, "zonemessages")) {
11426                /* Process mailboxes in this context */
11427                while (var) {
11428                   append_mailbox(cat, var->name, var->value);
11429                   var = var->next;
11430                }
11431             } else {
11432                /* Timezones in this context */
11433                while (var) {
11434                   struct vm_zone *z;
11435                   if ((z = ast_malloc(sizeof(*z)))) {
11436                      char *msg_format, *tzone;
11437                      msg_format = ast_strdupa(var->value);
11438                      tzone = strsep(&msg_format, "|,");
11439                      if (msg_format) {
11440                         ast_copy_string(z->name, var->name, sizeof(z->name));
11441                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11442                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11443                         AST_LIST_LOCK(&zones);
11444                         AST_LIST_INSERT_HEAD(&zones, z, list);
11445                         AST_LIST_UNLOCK(&zones);
11446                      } else {
11447                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11448                         ast_free(z);
11449                      }
11450                   } else {
11451                      AST_LIST_UNLOCK(&users);
11452                      ast_config_destroy(cfg);
11453                      return -1;
11454                   }
11455                   var = var->next;
11456                }
11457             }
11458          }
11459          cat = ast_category_browse(cfg, cat);
11460       }
11461       memset(fromstring, 0, sizeof(fromstring));
11462       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11463       strcpy(charset, "ISO-8859-1");
11464       if (emailbody) {
11465          ast_free(emailbody);
11466          emailbody = NULL;
11467       }
11468       if (emailsubject) {
11469          ast_free(emailsubject);
11470          emailsubject = NULL;
11471       }
11472       if (pagerbody) {
11473          ast_free(pagerbody);
11474          pagerbody = NULL;
11475       }
11476       if (pagersubject) {
11477          ast_free(pagersubject);
11478          pagersubject = NULL;
11479       }
11480       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11481          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11482       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11483          ast_copy_string(fromstring, val, sizeof(fromstring));
11484       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11485          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11486       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11487          ast_copy_string(charset, val, sizeof(charset));
11488       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11489          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11490          for (x = 0; x < 4; x++) {
11491             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11492          }
11493       }
11494       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11495          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11496          for (x = 0; x < 4; x++) {
11497             memcpy(&adsisec[x], &tmpadsi[x], 1);
11498          }
11499       }
11500       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11501          if (atoi(val)) {
11502             adsiver = atoi(val);
11503          }
11504       }
11505       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11506          ast_copy_string(zonetag, val, sizeof(zonetag));
11507       }
11508       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11509          emailsubject = ast_strdup(val);
11510       }
11511       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11512          emailbody = ast_strdup(substitute_escapes(val));
11513       }
11514       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11515          pagersubject = ast_strdup(val);
11516       }
11517       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11518          pagerbody = ast_strdup(substitute_escapes(val));
11519       }
11520       AST_LIST_UNLOCK(&users);
11521       ast_config_destroy(cfg);
11522 
11523       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11524          start_poll_thread();
11525       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11526          stop_poll_thread();;
11527 
11528       return 0;
11529    } else {
11530       AST_LIST_UNLOCK(&users);
11531       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11532       if (ucfg)
11533          ast_config_destroy(ucfg);
11534       return 0;
11535    }
11536 }
11537 
11538 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11539 {
11540    int res = -1;
11541    char dir[PATH_MAX];
11542    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11543    ast_debug(2, "About to try retrieving name file %s\n", dir);
11544    RETRIEVE(dir, -1, mailbox, context);
11545    if (ast_fileexists(dir, NULL, NULL)) {
11546       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11547    }
11548    DISPOSE(dir, -1);
11549    return res;
11550 }
11551 
11552 static int reload(void)
11553 {
11554    return load_config(1);
11555 }
11556 
11557 static int unload_module(void)
11558 {
11559    int res;
11560 
11561    res = ast_unregister_application(app);
11562    res |= ast_unregister_application(app2);
11563    res |= ast_unregister_application(app3);
11564    res |= ast_unregister_application(app4);
11565    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11566    res |= ast_manager_unregister("VoicemailUsersList");
11567    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11568    ast_uninstall_vm_functions();
11569    ao2_ref(inprocess_container, -1);
11570 
11571    if (poll_thread != AST_PTHREADT_NULL)
11572       stop_poll_thread();
11573 
11574    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11575    ast_unload_realtime("voicemail");
11576    ast_unload_realtime("voicemail_data");
11577 
11578    free_vm_users();
11579    free_vm_zones();
11580    return res;
11581 }
11582 
11583 static int load_module(void)
11584 {
11585    int res;
11586    my_umask = umask(0);
11587    umask(my_umask);
11588 
11589    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11590       return AST_MODULE_LOAD_DECLINE;
11591    }
11592 
11593    /* compute the location of the voicemail spool directory */
11594    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11595 
11596    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11597       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11598    }
11599 
11600    if ((res = load_config(0)))
11601       return res;
11602 
11603    res = ast_register_application_xml(app, vm_exec);
11604    res |= ast_register_application_xml(app2, vm_execmain);
11605    res |= ast_register_application_xml(app3, vm_box_exists);
11606    res |= ast_register_application_xml(app4, vmauthenticate);
11607    res |= ast_custom_function_register(&mailbox_exists_acf);
11608    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11609    if (res)
11610       return res;
11611 
11612    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11613 
11614    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11615    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11616    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11617 
11618    return res;
11619 }
11620 
11621 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11622 {
11623    int cmd = 0;
11624    char destination[80] = "";
11625    int retries = 0;
11626 
11627    if (!num) {
11628       ast_verb(3, "Destination number will be entered manually\n");
11629       while (retries < 3 && cmd != 't') {
11630          destination[1] = '\0';
11631          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11632          if (!cmd)
11633             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11634          if (!cmd)
11635             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11636          if (!cmd) {
11637             cmd = ast_waitfordigit(chan, 6000);
11638             if (cmd)
11639                destination[0] = cmd;
11640          }
11641          if (!cmd) {
11642             retries++;
11643          } else {
11644 
11645             if (cmd < 0)
11646                return 0;
11647             if (cmd == '*') {
11648                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11649                return 0;
11650             }
11651             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11652                retries++;
11653             else
11654                cmd = 't';
11655          }
11656       }
11657       if (retries >= 3) {
11658          return 0;
11659       }
11660       
11661    } else {
11662       if (option_verbose > 2)
11663          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11664       ast_copy_string(destination, num, sizeof(destination));
11665    }
11666 
11667    if (!ast_strlen_zero(destination)) {
11668       if (destination[strlen(destination) -1 ] == '*')
11669          return 0; 
11670       if (option_verbose > 2)
11671          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11672       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11673       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11674       chan->priority = 0;
11675       return 9;
11676    }
11677    return 0;
11678 }
11679 
11680 /*!
11681  * \brief The advanced options within a message.
11682  * \param chan
11683  * \param vmu 
11684  * \param vms
11685  * \param msg
11686  * \param option
11687  * \param record_gain
11688  *
11689  * Provides handling for the play message envelope, call the person back, or reply to message. 
11690  *
11691  * \return zero on success, -1 on error.
11692  */
11693 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)
11694 {
11695    int res = 0;
11696    char filename[PATH_MAX];
11697    struct ast_config *msg_cfg = NULL;
11698    const char *origtime, *context;
11699    char *name, *num;
11700    int retries = 0;
11701    char *cid;
11702    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11703 
11704    vms->starting = 0; 
11705 
11706    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11707 
11708    /* Retrieve info from VM attribute file */
11709    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11710    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11711    msg_cfg = ast_config_load(filename, config_flags);
11712    DISPOSE(vms->curdir, vms->curmsg);
11713    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11714       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11715       return 0;
11716    }
11717 
11718    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11719       ast_config_destroy(msg_cfg);
11720       return 0;
11721    }
11722 
11723    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11724 
11725    context = ast_variable_retrieve(msg_cfg, "message", "context");
11726    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11727       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11728    switch (option) {
11729    case 3: /* Play message envelope */
11730       if (!res)
11731          res = play_message_datetime(chan, vmu, origtime, filename);
11732       if (!res)
11733          res = play_message_callerid(chan, vms, cid, context, 0);
11734 
11735       res = 't';
11736       break;
11737 
11738    case 2:  /* Call back */
11739 
11740       if (ast_strlen_zero(cid))
11741          break;
11742 
11743       ast_callerid_parse(cid, &name, &num);
11744       while ((res > -1) && (res != 't')) {
11745          switch (res) {
11746          case '1':
11747             if (num) {
11748                /* Dial the CID number */
11749                res = dialout(chan, vmu, num, vmu->callback);
11750                if (res) {
11751                   ast_config_destroy(msg_cfg);
11752                   return 9;
11753                }
11754             } else {
11755                res = '2';
11756             }
11757             break;
11758 
11759          case '2':
11760             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11761             if (!ast_strlen_zero(vmu->dialout)) {
11762                res = dialout(chan, vmu, NULL, vmu->dialout);
11763                if (res) {
11764                   ast_config_destroy(msg_cfg);
11765                   return 9;
11766                }
11767             } else {
11768                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11769                res = ast_play_and_wait(chan, "vm-sorry");
11770             }
11771             ast_config_destroy(msg_cfg);
11772             return res;
11773          case '*':
11774             res = 't';
11775             break;
11776          case '3':
11777          case '4':
11778          case '5':
11779          case '6':
11780          case '7':
11781          case '8':
11782          case '9':
11783          case '0':
11784 
11785             res = ast_play_and_wait(chan, "vm-sorry");
11786             retries++;
11787             break;
11788          default:
11789             if (num) {
11790                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11791                res = ast_play_and_wait(chan, "vm-num-i-have");
11792                if (!res)
11793                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11794                if (!res)
11795                   res = ast_play_and_wait(chan, "vm-tocallnum");
11796                /* Only prompt for a caller-specified number if there is a dialout context specified */
11797                if (!ast_strlen_zero(vmu->dialout)) {
11798                   if (!res)
11799                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11800                }
11801             } else {
11802                res = ast_play_and_wait(chan, "vm-nonumber");
11803                if (!ast_strlen_zero(vmu->dialout)) {
11804                   if (!res)
11805                      res = ast_play_and_wait(chan, "vm-toenternumber");
11806                }
11807             }
11808             if (!res)
11809                res = ast_play_and_wait(chan, "vm-star-cancel");
11810             if (!res)
11811                res = ast_waitfordigit(chan, 6000);
11812             if (!res) {
11813                retries++;
11814                if (retries > 3)
11815                   res = 't';
11816             }
11817             break; 
11818             
11819          }
11820          if (res == 't')
11821             res = 0;
11822          else if (res == '*')
11823             res = -1;
11824       }
11825       break;
11826       
11827    case 1:  /* Reply */
11828       /* Send reply directly to sender */
11829       if (ast_strlen_zero(cid))
11830          break;
11831 
11832       ast_callerid_parse(cid, &name, &num);
11833       if (!num) {
11834          ast_verb(3, "No CID number available, no reply sent\n");
11835          if (!res)
11836             res = ast_play_and_wait(chan, "vm-nonumber");
11837          ast_config_destroy(msg_cfg);
11838          return res;
11839       } else {
11840          struct ast_vm_user vmu2;
11841          if (find_user(&vmu2, vmu->context, num)) {
11842             struct leave_vm_options leave_options;
11843             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11844             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11845 
11846             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11847             
11848             memset(&leave_options, 0, sizeof(leave_options));
11849             leave_options.record_gain = record_gain;
11850             res = leave_voicemail(chan, mailbox, &leave_options);
11851             if (!res)
11852                res = 't';
11853             ast_config_destroy(msg_cfg);
11854             return res;
11855          } else {
11856             /* Sender has no mailbox, can't reply */
11857             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11858             ast_play_and_wait(chan, "vm-nobox");
11859             res = 't';
11860             ast_config_destroy(msg_cfg);
11861             return res;
11862          }
11863       } 
11864       res = 0;
11865 
11866       break;
11867    }
11868 
11869 #ifndef IMAP_STORAGE
11870    ast_config_destroy(msg_cfg);
11871 
11872    if (!res) {
11873       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11874       vms->heard[msg] = 1;
11875       res = wait_file(chan, vms, vms->fn);
11876    }
11877 #endif
11878    return res;
11879 }
11880 
11881 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11882          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11883          signed char record_gain, struct vm_state *vms, char *flag)
11884 {
11885    /* Record message & let caller review or re-record it, or set options if applicable */
11886    int res = 0;
11887    int cmd = 0;
11888    int max_attempts = 3;
11889    int attempts = 0;
11890    int recorded = 0;
11891    int msg_exists = 0;
11892    signed char zero_gain = 0;
11893    char tempfile[PATH_MAX];
11894    char *acceptdtmf = "#";
11895    char *canceldtmf = "";
11896    int canceleddtmf = 0;
11897 
11898    /* Note that urgent and private are for flagging messages as such in the future */
11899 
11900    /* barf if no pointer passed to store duration in */
11901    if (duration == NULL) {
11902       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11903       return -1;
11904    }
11905 
11906    if (!outsidecaller)
11907       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11908    else
11909       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11910 
11911    cmd = '3';  /* Want to start by recording */
11912 
11913    while ((cmd >= 0) && (cmd != 't')) {
11914       switch (cmd) {
11915       case '1':
11916          if (!msg_exists) {
11917             /* In this case, 1 is to record a message */
11918             cmd = '3';
11919             break;
11920          } else {
11921             /* Otherwise 1 is to save the existing message */
11922             ast_verb(3, "Saving message as is\n");
11923             if (!outsidecaller) 
11924                ast_filerename(tempfile, recordfile, NULL);
11925             ast_stream_and_wait(chan, "vm-msgsaved", "");
11926             if (!outsidecaller) {
11927                /* Saves to IMAP server only if imapgreeting=yes */
11928                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11929                DISPOSE(recordfile, -1);
11930             }
11931             cmd = 't';
11932             return res;
11933          }
11934       case '2':
11935          /* Review */
11936          ast_verb(3, "Reviewing the message\n");
11937          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11938          break;
11939       case '3':
11940          msg_exists = 0;
11941          /* Record */
11942          if (recorded == 1) 
11943             ast_verb(3, "Re-recording the message\n");
11944          else  
11945             ast_verb(3, "Recording the message\n");
11946          
11947          if (recorded && outsidecaller) {
11948             cmd = ast_play_and_wait(chan, INTRO);
11949             cmd = ast_play_and_wait(chan, "beep");
11950          }
11951          recorded = 1;
11952          /* 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 */
11953          if (record_gain)
11954             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11955          if (ast_test_flag(vmu, VM_OPERATOR))
11956             canceldtmf = "0";
11957          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11958          if (strchr(canceldtmf, cmd)) {
11959          /* need this flag here to distinguish between pressing '0' during message recording or after */
11960             canceleddtmf = 1;
11961          }
11962          if (record_gain)
11963             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11964          if (cmd == -1) {
11965             /* User has hung up, no options to give */
11966             if (!outsidecaller) {
11967                /* user was recording a greeting and they hung up, so let's delete the recording. */
11968                ast_filedelete(tempfile, NULL);
11969             }     
11970             return cmd;
11971          }
11972          if (cmd == '0') {
11973             break;
11974          } else if (cmd == '*') {
11975             break;
11976 #if 0
11977          } else if (vmu->review && (*duration < 5)) {
11978             /* Message is too short */
11979             ast_verb(3, "Message too short\n");
11980             cmd = ast_play_and_wait(chan, "vm-tooshort");
11981             cmd = ast_filedelete(tempfile, NULL);
11982             break;
11983          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11984             /* Message is all silence */
11985             ast_verb(3, "Nothing recorded\n");
11986             cmd = ast_filedelete(tempfile, NULL);
11987             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11988             if (!cmd)
11989                cmd = ast_play_and_wait(chan, "vm-speakup");
11990             break;
11991 #endif
11992          } else {
11993             /* If all is well, a message exists */
11994             msg_exists = 1;
11995             cmd = 0;
11996          }
11997          break;
11998       case '4':
11999          if (outsidecaller) {  /* only mark vm messages */
12000             /* Mark Urgent */
12001             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
12002                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
12003                res = ast_play_and_wait(chan, "vm-marked-urgent");
12004                strcpy(flag, "Urgent");
12005             } else if (flag) {
12006                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
12007                res = ast_play_and_wait(chan, "vm-urgent-removed");
12008                strcpy(flag, "");
12009             } else {
12010                ast_play_and_wait(chan, "vm-sorry");
12011             }
12012             cmd = 0;
12013          } else {
12014             cmd = ast_play_and_wait(chan, "vm-sorry");
12015          }
12016          break;
12017       case '5':
12018       case '6':
12019       case '7':
12020       case '8':
12021       case '9':
12022       case '*':
12023       case '#':
12024          cmd = ast_play_and_wait(chan, "vm-sorry");
12025          break;
12026 #if 0 
12027 /*  XXX Commented out for the moment because of the dangers of deleting
12028     a message while recording (can put the message numbers out of sync) */
12029       case '*':
12030          /* Cancel recording, delete message, offer to take another message*/
12031          cmd = ast_play_and_wait(chan, "vm-deleted");
12032          cmd = ast_filedelete(tempfile, NULL);
12033          if (outsidecaller) {
12034             res = vm_exec(chan, NULL);
12035             return res;
12036          }
12037          else
12038             return 1;
12039 #endif
12040       case '0':
12041          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
12042             cmd = ast_play_and_wait(chan, "vm-sorry");
12043             break;
12044          }
12045          if (msg_exists || recorded) {
12046             cmd = ast_play_and_wait(chan, "vm-saveoper");
12047             if (!cmd)
12048                cmd = ast_waitfordigit(chan, 3000);
12049             if (cmd == '1') {
12050                ast_filerename(tempfile, recordfile, NULL);
12051                ast_play_and_wait(chan, "vm-msgsaved");
12052                cmd = '0';
12053             } else if (cmd == '4') {
12054                if (flag) {
12055                   ast_play_and_wait(chan, "vm-marked-urgent");
12056                   strcpy(flag, "Urgent");
12057                }
12058                ast_play_and_wait(chan, "vm-msgsaved");
12059                cmd = '0';
12060             } else {
12061                ast_play_and_wait(chan, "vm-deleted");
12062                DELETE(tempfile, -1, tempfile, vmu);
12063                cmd = '0';
12064             }
12065          }
12066          return cmd;
12067       default:
12068          /* If the caller is an ouside caller, and the review option is enabled,
12069             allow them to review the message, but let the owner of the box review
12070             their OGM's */
12071          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
12072             return cmd;
12073          if (msg_exists) {
12074             cmd = ast_play_and_wait(chan, "vm-review");
12075             if (!cmd && outsidecaller) {
12076                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
12077                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
12078                } else if (flag) {
12079                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
12080                }
12081             }
12082          } else {
12083             cmd = ast_play_and_wait(chan, "vm-torerecord");
12084             if (!cmd)
12085                cmd = ast_waitfordigit(chan, 600);
12086          }
12087          
12088          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
12089             cmd = ast_play_and_wait(chan, "vm-reachoper");
12090             if (!cmd)
12091                cmd = ast_waitfordigit(chan, 600);
12092          }
12093 #if 0
12094          if (!cmd)
12095             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
12096 #endif
12097          if (!cmd)
12098             cmd = ast_waitfordigit(chan, 6000);
12099          if (!cmd) {
12100             attempts++;
12101          }
12102          if (attempts > max_attempts) {
12103             cmd = 't';
12104          }
12105       }
12106    }
12107    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
12108       /* Hang up or timeout, so delete the recording. */
12109       ast_filedelete(tempfile, NULL);
12110    }
12111 
12112    if (cmd != 't' && outsidecaller)
12113       ast_play_and_wait(chan, "vm-goodbye");
12114 
12115    return cmd;
12116 }
12117 
12118 /* This is a workaround so that menuselect displays a proper description
12119  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
12120  */
12121 
12122 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
12123       .load = load_module,
12124       .unload = unload_module,
12125       .reload = reload,
12126       );