Tue Aug 24 2010 19:41:26

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: 280671 $")
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 curmsg;
00631    int lastmsg;
00632    int newmessages;
00633    int oldmessages;
00634    int urgentmessages;
00635    int starting;
00636    int repeats;
00637 #ifdef IMAP_STORAGE
00638    ast_mutex_t lock;
00639    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00640    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00641    MAILSTREAM *mailstream;
00642    int vmArrayIndex;
00643    char imapuser[80];                   /*!< IMAP server login */
00644    int imapversion;
00645    int interactive;
00646    char introfn[PATH_MAX];              /*!< Name of prepended file */
00647    unsigned int quota_limit;
00648    unsigned int quota_usage;
00649    struct vm_state *persist_vms;
00650 #endif
00651 };
00652 
00653 #ifdef ODBC_STORAGE
00654 static char odbc_database[80];
00655 static char odbc_table[80];
00656 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00657 #define DISPOSE(a,b) remove_file(a,b)
00658 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00659 #define EXISTS(a,b,c,d) (message_exists(a,b))
00660 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00661 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00662 #define DELETE(a,b,c,d) (delete_file(a,b))
00663 #else
00664 #ifdef IMAP_STORAGE
00665 #define DISPOSE(a,b) (imap_remove_file(a,b))
00666 #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))
00667 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00668 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00669 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00670 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00671 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00672 #else
00673 #define RETRIEVE(a,b,c,d)
00674 #define DISPOSE(a,b)
00675 #define STORE(a,b,c,d,e,f,g,h,i,j)
00676 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00677 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00678 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00679 #define DELETE(a,b,c,d) (vm_delete(c))
00680 #endif
00681 #endif
00682 
00683 static char VM_SPOOL_DIR[PATH_MAX];
00684 
00685 static char ext_pass_cmd[128];
00686 static char ext_pass_check_cmd[128];
00687 
00688 static int my_umask;
00689 
00690 #define PWDCHANGE_INTERNAL (1 << 1)
00691 #define PWDCHANGE_EXTERNAL (1 << 2)
00692 static int pwdchange = PWDCHANGE_INTERNAL;
00693 
00694 #ifdef ODBC_STORAGE
00695 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00696 #else
00697 # ifdef IMAP_STORAGE
00698 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00699 # else
00700 # define tdesc "Comedian Mail (Voicemail System)"
00701 # endif
00702 #endif
00703 
00704 static char userscontext[AST_MAX_EXTENSION] = "default";
00705 
00706 static char *addesc = "Comedian Mail";
00707 
00708 /* Leave a message */
00709 static char *app = "VoiceMail";
00710 
00711 /* Check mail, control, etc */
00712 static char *app2 = "VoiceMailMain";
00713 
00714 static char *app3 = "MailboxExists";
00715 static char *app4 = "VMAuthenticate";
00716 
00717 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00718 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00719 static char zonetag[80];
00720 static int maxsilence;
00721 static int maxmsg;
00722 static int maxdeletedmsg;
00723 static int silencethreshold = 128;
00724 static char serveremail[80];
00725 static char mailcmd[160];  /* Configurable mail cmd */
00726 static char externnotify[160]; 
00727 static struct ast_smdi_interface *smdi_iface = NULL;
00728 static char vmfmts[80];
00729 static double volgain;
00730 static int vmminsecs;
00731 static int vmmaxsecs;
00732 static int maxgreet;
00733 static int skipms;
00734 static int maxlogins;
00735 static int minpassword;
00736 
00737 /*! Poll mailboxes for changes since there is something external to
00738  *  app_voicemail that may change them. */
00739 static unsigned int poll_mailboxes;
00740 
00741 /*! Polling frequency */
00742 static unsigned int poll_freq;
00743 /*! By default, poll every 30 seconds */
00744 #define DEFAULT_POLL_FREQ 30
00745 
00746 AST_MUTEX_DEFINE_STATIC(poll_lock);
00747 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00748 static pthread_t poll_thread = AST_PTHREADT_NULL;
00749 static unsigned char poll_thread_run;
00750 
00751 /*! Subscription to ... MWI event subscriptions */
00752 static struct ast_event_sub *mwi_sub_sub;
00753 /*! Subscription to ... MWI event un-subscriptions */
00754 static struct ast_event_sub *mwi_unsub_sub;
00755 
00756 /*!
00757  * \brief An MWI subscription
00758  *
00759  * This is so we can keep track of which mailboxes are subscribed to.
00760  * This way, we know which mailboxes to poll when the pollmailboxes
00761  * option is being used.
00762  */
00763 struct mwi_sub {
00764    AST_RWLIST_ENTRY(mwi_sub) entry;
00765    int old_urgent;
00766    int old_new;
00767    int old_old;
00768    uint32_t uniqueid;
00769    char mailbox[1];
00770 };
00771 
00772 struct mwi_sub_task {
00773    const char *mailbox;
00774    const char *context;
00775    uint32_t uniqueid;
00776 };
00777 
00778 static struct ast_taskprocessor *mwi_subscription_tps;
00779 
00780 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00781 
00782 /* custom audio control prompts for voicemail playback */
00783 static char listen_control_forward_key[12];
00784 static char listen_control_reverse_key[12];
00785 static char listen_control_pause_key[12];
00786 static char listen_control_restart_key[12];
00787 static char listen_control_stop_key[12];
00788 
00789 /* custom password sounds */
00790 static char vm_password[80] = "vm-password";
00791 static char vm_newpassword[80] = "vm-newpassword";
00792 static char vm_passchanged[80] = "vm-passchanged";
00793 static char vm_reenterpassword[80] = "vm-reenterpassword";
00794 static char vm_mismatch[80] = "vm-mismatch";
00795 static char vm_invalid_password[80] = "vm-invalid-password";
00796 static char vm_pls_try_again[80] = "vm-pls-try-again";
00797 
00798 static struct ast_flags globalflags = {0};
00799 
00800 static int saydurationminfo;
00801 
00802 static char dialcontext[AST_MAX_CONTEXT] = "";
00803 static char callcontext[AST_MAX_CONTEXT] = "";
00804 static char exitcontext[AST_MAX_CONTEXT] = "";
00805 
00806 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00807 
00808 
00809 static char *emailbody = NULL;
00810 static char *emailsubject = NULL;
00811 static char *pagerbody = NULL;
00812 static char *pagersubject = NULL;
00813 static char fromstring[100];
00814 static char pagerfromstring[100];
00815 static char charset[32] = "ISO-8859-1";
00816 
00817 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00818 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00819 static int adsiver = 1;
00820 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00821 
00822 /* Forward declarations - generic */
00823 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00824 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);
00825 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00826 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00827          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00828          signed char record_gain, struct vm_state *vms, char *flag);
00829 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00830 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00831 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);
00832 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);
00833 static void apply_options(struct ast_vm_user *vmu, const char *options);
00834 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);
00835 static int is_valid_dtmf(const char *key);
00836 
00837 struct ao2_container *inprocess_container;
00838 
00839 struct inprocess {
00840    int count;
00841    char *context;
00842    char mailbox[0];
00843 };
00844 
00845 static int inprocess_hash_fn(const void *obj, const int flags)
00846 {
00847    const struct inprocess *i = obj;
00848    return atoi(i->mailbox);
00849 }
00850 
00851 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00852 {
00853    struct inprocess *i = obj, *j = arg;
00854    if (!strcmp(i->mailbox, j->mailbox)) {
00855       return 0;
00856    }
00857    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00858 }
00859 
00860 static int inprocess_count(const char *context, const char *mailbox, int delta)
00861 {
00862    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00863    arg->context = arg->mailbox + strlen(mailbox) + 1;
00864    strcpy(arg->mailbox, mailbox); /* SAFE */
00865    strcpy(arg->context, context); /* SAFE */
00866    ao2_lock(inprocess_container);
00867    if ((i = ao2_find(inprocess_container, arg, 0))) {
00868       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00869       ao2_unlock(inprocess_container);
00870       ao2_ref(i, -1);
00871       return ret;
00872    }
00873    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00874       ao2_unlock(inprocess_container);
00875       return 0;
00876    }
00877    i->context = i->mailbox + strlen(mailbox) + 1;
00878    strcpy(i->mailbox, mailbox); /* SAFE */
00879    strcpy(i->context, context); /* SAFE */
00880    i->count = delta;
00881    ao2_link(inprocess_container, i);
00882    ao2_unlock(inprocess_container);
00883    ao2_ref(i, -1);
00884    return 0;
00885 }
00886 
00887 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00888 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00889 #endif
00890 
00891 /*!
00892  * \brief Strips control and non 7-bit clean characters from input string.
00893  *
00894  * \note To map control and none 7-bit characters to a 7-bit clean characters
00895  *  please use ast_str_encode_mine().
00896  */
00897 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00898 {
00899    char *bufptr = buf;
00900    for (; *input; input++) {
00901       if (*input < 32) {
00902          continue;
00903       }
00904       *bufptr++ = *input;
00905       if (bufptr == buf + buflen - 1) {
00906          break;
00907       }
00908    }
00909    *bufptr = '\0';
00910    return buf;
00911 }
00912 
00913 
00914 /*!
00915  * \brief Sets default voicemail system options to a voicemail user.
00916  *
00917  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00918  * - all the globalflags
00919  * - the saydurationminfo
00920  * - the callcontext
00921  * - the dialcontext
00922  * - the exitcontext
00923  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00924  * - volume gain.
00925  */
00926 static void populate_defaults(struct ast_vm_user *vmu)
00927 {
00928    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00929    if (saydurationminfo)
00930       vmu->saydurationm = saydurationminfo;
00931    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00932    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00933    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00934    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00935    if (vmmaxsecs)
00936       vmu->maxsecs = vmmaxsecs;
00937    if (maxmsg)
00938       vmu->maxmsg = maxmsg;
00939    if (maxdeletedmsg)
00940       vmu->maxdeletedmsg = maxdeletedmsg;
00941    vmu->volgain = volgain;
00942    vmu->emailsubject = NULL;
00943    vmu->emailbody = NULL;
00944 }
00945 
00946 /*!
00947  * \brief Sets a a specific property value.
00948  * \param vmu The voicemail user object to work with.
00949  * \param var The name of the property to be set.
00950  * \param value The value to be set to the property.
00951  * 
00952  * The property name must be one of the understood properties. See the source for details.
00953  */
00954 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00955 {
00956    int x;
00957    if (!strcasecmp(var, "attach")) {
00958       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00959    } else if (!strcasecmp(var, "attachfmt")) {
00960       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00961    } else if (!strcasecmp(var, "serveremail")) {
00962       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00963    } else if (!strcasecmp(var, "language")) {
00964       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00965    } else if (!strcasecmp(var, "tz")) {
00966       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00967 #ifdef IMAP_STORAGE
00968    } else if (!strcasecmp(var, "imapuser")) {
00969       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00970       vmu->imapversion = imapversion;
00971    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00972       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00973       vmu->imapversion = imapversion;
00974    } else if (!strcasecmp(var, "imapvmshareid")) {
00975       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00976       vmu->imapversion = imapversion;
00977 #endif
00978    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00979       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00980    } else if (!strcasecmp(var, "saycid")){
00981       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00982    } else if (!strcasecmp(var,"sendvoicemail")){
00983       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00984    } else if (!strcasecmp(var, "review")){
00985       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00986    } else if (!strcasecmp(var, "tempgreetwarn")){
00987       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00988    } else if (!strcasecmp(var, "messagewrap")){
00989       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00990    } else if (!strcasecmp(var, "operator")) {
00991       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00992    } else if (!strcasecmp(var, "envelope")){
00993       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00994    } else if (!strcasecmp(var, "moveheard")){
00995       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00996    } else if (!strcasecmp(var, "sayduration")){
00997       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00998    } else if (!strcasecmp(var, "saydurationm")){
00999       if (sscanf(value, "%30d", &x) == 1) {
01000          vmu->saydurationm = x;
01001       } else {
01002          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01003       }
01004    } else if (!strcasecmp(var, "forcename")){
01005       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01006    } else if (!strcasecmp(var, "forcegreetings")){
01007       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01008    } else if (!strcasecmp(var, "callback")) {
01009       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01010    } else if (!strcasecmp(var, "dialout")) {
01011       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01012    } else if (!strcasecmp(var, "exitcontext")) {
01013       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01014    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01015       vmu->maxsecs = atoi(value);
01016       if (vmu->maxsecs <= 0) {
01017          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01018          vmu->maxsecs = vmmaxsecs;
01019       } else {
01020          vmu->maxsecs = atoi(value);
01021       }
01022       if (!strcasecmp(var, "maxmessage"))
01023          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01024    } else if (!strcasecmp(var, "maxmsg")) {
01025       vmu->maxmsg = atoi(value);
01026       if (vmu->maxmsg <= 0) {
01027          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01028          vmu->maxmsg = MAXMSG;
01029       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01030          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01031          vmu->maxmsg = MAXMSGLIMIT;
01032       }
01033    } else if (!strcasecmp(var, "backupdeleted")) {
01034       if (sscanf(value, "%30d", &x) == 1)
01035          vmu->maxdeletedmsg = x;
01036       else if (ast_true(value))
01037          vmu->maxdeletedmsg = MAXMSG;
01038       else
01039          vmu->maxdeletedmsg = 0;
01040 
01041       if (vmu->maxdeletedmsg < 0) {
01042          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01043          vmu->maxdeletedmsg = MAXMSG;
01044       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01045          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01046          vmu->maxdeletedmsg = MAXMSGLIMIT;
01047       }
01048    } else if (!strcasecmp(var, "volgain")) {
01049       sscanf(value, "%30lf", &vmu->volgain);
01050    } else if (!strcasecmp(var, "options")) {
01051       apply_options(vmu, value);
01052    }
01053 }
01054 
01055 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01056 {
01057    int fds[2], pid = 0;
01058 
01059    memset(buf, 0, len);
01060 
01061    if (pipe(fds)) {
01062       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01063    } else {
01064       /* good to go*/
01065       pid = ast_safe_fork(0);
01066 
01067       if (pid < 0) {
01068          /* ok maybe not */
01069          close(fds[0]);
01070          close(fds[1]);
01071          snprintf(buf, len, "FAILURE: Fork failed");
01072       } else if (pid) {
01073          /* parent */
01074          close(fds[1]);
01075          if (read(fds[0], buf, len) < 0) {
01076             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01077          }
01078          close(fds[0]);
01079       } else {
01080          /*  child */
01081          AST_DECLARE_APP_ARGS(arg,
01082             AST_APP_ARG(v)[20];
01083          );
01084          char *mycmd = ast_strdupa(command);
01085 
01086          close(fds[0]);
01087          dup2(fds[1], STDOUT_FILENO);
01088          close(fds[1]);
01089          ast_close_fds_above_n(STDOUT_FILENO);
01090 
01091          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01092 
01093          execv(arg.v[0], arg.v); 
01094          printf("FAILURE: %s", strerror(errno));
01095          _exit(0);
01096       }
01097    }
01098    return buf;
01099 }
01100 
01101 /*!
01102  * \brief Check that password meets minimum required length
01103  * \param vmu The voicemail user to change the password for.
01104  * \param password The password string to check
01105  *
01106  * \return zero on ok, 1 on not ok.
01107  */
01108 static int check_password(struct ast_vm_user *vmu, char *password)
01109 {
01110    /* check minimum length */
01111    if (strlen(password) < minpassword)
01112       return 1;
01113    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01114       char cmd[255], buf[255];
01115 
01116       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01117 
01118       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01119       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01120          ast_debug(5, "Result: %s\n", buf);
01121          if (!strncasecmp(buf, "VALID", 5)) {
01122             ast_debug(3, "Passed password check: '%s'\n", buf);
01123             return 0;
01124          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01125             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01126             return 0;
01127          } else {
01128             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01129             return 1;
01130          }
01131       }
01132    }
01133    return 0;
01134 }
01135 
01136 /*! 
01137  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01138  * \param vmu The voicemail user to change the password for.
01139  * \param password The new value to be set to the password for this user.
01140  * 
01141  * This only works if there is a realtime engine configured.
01142  * This is called from the (top level) vm_change_password.
01143  *
01144  * \return zero on success, -1 on error.
01145  */
01146 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01147 {
01148    int res = -1;
01149    if (!strcmp(vmu->password, password)) {
01150       /* No change (but an update would return 0 rows updated, so we opt out here) */
01151       return 0;
01152    }
01153 
01154    if (strlen(password) > 10) {
01155       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01156    }
01157    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01158       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01159       res = 0;
01160    }
01161    return res;
01162 }
01163 
01164 /*!
01165  * \brief Destructively Parse options and apply.
01166  */
01167 static void apply_options(struct ast_vm_user *vmu, const char *options)
01168 {  
01169    char *stringp;
01170    char *s;
01171    char *var, *value;
01172    stringp = ast_strdupa(options);
01173    while ((s = strsep(&stringp, "|"))) {
01174       value = s;
01175       if ((var = strsep(&value, "=")) && value) {
01176          apply_option(vmu, var, value);
01177       }
01178    }  
01179 }
01180 
01181 /*!
01182  * \brief Loads the options specific to a voicemail user.
01183  * 
01184  * This is called when a vm_user structure is being set up, such as from load_options.
01185  */
01186 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01187 {
01188    for (; var; var = var->next) {
01189       if (!strcasecmp(var->name, "vmsecret")) {
01190          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01191       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01192          if (ast_strlen_zero(retval->password))
01193             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01194       } else if (!strcasecmp(var->name, "uniqueid")) {
01195          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01196       } else if (!strcasecmp(var->name, "pager")) {
01197          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01198       } else if (!strcasecmp(var->name, "email")) {
01199          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01200       } else if (!strcasecmp(var->name, "fullname")) {
01201          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01202       } else if (!strcasecmp(var->name, "context")) {
01203          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01204       } else if (!strcasecmp(var->name, "emailsubject")) {
01205          retval->emailsubject = ast_strdup(var->value);
01206       } else if (!strcasecmp(var->name, "emailbody")) {
01207          retval->emailbody = ast_strdup(var->value);
01208 #ifdef IMAP_STORAGE
01209       } else if (!strcasecmp(var->name, "imapuser")) {
01210          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01211          retval->imapversion = imapversion;
01212       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01213          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01214          retval->imapversion = imapversion;
01215       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01216          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01217          retval->imapversion = imapversion;
01218 #endif
01219       } else
01220          apply_option(retval, var->name, var->value);
01221    }
01222 }
01223 
01224 /*!
01225  * \brief Determines if a DTMF key entered is valid.
01226  * \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.
01227  *
01228  * Tests the character entered against the set of valid DTMF characters. 
01229  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01230  */
01231 static int is_valid_dtmf(const char *key)
01232 {
01233    int i;
01234    char *local_key = ast_strdupa(key);
01235 
01236    for (i = 0; i < strlen(key); ++i) {
01237       if (!strchr(VALID_DTMF, *local_key)) {
01238          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01239          return 0;
01240       }
01241       local_key++;
01242    }
01243    return 1;
01244 }
01245 
01246 /*!
01247  * \brief Finds a voicemail user from the realtime engine.
01248  * \param ivm
01249  * \param context
01250  * \param mailbox
01251  *
01252  * 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.
01253  *
01254  * \return The ast_vm_user structure for the user that was found.
01255  */
01256 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01257 {
01258    struct ast_variable *var;
01259    struct ast_vm_user *retval;
01260 
01261    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01262       if (!ivm)
01263          ast_set_flag(retval, VM_ALLOCED);   
01264       else
01265          memset(retval, 0, sizeof(*retval));
01266       if (mailbox) 
01267          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01268       populate_defaults(retval);
01269       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01270          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01271       else
01272          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01273       if (var) {
01274          apply_options_full(retval, var);
01275          ast_variables_destroy(var);
01276       } else { 
01277          if (!ivm) 
01278             ast_free(retval);
01279          retval = NULL;
01280       }  
01281    } 
01282    return retval;
01283 }
01284 
01285 /*!
01286  * \brief Finds a voicemail user from the users file or the realtime engine.
01287  * \param ivm
01288  * \param context
01289  * \param mailbox
01290  * 
01291  * \return The ast_vm_user structure for the user that was found.
01292  */
01293 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01294 {
01295    /* This function could be made to generate one from a database, too */
01296    struct ast_vm_user *vmu=NULL, *cur;
01297    AST_LIST_LOCK(&users);
01298 
01299    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01300       context = "default";
01301 
01302    AST_LIST_TRAVERSE(&users, cur, list) {
01303 #ifdef IMAP_STORAGE
01304       if (cur->imapversion != imapversion) {
01305          continue;
01306       }
01307 #endif
01308       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01309          break;
01310       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01311          break;
01312    }
01313    if (cur) {
01314       /* Make a copy, so that on a reload, we have no race */
01315       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01316          memcpy(vmu, cur, sizeof(*vmu));
01317          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01318          AST_LIST_NEXT(vmu, list) = NULL;
01319       }
01320    } else
01321       vmu = find_user_realtime(ivm, context, mailbox);
01322    AST_LIST_UNLOCK(&users);
01323    return vmu;
01324 }
01325 
01326 /*!
01327  * \brief Resets a user password to a specified password.
01328  * \param context
01329  * \param mailbox
01330  * \param newpass
01331  *
01332  * This does the actual change password work, called by the vm_change_password() function.
01333  *
01334  * \return zero on success, -1 on error.
01335  */
01336 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01337 {
01338    /* This function could be made to generate one from a database, too */
01339    struct ast_vm_user *cur;
01340    int res = -1;
01341    AST_LIST_LOCK(&users);
01342    AST_LIST_TRAVERSE(&users, cur, list) {
01343       if ((!context || !strcasecmp(context, cur->context)) &&
01344          (!strcasecmp(mailbox, cur->mailbox)))
01345             break;
01346    }
01347    if (cur) {
01348       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01349       res = 0;
01350    }
01351    AST_LIST_UNLOCK(&users);
01352    return res;
01353 }
01354 
01355 /*! 
01356  * \brief The handler for the change password option.
01357  * \param vmu The voicemail user to work with.
01358  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01359  * 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.
01360  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01361  */
01362 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01363 {
01364    struct ast_config   *cfg=NULL;
01365    struct ast_variable *var=NULL;
01366    struct ast_category *cat=NULL;
01367    char *category=NULL, *value=NULL, *new=NULL;
01368    const char *tmp=NULL;
01369    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01370    if (!change_password_realtime(vmu, newpassword))
01371       return;
01372 
01373    /* check voicemail.conf */
01374    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01375       while ((category = ast_category_browse(cfg, category))) {
01376          if (!strcasecmp(category, vmu->context)) {
01377             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01378                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01379                break;
01380             }
01381             value = strstr(tmp,",");
01382             if (!value) {
01383                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01384                break;
01385             }
01386             new = alloca((strlen(value)+strlen(newpassword)+1));
01387             sprintf(new,"%s%s", newpassword, value);
01388             if (!(cat = ast_category_get(cfg, category))) {
01389                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01390                break;
01391             }
01392             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01393          }
01394       }
01395       /* save the results */
01396       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01397       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01398       ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01399    }
01400    category = NULL;
01401    var = NULL;
01402    /* check users.conf and update the password stored for the mailbox*/
01403    /* if no vmsecret entry exists create one. */
01404    if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01405       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01406       while ((category = ast_category_browse(cfg, category))) {
01407          ast_debug(4, "users.conf: %s\n", category);
01408          if (!strcasecmp(category, vmu->mailbox)) {
01409             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01410                ast_debug(3, "looks like we need to make vmsecret!\n");
01411                var = ast_variable_new("vmsecret", newpassword, "");
01412             } 
01413             new = alloca(strlen(newpassword)+1);
01414             sprintf(new, "%s", newpassword);
01415             if (!(cat = ast_category_get(cfg, category))) {
01416                ast_debug(4, "failed to get category!\n");
01417                break;
01418             }
01419             if (!var)      
01420                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01421             else
01422                ast_variable_append(cat, var);
01423          }
01424       }
01425       /* save the results and clean things up */
01426       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01427       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01428       ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01429    }
01430 }
01431 
01432 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01433 {
01434    char buf[255];
01435    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01436    if (!ast_safe_system(buf)) {
01437       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01438       /* Reset the password in memory, too */
01439       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01440    }
01441 }
01442 
01443 /*! 
01444  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01445  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01446  * \param len The length of the path string that was written out.
01447  * 
01448  * The path is constructed as 
01449  *    VM_SPOOL_DIRcontext/ext/folder
01450  *
01451  * \return zero on success, -1 on error.
01452  */
01453 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01454 {
01455    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01456 }
01457 
01458 /*! 
01459  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01460  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01461  * \param len The length of the path string that was written out.
01462  * 
01463  * The path is constructed as 
01464  *    VM_SPOOL_DIRcontext/ext/folder
01465  *
01466  * \return zero on success, -1 on error.
01467  */
01468 static int make_file(char *dest, const int len, const char *dir, const int num)
01469 {
01470    return snprintf(dest, len, "%s/msg%04d", dir, num);
01471 }
01472 
01473 /* same as mkstemp, but return a FILE * */
01474 static FILE *vm_mkftemp(char *template)
01475 {
01476    FILE *p = NULL;
01477    int pfd = mkstemp(template);
01478    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01479    if (pfd > -1) {
01480       p = fdopen(pfd, "w+");
01481       if (!p) {
01482          close(pfd);
01483          pfd = -1;
01484       }
01485    }
01486    return p;
01487 }
01488 
01489 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01490  * \param dest    String. base directory.
01491  * \param len     Length of dest.
01492  * \param context String. Ignored if is null or empty string.
01493  * \param ext     String. Ignored if is null or empty string.
01494  * \param folder  String. Ignored if is null or empty string. 
01495  * \return -1 on failure, 0 on success.
01496  */
01497 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01498 {
01499    mode_t   mode = VOICEMAIL_DIR_MODE;
01500    int res;
01501 
01502    make_dir(dest, len, context, ext, folder);
01503    if ((res = ast_mkdir(dest, mode))) {
01504       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01505       return -1;
01506    }
01507    return 0;
01508 }
01509 
01510 static const char *mbox(int id)
01511 {
01512    static const char *msgs[] = {
01513 #ifdef IMAP_STORAGE
01514       imapfolder,
01515 #else
01516       "INBOX",
01517 #endif
01518       "Old",
01519       "Work",
01520       "Family",
01521       "Friends",
01522       "Cust1",
01523       "Cust2",
01524       "Cust3",
01525       "Cust4",
01526       "Cust5",
01527       "Deleted",
01528       "Urgent"
01529    };
01530    return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
01531 }
01532 
01533 static void free_user(struct ast_vm_user *vmu)
01534 {
01535    if (ast_test_flag(vmu, VM_ALLOCED)) {
01536       if (vmu->emailbody != NULL) {
01537          ast_free(vmu->emailbody);
01538          vmu->emailbody = NULL;
01539       }
01540       if (vmu->emailsubject != NULL) {
01541          ast_free(vmu->emailsubject);
01542          vmu->emailsubject = NULL;
01543       }
01544       ast_free(vmu);
01545    }
01546 }
01547 
01548 /* All IMAP-specific functions should go in this block. This
01549  * keeps them from being spread out all over the code */
01550 #ifdef IMAP_STORAGE
01551 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01552 {
01553    char arg[10];
01554    struct vm_state *vms;
01555    unsigned long messageNum;
01556 
01557    /* If greetings aren't stored in IMAP, just delete the file */
01558    if (msgnum < 0 && !imapgreetings) {
01559       ast_filedelete(file, NULL);
01560       return;
01561    }
01562 
01563    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01564       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);
01565       return;
01566    }
01567 
01568    /* find real message number based on msgnum */
01569    /* this may be an index into vms->msgArray based on the msgnum. */
01570    messageNum = vms->msgArray[msgnum];
01571    if (messageNum == 0) {
01572       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01573       return;
01574    }
01575    if (option_debug > 2)
01576       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01577    /* delete message */
01578    snprintf (arg, sizeof(arg), "%lu",messageNum);
01579    ast_mutex_lock(&vms->lock);
01580    mail_setflag (vms->mailstream,arg,"\\DELETED");
01581    mail_expunge(vms->mailstream);
01582    ast_mutex_unlock(&vms->lock);
01583 }
01584 
01585 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01586 {
01587    struct vm_state *vms_p;
01588    char *file, *filename;
01589    char *attachment;
01590    int ret = 0, i;
01591    BODY *body;
01592 
01593    /* This function is only used for retrieval of IMAP greetings
01594     * regular messages are not retrieved this way, nor are greetings
01595     * if they are stored locally*/
01596    if (msgnum > -1 || !imapgreetings) {
01597       return 0;
01598    } else {
01599       file = strrchr(ast_strdupa(dir), '/');
01600       if (file)
01601          *file++ = '\0';
01602       else {
01603          ast_debug (1, "Failed to procure file name from directory passed.\n");
01604          return -1;
01605       }
01606    }
01607 
01608    /* check if someone is accessing this box right now... */
01609    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01610       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01611       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01612       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01613       * that's all we need to do.
01614       */
01615       if (!(vms_p = create_vm_state_from_user(vmu))) {
01616          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01617          return -1;
01618       }
01619    }
01620    
01621    /* Greetings will never have a prepended message */
01622    *vms_p->introfn = '\0';
01623 
01624    ast_mutex_lock(&vms_p->lock);
01625    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01626    if (!vms_p->mailstream) {
01627       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01628       ast_mutex_unlock(&vms_p->lock);
01629       return -1;
01630    }
01631 
01632    /*XXX Yuck, this could probably be done a lot better */
01633    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01634       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01635       /* We have the body, now we extract the file name of the first attachment. */
01636       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01637          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01638       } else {
01639          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01640          ast_mutex_unlock(&vms_p->lock);
01641          return -1;
01642       }
01643       filename = strsep(&attachment, ".");
01644       if (!strcmp(filename, file)) {
01645          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01646          vms_p->msgArray[vms_p->curmsg] = i + 1;
01647          save_body(body, vms_p, "2", attachment, 0);
01648          ast_mutex_unlock(&vms_p->lock);
01649          return 0;
01650       }
01651    }
01652    ast_mutex_unlock(&vms_p->lock);
01653 
01654    return -1;
01655 }
01656 
01657 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01658 {
01659    BODY *body;
01660    char *header_content;
01661    char *attachedfilefmt;
01662    char buf[80];
01663    struct vm_state *vms;
01664    char text_file[PATH_MAX];
01665    FILE *text_file_ptr;
01666    int res = 0;
01667    struct ast_vm_user *vmu;
01668 
01669    if (!(vmu = find_user(NULL, context, mailbox))) {
01670       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01671       return -1;
01672    }
01673    
01674    if (msgnum < 0) {
01675       if (imapgreetings) {
01676          res = imap_retrieve_greeting(dir, msgnum, vmu);
01677          goto exit;
01678       } else {
01679          res = 0;
01680          goto exit;
01681       }
01682    }
01683 
01684    /* Before anything can happen, we need a vm_state so that we can
01685     * actually access the imap server through the vms->mailstream
01686     */
01687    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01688       /* This should not happen. If it does, then I guess we'd
01689        * need to create the vm_state, extract which mailbox to
01690        * open, and then set up the msgArray so that the correct
01691        * IMAP message could be accessed. If I have seen correctly
01692        * though, the vms should be obtainable from the vmstates list
01693        * and should have its msgArray properly set up.
01694        */
01695       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01696       res = -1;
01697       goto exit;
01698    }
01699    
01700    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01701    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01702 
01703    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01704    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01705       res = 0;
01706       goto exit;
01707    }
01708 
01709    if (option_debug > 2)
01710       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01711    if (vms->msgArray[msgnum] == 0) {
01712       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01713       res = -1;
01714       goto exit;
01715    }
01716 
01717    /* This will only work for new messages... */
01718    ast_mutex_lock(&vms->lock);
01719    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01720    ast_mutex_unlock(&vms->lock);
01721    /* empty string means no valid header */
01722    if (ast_strlen_zero(header_content)) {
01723       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01724       res = -1;
01725       goto exit;
01726    }
01727 
01728    ast_mutex_lock(&vms->lock);
01729    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01730    ast_mutex_unlock(&vms->lock);
01731 
01732    /* We have the body, now we extract the file name of the first attachment. */
01733    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01734       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01735    } else {
01736       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01737       res = -1;
01738       goto exit;
01739    }
01740    
01741    /* Find the format of the attached file */
01742 
01743    strsep(&attachedfilefmt, ".");
01744    if (!attachedfilefmt) {
01745       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01746       res = -1;
01747       goto exit;
01748    }
01749    
01750    save_body(body, vms, "2", attachedfilefmt, 0);
01751    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01752       *vms->introfn = '\0';
01753    }
01754 
01755    /* Get info from headers!! */
01756    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01757 
01758    if (!(text_file_ptr = fopen(text_file, "w"))) {
01759       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01760    }
01761 
01762    fprintf(text_file_ptr, "%s\n", "[message]");
01763 
01764    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01765    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01766    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01767    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01768    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01769    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01770    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01771    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01772    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01773    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01774    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01775    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01776    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01777    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01778    fclose(text_file_ptr);
01779 
01780 exit:
01781    free_user(vmu);
01782    return res;
01783 }
01784 
01785 static int folder_int(const char *folder)
01786 {
01787    /*assume a NULL folder means INBOX*/
01788    if (!folder) {
01789       return 0;
01790    }
01791    if (!strcasecmp(folder, imapfolder)) {
01792       return 0;
01793    } else if (!strcasecmp(folder, "Old")) {
01794       return 1;
01795    } else if (!strcasecmp(folder, "Work")) {
01796       return 2;
01797    } else if (!strcasecmp(folder, "Family")) {
01798       return 3;
01799    } else if (!strcasecmp(folder, "Friends")) {
01800       return 4;
01801    } else if (!strcasecmp(folder, "Cust1")) {
01802       return 5;
01803    } else if (!strcasecmp(folder, "Cust2")) {
01804       return 6;
01805    } else if (!strcasecmp(folder, "Cust3")) {
01806       return 7;
01807    } else if (!strcasecmp(folder, "Cust4")) {
01808       return 8;
01809    } else if (!strcasecmp(folder, "Cust5")) {
01810       return 9;
01811    } else if (!strcasecmp(folder, "Urgent")) {
01812       return 11;
01813    } else { /*assume they meant INBOX if folder is not found otherwise*/
01814       return 0;
01815    }
01816 }
01817 
01818 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01819 {
01820    SEARCHPGM *pgm;
01821    SEARCHHEADER *hdr;
01822 
01823    struct ast_vm_user *vmu, vmus;
01824    struct vm_state *vms_p;
01825    int ret = 0;
01826    int fold = folder_int(folder);
01827    int urgent = 0;
01828    
01829    /* If URGENT, then look at INBOX */
01830    if (fold == 11) {
01831       fold = NEW_FOLDER;
01832       urgent = 1;
01833    }
01834 
01835    if (ast_strlen_zero(mailbox))
01836       return 0;
01837 
01838    /* We have to get the user before we can open the stream! */
01839    vmu = find_user(&vmus, context, mailbox);
01840    if (!vmu) {
01841       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01842       return -1;
01843    } else {
01844       /* No IMAP account available */
01845       if (vmu->imapuser[0] == '\0') {
01846          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01847          return -1;
01848       }
01849    }
01850    
01851    /* No IMAP account available */
01852    if (vmu->imapuser[0] == '\0') {
01853       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01854       free_user(vmu);
01855       return -1;
01856    }
01857 
01858    /* check if someone is accessing this box right now... */
01859    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01860    if (!vms_p) {
01861       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01862    }
01863    if (vms_p) {
01864       ast_debug(3, "Returning before search - user is logged in\n");
01865       if (fold == 0) { /* INBOX */
01866          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
01867       }
01868       if (fold == 1) { /* Old messages */
01869          return vms_p->oldmessages;
01870       }
01871    }
01872 
01873    /* add one if not there... */
01874    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01875    if (!vms_p) {
01876       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01877    }
01878 
01879    if (!vms_p) {
01880       vms_p = create_vm_state_from_user(vmu);
01881    }
01882    ret = init_mailstream(vms_p, fold);
01883    if (!vms_p->mailstream) {
01884       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01885       return -1;
01886    }
01887    if (ret == 0) {
01888       ast_mutex_lock(&vms_p->lock);
01889       pgm = mail_newsearchpgm ();
01890       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01891       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01892       pgm->header = hdr;
01893       if (fold != OLD_FOLDER) {
01894          pgm->unseen = 1;
01895          pgm->seen = 0;
01896       }
01897       /* In the special case where fold is 1 (old messages) we have to do things a bit
01898        * differently. Old messages are stored in the INBOX but are marked as "seen"
01899        */
01900       else {
01901          pgm->unseen = 0;
01902          pgm->seen = 1;
01903       }
01904       /* look for urgent messages */
01905       if (fold == NEW_FOLDER) {
01906          if (urgent) {
01907             pgm->flagged = 1;
01908             pgm->unflagged = 0;
01909          } else {
01910             pgm->flagged = 0;
01911             pgm->unflagged = 1;
01912          }
01913       }
01914       pgm->undeleted = 1;
01915       pgm->deleted = 0;
01916 
01917       vms_p->vmArrayIndex = 0;
01918       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01919       if (fold == 0 && urgent == 0)
01920          vms_p->newmessages = vms_p->vmArrayIndex;
01921       if (fold == 1)
01922          vms_p->oldmessages = vms_p->vmArrayIndex;
01923       if (fold == 0 && urgent == 1)
01924          vms_p->urgentmessages = vms_p->vmArrayIndex;
01925       /*Freeing the searchpgm also frees the searchhdr*/
01926       mail_free_searchpgm(&pgm);
01927       ast_mutex_unlock(&vms_p->lock);
01928       vms_p->updated = 0;
01929       return vms_p->vmArrayIndex;
01930    } else {
01931       ast_mutex_lock(&vms_p->lock);
01932       mail_ping(vms_p->mailstream);
01933       ast_mutex_unlock(&vms_p->lock);
01934    }
01935    return 0;
01936 }
01937 
01938 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01939 {
01940    /* Check if mailbox is full */
01941    check_quota(vms, imapfolder);
01942    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01943       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01944       ast_play_and_wait(chan, "vm-mailboxfull");
01945       return -1;
01946    }
01947    
01948    /* Check if we have exceeded maxmsg */
01949    if (msgnum >= vmu->maxmsg  - inprocess_count(vmu->mailbox, vmu->context, 0)) {
01950       ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
01951       ast_play_and_wait(chan, "vm-mailboxfull");
01952       return -1;
01953    }
01954 
01955    return 0;
01956 }
01957 
01958 /*!
01959  * \brief Gets the number of messages that exist in a mailbox folder.
01960  * \param context
01961  * \param mailbox
01962  * \param folder
01963  * 
01964  * This method is used when IMAP backend is used.
01965  * \return The number of messages in this mailbox folder (zero or more).
01966  */
01967 static int messagecount(const char *context, const char *mailbox, const char *folder)
01968 {
01969    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
01970       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
01971    } else {
01972       return __messagecount(context, mailbox, folder);
01973    }
01974 }
01975 
01976 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)
01977 {
01978    char *myserveremail = serveremail;
01979    char fn[PATH_MAX];
01980    char introfn[PATH_MAX];
01981    char mailbox[256];
01982    char *stringp;
01983    FILE *p=NULL;
01984    char tmp[80] = "/tmp/astmail-XXXXXX";
01985    long len;
01986    void *buf;
01987    int tempcopy = 0;
01988    STRING str;
01989    int ret; /* for better error checking */
01990    char *imap_flags = NIL;
01991    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
01992 
01993     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
01994     if (msgnum < 0 && !imapgreetings) {
01995         return 0;
01996     }
01997    
01998    if (imap_check_limits(chan, vms, vmu, msgcount)) {
01999       return -1;
02000    }
02001 
02002    /* Set urgent flag for IMAP message */
02003    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02004       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02005       imap_flags="\\FLAGGED";
02006    }
02007    
02008    /* Attach only the first format */
02009    fmt = ast_strdupa(fmt);
02010    stringp = fmt;
02011    strsep(&stringp, "|");
02012 
02013    if (!ast_strlen_zero(vmu->serveremail))
02014       myserveremail = vmu->serveremail;
02015 
02016    if (msgnum > -1)
02017       make_file(fn, sizeof(fn), dir, msgnum);
02018    else
02019       ast_copy_string (fn, dir, sizeof(fn));
02020 
02021    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02022    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02023       *introfn = '\0';
02024    }
02025    
02026    if (ast_strlen_zero(vmu->email)) {
02027       /* We need the vmu->email to be set when we call make_email_file, but
02028        * if we keep it set, a duplicate e-mail will be created. So at the end
02029        * of this function, we will revert back to an empty string if tempcopy
02030        * is 1.
02031        */
02032       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02033       tempcopy = 1;
02034    }
02035 
02036    if (!strcmp(fmt, "wav49"))
02037       fmt = "WAV";
02038    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02039 
02040    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02041       command hangs. */
02042    if (!(p = vm_mkftemp(tmp))) {
02043       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02044       if (tempcopy)
02045          *(vmu->email) = '\0';
02046       return -1;
02047    }
02048 
02049    if (msgnum < 0 && imapgreetings) {
02050       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02051          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02052          return -1;
02053       }
02054       imap_delete_old_greeting(fn, vms);
02055    }
02056 
02057    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);
02058    /* read mail file to memory */
02059    len = ftell(p);
02060    rewind(p);
02061    if (!(buf = ast_malloc(len + 1))) {
02062       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02063       fclose(p);
02064       if (tempcopy)
02065          *(vmu->email) = '\0';
02066       return -1;
02067    }
02068    if (fread(buf, len, 1, p) < len) {
02069       if (ferror(p)) {
02070          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02071          return -1;
02072       }
02073    }
02074    ((char *)buf)[len] = '\0';
02075    INIT(&str, mail_string, buf, len);
02076    ret = init_mailstream(vms, NEW_FOLDER);
02077    if (ret == 0) {
02078       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02079       ast_mutex_lock(&vms->lock);
02080       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02081          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02082       ast_mutex_unlock(&vms->lock);
02083       fclose(p);
02084       unlink(tmp);
02085       ast_free(buf);
02086    } else {
02087       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
02088       fclose(p);
02089       unlink(tmp);
02090       ast_free(buf);
02091       return -1;
02092    }
02093    ast_debug(3, "%s stored\n", fn);
02094    
02095    if (tempcopy)
02096       *(vmu->email) = '\0';
02097    
02098    return 0;
02099 
02100 }
02101 
02102 /*!
02103  * \brief Gets the number of messages that exist in the inbox folder.
02104  * \param mailbox_context
02105  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02106  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02107  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02108  * 
02109  * This method is used when IMAP backend is used.
02110  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02111  *
02112  * \return zero on success, -1 on error.
02113  */
02114 
02115 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02116 {
02117    char tmp[PATH_MAX] = "";
02118    char *mailboxnc;
02119    char *context;
02120    char *mb;
02121    char *cur;
02122    if (newmsgs)
02123       *newmsgs = 0;
02124    if (oldmsgs)
02125       *oldmsgs = 0;
02126    if (urgentmsgs)
02127       *urgentmsgs = 0;
02128 
02129    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
02130    /* If no mailbox, return immediately */
02131    if (ast_strlen_zero(mailbox_context))
02132       return 0;
02133    
02134    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02135    context = strchr(tmp, '@');
02136    if (strchr(mailbox_context, ',')) {
02137       int tmpnew, tmpold, tmpurgent;
02138       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02139       mb = tmp;
02140       while ((cur = strsep(&mb, ", "))) {
02141          if (!ast_strlen_zero(cur)) {
02142             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02143                return -1;
02144             else {
02145                if (newmsgs)
02146                   *newmsgs += tmpnew; 
02147                if (oldmsgs)
02148                   *oldmsgs += tmpold;
02149                if (urgentmsgs)
02150                   *urgentmsgs += tmpurgent;
02151             }
02152          }
02153       }
02154       return 0;
02155    }
02156    if (context) {
02157       *context = '\0';
02158       mailboxnc = tmp;
02159       context++;
02160    } else {
02161       context = "default";
02162       mailboxnc = (char *)mailbox_context;
02163    }
02164    if (newmsgs) {
02165       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02166          return -1;
02167       }
02168    }
02169    if (oldmsgs) {
02170       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02171          return -1;
02172       }
02173    }
02174    if (urgentmsgs) {
02175       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02176          return -1;
02177       }
02178    }
02179    return 0;
02180 }
02181 
02182 /** 
02183  * \brief Determines if the given folder has messages.
02184  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02185  * \param folder the folder to look in
02186  *
02187  * This function is used when the mailbox is stored in an IMAP back end.
02188  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02189  * \return 1 if the folder has one or more messages. zero otherwise.
02190  */
02191 
02192 static int has_voicemail(const char *mailbox, const char *folder)
02193 {
02194    char tmp[256], *tmp2, *box, *context;
02195    ast_copy_string(tmp, mailbox, sizeof(tmp));
02196    tmp2 = tmp;
02197    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02198       while ((box = strsep(&tmp2, ",&"))) {
02199          if (!ast_strlen_zero(box)) {
02200             if (has_voicemail(box, folder)) {
02201                return 1;
02202             }
02203          }
02204       }
02205    }
02206    if ((context = strchr(tmp, '@'))) {
02207       *context++ = '\0';
02208    } else {
02209       context = "default";
02210    }
02211    return __messagecount(context, tmp, folder) ? 1 : 0;
02212 }
02213 
02214 /*!
02215  * \brief Copies a message from one mailbox to another.
02216  * \param chan
02217  * \param vmu
02218  * \param imbox
02219  * \param msgnum
02220  * \param duration
02221  * \param recip
02222  * \param fmt
02223  * \param dir
02224  *
02225  * This works with IMAP storage based mailboxes.
02226  *
02227  * \return zero on success, -1 on error.
02228  */
02229 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)
02230 {
02231    struct vm_state *sendvms = NULL, *destvms = NULL;
02232    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02233    if (msgnum >= recip->maxmsg) {
02234       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02235       return -1;
02236    }
02237    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02238       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02239       return -1;
02240    }
02241    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02242       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02243       return -1;
02244    }
02245    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02246    ast_mutex_lock(&sendvms->lock);
02247    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02248       ast_mutex_unlock(&sendvms->lock);
02249       return 0;
02250    }
02251    ast_mutex_unlock(&sendvms->lock);
02252    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02253    return -1;
02254 }
02255 
02256 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02257 {
02258    char tmp[256], *t = tmp;
02259    size_t left = sizeof(tmp);
02260    
02261    if (box == OLD_FOLDER) {
02262       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02263    } else {
02264       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02265    }
02266 
02267    if (box == NEW_FOLDER) {
02268       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02269    } else {
02270       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02271    }
02272 
02273    /* Build up server information */
02274    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02275 
02276    /* Add authentication user if present */
02277    if (!ast_strlen_zero(authuser))
02278       ast_build_string(&t, &left, "/authuser=%s", authuser);
02279 
02280    /* Add flags if present */
02281    if (!ast_strlen_zero(imapflags))
02282       ast_build_string(&t, &left, "/%s", imapflags);
02283 
02284    /* End with username */
02285 #if 1
02286    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02287 #else
02288    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02289 #endif
02290    if (box == NEW_FOLDER || box == OLD_FOLDER)
02291       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02292    else if (box == GREETINGS_FOLDER)
02293       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02294    else {   /* Other folders such as Friends, Family, etc... */
02295       if (!ast_strlen_zero(imapparentfolder)) {
02296          /* imapparentfolder would typically be set to INBOX */
02297          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02298       } else {
02299          snprintf(spec, len, "%s%s", tmp, mbox(box));
02300       }
02301    }
02302 }
02303 
02304 static int init_mailstream(struct vm_state *vms, int box)
02305 {
02306    MAILSTREAM *stream = NIL;
02307    long debug;
02308    char tmp[256];
02309    
02310    if (!vms) {
02311       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02312       return -1;
02313    }
02314    if (option_debug > 2)
02315       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02316    if (vms->mailstream == NIL || !vms->mailstream) {
02317       if (option_debug)
02318          ast_log (LOG_DEBUG,"mailstream not set.\n");
02319    } else {
02320       stream = vms->mailstream;
02321    }
02322    /* debug = T;  user wants protocol telemetry? */
02323    debug = NIL;  /* NO protocol telemetry? */
02324 
02325    if (delimiter == '\0') {      /* did not probe the server yet */
02326       char *cp;
02327 #ifdef USE_SYSTEM_IMAP
02328 #include <imap/linkage.c>
02329 #elif defined(USE_SYSTEM_CCLIENT)
02330 #include <c-client/linkage.c>
02331 #else
02332 #include "linkage.c"
02333 #endif
02334       /* Connect to INBOX first to get folders delimiter */
02335       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02336       ast_mutex_lock(&vms->lock);
02337       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02338       ast_mutex_unlock(&vms->lock);
02339       if (stream == NIL) {
02340          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02341          return -1;
02342       }
02343       get_mailbox_delimiter(stream);
02344       /* update delimiter in imapfolder */
02345       for (cp = imapfolder; *cp; cp++)
02346          if (*cp == '/')
02347             *cp = delimiter;
02348    }
02349    /* Now connect to the target folder */
02350    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02351    if (option_debug > 2)
02352       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02353    ast_mutex_lock(&vms->lock);
02354    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02355    ast_mutex_unlock(&vms->lock);
02356    if (vms->mailstream == NIL) {
02357       return -1;
02358    } else {
02359       return 0;
02360    }
02361 }
02362 
02363 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02364 {
02365    SEARCHPGM *pgm;
02366    SEARCHHEADER *hdr;
02367    int ret, urgent = 0;
02368 
02369    /* If Urgent, then look at INBOX */
02370    if (box == 11) {
02371       box = NEW_FOLDER;
02372       urgent = 1;
02373    }
02374 
02375    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02376    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02377    vms->imapversion = vmu->imapversion;
02378 
02379    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02380       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02381       return -1;
02382    }
02383    
02384    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02385    
02386    /* Check Quota */
02387    if  (box == 0)  {
02388       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02389       check_quota(vms,(char *)mbox(box));
02390    }
02391 
02392    ast_mutex_lock(&vms->lock);
02393    pgm = mail_newsearchpgm();
02394 
02395    /* Check IMAP folder for Asterisk messages only... */
02396    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02397    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02398    pgm->header = hdr;
02399    pgm->deleted = 0;
02400    pgm->undeleted = 1;
02401 
02402    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02403    if (box == NEW_FOLDER && urgent == 1) {
02404       pgm->unseen = 1;
02405       pgm->seen = 0;
02406       pgm->flagged = 1;
02407       pgm->unflagged = 0;
02408    } else if (box == NEW_FOLDER && urgent == 0) {
02409       pgm->unseen = 1;
02410       pgm->seen = 0;
02411       pgm->flagged = 0;
02412       pgm->unflagged = 1;
02413    } else if (box == OLD_FOLDER) {
02414       pgm->seen = 1;
02415       pgm->unseen = 0;
02416    }
02417 
02418    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02419 
02420    vms->vmArrayIndex = 0;
02421    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02422    vms->lastmsg = vms->vmArrayIndex - 1;
02423    mail_free_searchpgm(&pgm);
02424 
02425    ast_mutex_unlock(&vms->lock);
02426    return 0;
02427 }
02428 
02429 static void write_file(char *filename, char *buffer, unsigned long len)
02430 {
02431    FILE *output;
02432 
02433    output = fopen (filename, "w");
02434    if (fwrite(buffer, len, 1, output) != 1) {
02435       if (ferror(output)) {
02436          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02437       }
02438    }
02439    fclose (output);
02440 }
02441 
02442 static void update_messages_by_imapuser(const char *user, unsigned long number)
02443 {
02444    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02445 
02446    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02447       return;
02448    }
02449 
02450    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02451    vms->msgArray[vms->vmArrayIndex++] = number;
02452 }
02453 
02454 void mm_searched(MAILSTREAM *stream, unsigned long number)
02455 {
02456    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02457 
02458    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02459       return;
02460 
02461    update_messages_by_imapuser(user, number);
02462 }
02463 
02464 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02465 {
02466    struct ast_variable *var;
02467    struct ast_vm_user *vmu;
02468 
02469    vmu = ast_calloc(1, sizeof *vmu);
02470    if (!vmu)
02471       return NULL;
02472    ast_set_flag(vmu, VM_ALLOCED);
02473    populate_defaults(vmu);
02474 
02475    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02476    if (var) {
02477       apply_options_full(vmu, var);
02478       ast_variables_destroy(var);
02479       return vmu;
02480    } else {
02481       ast_free(vmu);
02482       return NULL;
02483    }
02484 }
02485 
02486 /* Interfaces to C-client */
02487 
02488 void mm_exists(MAILSTREAM * stream, unsigned long number)
02489 {
02490    /* mail_ping will callback here if new mail! */
02491    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02492    if (number == 0) return;
02493    set_update(stream);
02494 }
02495 
02496 
02497 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02498 {
02499    /* mail_ping will callback here if expunged mail! */
02500    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02501    if (number == 0) return;
02502    set_update(stream);
02503 }
02504 
02505 
02506 void mm_flags(MAILSTREAM * stream, unsigned long number)
02507 {
02508    /* mail_ping will callback here if read mail! */
02509    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02510    if (number == 0) return;
02511    set_update(stream);
02512 }
02513 
02514 
02515 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02516 {
02517    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02518    mm_log (string, errflg);
02519 }
02520 
02521 
02522 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02523 {
02524    if (delimiter == '\0') {
02525       delimiter = delim;
02526    }
02527 
02528    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02529    if (attributes & LATT_NOINFERIORS)
02530       ast_debug(5, "no inferiors\n");
02531    if (attributes & LATT_NOSELECT)
02532       ast_debug(5, "no select\n");
02533    if (attributes & LATT_MARKED)
02534       ast_debug(5, "marked\n");
02535    if (attributes & LATT_UNMARKED)
02536       ast_debug(5, "unmarked\n");
02537 }
02538 
02539 
02540 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02541 {
02542    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02543    if (attributes & LATT_NOINFERIORS)
02544       ast_debug(5, "no inferiors\n");
02545    if (attributes & LATT_NOSELECT)
02546       ast_debug(5, "no select\n");
02547    if (attributes & LATT_MARKED)
02548       ast_debug(5, "marked\n");
02549    if (attributes & LATT_UNMARKED)
02550       ast_debug(5, "unmarked\n");
02551 }
02552 
02553 
02554 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02555 {
02556    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02557    if (status->flags & SA_MESSAGES)
02558       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02559    if (status->flags & SA_RECENT)
02560       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02561    if (status->flags & SA_UNSEEN)
02562       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02563    if (status->flags & SA_UIDVALIDITY)
02564       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02565    if (status->flags & SA_UIDNEXT)
02566       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02567    ast_log(AST_LOG_NOTICE, "\n");
02568 }
02569 
02570 
02571 void mm_log(char *string, long errflg)
02572 {
02573    switch ((short) errflg) {
02574       case NIL:
02575          ast_debug(1,"IMAP Info: %s\n", string);
02576          break;
02577       case PARSE:
02578       case WARN:
02579          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02580          break;
02581       case ERROR:
02582          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02583          break;
02584    }
02585 }
02586 
02587 
02588 void mm_dlog(char *string)
02589 {
02590    ast_log(AST_LOG_NOTICE, "%s\n", string);
02591 }
02592 
02593 
02594 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02595 {
02596    struct ast_vm_user *vmu;
02597 
02598    ast_debug(4, "Entering callback mm_login\n");
02599 
02600    ast_copy_string(user, mb->user, MAILTMPLEN);
02601 
02602    /* We should only do this when necessary */
02603    if (!ast_strlen_zero(authpassword)) {
02604       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02605    } else {
02606       AST_LIST_TRAVERSE(&users, vmu, list) {
02607          if (!strcasecmp(mb->user, vmu->imapuser)) {
02608             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02609             break;
02610          }
02611       }
02612       if (!vmu) {
02613          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02614             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02615             free_user(vmu);
02616          }
02617       }
02618    }
02619 }
02620 
02621 
02622 void mm_critical(MAILSTREAM * stream)
02623 {
02624 }
02625 
02626 
02627 void mm_nocritical(MAILSTREAM * stream)
02628 {
02629 }
02630 
02631 
02632 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02633 {
02634    kill (getpid (), SIGSTOP);
02635    return NIL;
02636 }
02637 
02638 
02639 void mm_fatal(char *string)
02640 {
02641    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02642 }
02643 
02644 /* C-client callback to handle quota */
02645 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02646 {
02647    struct vm_state *vms;
02648    char *mailbox = stream->mailbox, *user;
02649    char buf[1024] = "";
02650    unsigned long usage = 0, limit = 0;
02651    
02652    while (pquota) {
02653       usage = pquota->usage;
02654       limit = pquota->limit;
02655       pquota = pquota->next;
02656    }
02657    
02658    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)))) {
02659       ast_log(AST_LOG_ERROR, "No state found.\n");
02660       return;
02661    }
02662 
02663    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02664 
02665    vms->quota_usage = usage;
02666    vms->quota_limit = limit;
02667 }
02668 
02669 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02670 {
02671    char *start, *eol_pnt;
02672    int taglen;
02673 
02674    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02675       return NULL;
02676 
02677    taglen = strlen(tag) + 1;
02678    if (taglen < 1)
02679       return NULL;
02680 
02681    if (!(start = strstr(header, tag)))
02682       return NULL;
02683 
02684    /* Since we can be called multiple times we should clear our buffer */
02685    memset(buf, 0, len);
02686 
02687    ast_copy_string(buf, start+taglen, len);
02688    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02689       *eol_pnt = '\0';
02690    return buf;
02691 }
02692 
02693 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02694 {
02695    char *start, *quote, *eol_pnt;
02696 
02697    if (ast_strlen_zero(mailbox))
02698       return NULL;
02699 
02700    if (!(start = strstr(mailbox, "/user=")))
02701       return NULL;
02702 
02703    ast_copy_string(buf, start+6, len);
02704 
02705    if (!(quote = strchr(buf, '\"'))) {
02706       if (!(eol_pnt = strchr(buf, '/')))
02707          eol_pnt = strchr(buf,'}');
02708       *eol_pnt = '\0';
02709       return buf;
02710    } else {
02711       eol_pnt = strchr(buf+1,'\"');
02712       *eol_pnt = '\0';
02713       return buf+1;
02714    }
02715 }
02716 
02717 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02718 {
02719    struct vm_state *vms_p;
02720 
02721    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02722    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02723       return vms_p;
02724    }
02725    if (option_debug > 4)
02726       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02727    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02728       return NULL;
02729    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02730    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02731    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02732    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02733    vms_p->imapversion = vmu->imapversion;
02734    if (option_debug > 4)
02735       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02736    vms_p->updated = 1;
02737    /* set mailbox to INBOX! */
02738    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02739    init_vm_state(vms_p);
02740    vmstate_insert(vms_p);
02741    return vms_p;
02742 }
02743 
02744 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02745 {
02746    struct vmstate *vlist = NULL;
02747 
02748    if (interactive) {
02749       struct vm_state *vms;
02750       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02751       vms = pthread_getspecific(ts_vmstate.key);
02752       return vms;
02753    }
02754 
02755    AST_LIST_LOCK(&vmstates);
02756    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02757       if (!vlist->vms) {
02758          ast_debug(3, "error: vms is NULL for %s\n", user);
02759          continue;
02760       }
02761       if (vlist->vms->imapversion != imapversion) {
02762          continue;
02763       }
02764       if (!vlist->vms->imapuser) {
02765          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02766          continue;
02767       }
02768 
02769       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02770          AST_LIST_UNLOCK(&vmstates);
02771          return vlist->vms;
02772       }
02773    }
02774    AST_LIST_UNLOCK(&vmstates);
02775 
02776    ast_debug(3, "%s not found in vmstates\n", user);
02777 
02778    return NULL;
02779 }
02780 
02781 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02782 {
02783 
02784    struct vmstate *vlist = NULL;
02785    const char *local_context = S_OR(context, "default");
02786 
02787    if (interactive) {
02788       struct vm_state *vms;
02789       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02790       vms = pthread_getspecific(ts_vmstate.key);
02791       return vms;
02792    }
02793 
02794    AST_LIST_LOCK(&vmstates);
02795    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02796       if (!vlist->vms) {
02797          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02798          continue;
02799       }
02800       if (vlist->vms->imapversion != imapversion) {
02801          continue;
02802       }
02803       if (!vlist->vms->username || !vlist->vms->context) {
02804          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02805          continue;
02806       }
02807 
02808       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);
02809       
02810       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02811          ast_debug(3, "Found it!\n");
02812          AST_LIST_UNLOCK(&vmstates);
02813          return vlist->vms;
02814       }
02815    }
02816    AST_LIST_UNLOCK(&vmstates);
02817 
02818    ast_debug(3, "%s not found in vmstates\n", mailbox);
02819 
02820    return NULL;
02821 }
02822 
02823 static void vmstate_insert(struct vm_state *vms) 
02824 {
02825    struct vmstate *v;
02826    struct vm_state *altvms;
02827 
02828    /* If interactive, it probably already exists, and we should
02829       use the one we already have since it is more up to date.
02830       We can compare the username to find the duplicate */
02831    if (vms->interactive == 1) {
02832       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02833       if (altvms) {  
02834          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02835          vms->newmessages = altvms->newmessages;
02836          vms->oldmessages = altvms->oldmessages;
02837          vms->vmArrayIndex = altvms->vmArrayIndex;
02838          vms->lastmsg = altvms->lastmsg;
02839          vms->curmsg = altvms->curmsg;
02840          /* get a pointer to the persistent store */
02841          vms->persist_vms = altvms;
02842          /* Reuse the mailstream? */
02843 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02844          vms->mailstream = altvms->mailstream;
02845 #else
02846          vms->mailstream = NIL;
02847 #endif
02848       }
02849       return;
02850    }
02851 
02852    if (!(v = ast_calloc(1, sizeof(*v))))
02853       return;
02854    
02855    v->vms = vms;
02856 
02857    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02858 
02859    AST_LIST_LOCK(&vmstates);
02860    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02861    AST_LIST_UNLOCK(&vmstates);
02862 }
02863 
02864 static void vmstate_delete(struct vm_state *vms) 
02865 {
02866    struct vmstate *vc = NULL;
02867    struct vm_state *altvms = NULL;
02868 
02869    /* If interactive, we should copy pertinent info
02870       back to the persistent state (to make update immediate) */
02871    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02872       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02873       altvms->newmessages = vms->newmessages;
02874       altvms->oldmessages = vms->oldmessages;
02875       altvms->updated = 1;
02876       vms->mailstream = mail_close(vms->mailstream);
02877 
02878       /* Interactive states are not stored within the persistent list */
02879       return;
02880    }
02881    
02882    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02883    
02884    AST_LIST_LOCK(&vmstates);
02885    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02886       if (vc->vms == vms) {
02887          AST_LIST_REMOVE_CURRENT(list);
02888          break;
02889       }
02890    }
02891    AST_LIST_TRAVERSE_SAFE_END
02892    AST_LIST_UNLOCK(&vmstates);
02893    
02894    if (vc) {
02895       ast_mutex_destroy(&vc->vms->lock);
02896       ast_free(vc);
02897    }
02898    else
02899       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02900 }
02901 
02902 static void set_update(MAILSTREAM * stream) 
02903 {
02904    struct vm_state *vms;
02905    char *mailbox = stream->mailbox, *user;
02906    char buf[1024] = "";
02907 
02908    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02909       if (user && option_debug > 2)
02910          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02911       return;
02912    }
02913 
02914    ast_debug(3, "User %s mailbox set for update.\n", user);
02915 
02916    vms->updated = 1; /* Set updated flag since mailbox changed */
02917 }
02918 
02919 static void init_vm_state(struct vm_state *vms) 
02920 {
02921    int x;
02922    vms->vmArrayIndex = 0;
02923    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02924       vms->msgArray[x] = 0;
02925    }
02926    ast_mutex_init(&vms->lock);
02927 }
02928 
02929 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02930 {
02931    char *body_content;
02932    char *body_decoded;
02933    char *fn = is_intro ? vms->introfn : vms->fn;
02934    unsigned long len;
02935    unsigned long newlen;
02936    char filename[256];
02937    
02938    if (!body || body == NIL)
02939       return -1;
02940 
02941    ast_mutex_lock(&vms->lock);
02942    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02943    ast_mutex_unlock(&vms->lock);
02944    if (body_content != NIL) {
02945       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02946       /* ast_debug(1,body_content); */
02947       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02948       /* If the body of the file is empty, return an error */
02949       if (!newlen) {
02950          return -1;
02951       }
02952       write_file(filename, (char *) body_decoded, newlen);
02953    } else {
02954       ast_debug(5, "Body of message is NULL.\n");
02955       return -1;
02956    }
02957    return 0;
02958 }
02959 
02960 /*! 
02961  * \brief Get delimiter via mm_list callback 
02962  * \param stream
02963  *
02964  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02965  */
02966 /* MUTEX should already be held */
02967 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02968    char tmp[50];
02969    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02970    mail_list(stream, tmp, "*");
02971 }
02972 
02973 /*! 
02974  * \brief Check Quota for user 
02975  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02976  * \param mailbox the mailbox to check the quota for.
02977  *
02978  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02979  */
02980 static void check_quota(struct vm_state *vms, char *mailbox) {
02981    ast_mutex_lock(&vms->lock);
02982    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02983    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02984    if (vms && vms->mailstream != NULL) {
02985       imap_getquotaroot(vms->mailstream, mailbox);
02986    } else {
02987       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02988    }
02989    ast_mutex_unlock(&vms->lock);
02990 }
02991 
02992 #endif /* IMAP_STORAGE */
02993 
02994 /*! \brief Lock file path
02995     only return failure if ast_lock_path returns 'timeout',
02996    not if the path does not exist or any other reason
02997 */
02998 static int vm_lock_path(const char *path)
02999 {
03000    switch (ast_lock_path(path)) {
03001    case AST_LOCK_TIMEOUT:
03002       return -1;
03003    default:
03004       return 0;
03005    }
03006 }
03007 
03008 
03009 #ifdef ODBC_STORAGE
03010 struct generic_prepare_struct {
03011    char *sql;
03012    int argc;
03013    char **argv;
03014 };
03015 
03016 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03017 {
03018    struct generic_prepare_struct *gps = data;
03019    int res, i;
03020    SQLHSTMT stmt;
03021 
03022    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03023    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03024       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03025       return NULL;
03026    }
03027    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
03028    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03029       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03030       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03031       return NULL;
03032    }
03033    for (i = 0; i < gps->argc; i++)
03034       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03035 
03036    return stmt;
03037 }
03038 
03039 /*!
03040  * \brief Retrieves a file from an ODBC data store.
03041  * \param dir the path to the file to be retreived.
03042  * \param msgnum the message number, such as within a mailbox folder.
03043  * 
03044  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03045  * 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.
03046  *
03047  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03048  * The output is the message information file with the name msgnum and the extension .txt
03049  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03050  * 
03051  * \return 0 on success, -1 on error.
03052  */
03053 static int retrieve_file(char *dir, int msgnum)
03054 {
03055    int x = 0;
03056    int res;
03057    int fd=-1;
03058    size_t fdlen = 0;
03059    void *fdm = MAP_FAILED;
03060    SQLSMALLINT colcount=0;
03061    SQLHSTMT stmt;
03062    char sql[PATH_MAX];
03063    char fmt[80]="";
03064    char *c;
03065    char coltitle[256];
03066    SQLSMALLINT collen;
03067    SQLSMALLINT datatype;
03068    SQLSMALLINT decimaldigits;
03069    SQLSMALLINT nullable;
03070    SQLULEN colsize;
03071    SQLLEN colsize2;
03072    FILE *f=NULL;
03073    char rowdata[80];
03074    char fn[PATH_MAX];
03075    char full_fn[PATH_MAX];
03076    char msgnums[80];
03077    char *argv[] = { dir, msgnums };
03078    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03079 
03080    struct odbc_obj *obj;
03081    obj = ast_odbc_request_obj(odbc_database, 0);
03082    if (obj) {
03083       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03084       c = strchr(fmt, '|');
03085       if (c)
03086          *c = '\0';
03087       if (!strcasecmp(fmt, "wav49"))
03088          strcpy(fmt, "WAV");
03089       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03090       if (msgnum > -1)
03091          make_file(fn, sizeof(fn), dir, msgnum);
03092       else
03093          ast_copy_string(fn, dir, sizeof(fn));
03094 
03095       /* Create the information file */
03096       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03097       
03098       if (!(f = fopen(full_fn, "w+"))) {
03099          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03100          goto yuck;
03101       }
03102       
03103       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03104       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03105       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03106       if (!stmt) {
03107          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03108          ast_odbc_release_obj(obj);
03109          goto yuck;
03110       }
03111       res = SQLFetch(stmt);
03112       if (res == SQL_NO_DATA) {
03113          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03114          ast_odbc_release_obj(obj);
03115          goto yuck;
03116       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03117          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03118          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03119          ast_odbc_release_obj(obj);
03120          goto yuck;
03121       }
03122       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03123       if (fd < 0) {
03124          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03125          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03126          ast_odbc_release_obj(obj);
03127          goto yuck;
03128       }
03129       res = SQLNumResultCols(stmt, &colcount);
03130       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03131          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03132          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03133          ast_odbc_release_obj(obj);
03134          goto yuck;
03135       }
03136       if (f) 
03137          fprintf(f, "[message]\n");
03138       for (x=0;x<colcount;x++) {
03139          rowdata[0] = '\0';
03140          collen = sizeof(coltitle);
03141          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
03142                   &datatype, &colsize, &decimaldigits, &nullable);
03143          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03144             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03145             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03146             ast_odbc_release_obj(obj);
03147             goto yuck;
03148          }
03149          if (!strcasecmp(coltitle, "recording")) {
03150             off_t offset;
03151             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03152             fdlen = colsize2;
03153             if (fd > -1) {
03154                char tmp[1]="";
03155                lseek(fd, fdlen - 1, SEEK_SET);
03156                if (write(fd, tmp, 1) != 1) {
03157                   close(fd);
03158                   fd = -1;
03159                   continue;
03160                }
03161                /* Read out in small chunks */
03162                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03163                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03164                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03165                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03166                      ast_odbc_release_obj(obj);
03167                      goto yuck;
03168                   } else {
03169                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03170                      munmap(fdm, CHUNKSIZE);
03171                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03172                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03173                         unlink(full_fn);
03174                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03175                         ast_odbc_release_obj(obj);
03176                         goto yuck;
03177                      }
03178                   }
03179                }
03180                if (truncate(full_fn, fdlen) < 0) {
03181                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03182                }
03183             }
03184          } else {
03185             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03186             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03187                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03188                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03189                ast_odbc_release_obj(obj);
03190                goto yuck;
03191             }
03192             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03193                fprintf(f, "%s=%s\n", coltitle, rowdata);
03194          }
03195       }
03196       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03197       ast_odbc_release_obj(obj);
03198    } else
03199       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03200 yuck: 
03201    if (f)
03202       fclose(f);
03203    if (fd > -1)
03204       close(fd);
03205    return x - 1;
03206 }
03207 
03208 /*!
03209  * \brief Determines the highest message number in use for a given user and mailbox folder.
03210  * \param vmu 
03211  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03212  *
03213  * This method is used when mailboxes are stored in an ODBC back end.
03214  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03215  *
03216  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03217  */
03218 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03219 {
03220    int x = 0;
03221    int res;
03222    SQLHSTMT stmt;
03223    char sql[PATH_MAX];
03224    char rowdata[20];
03225    char *argv[] = { dir };
03226    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03227 
03228    struct odbc_obj *obj;
03229    obj = ast_odbc_request_obj(odbc_database, 0);
03230    if (obj) {
03231       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03232       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03233       if (!stmt) {
03234          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03235          ast_odbc_release_obj(obj);
03236          goto yuck;
03237       }
03238       res = SQLFetch(stmt);
03239       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03240          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03241          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03242          ast_odbc_release_obj(obj);
03243          goto yuck;
03244       }
03245       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03246       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03247          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03248          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03249          ast_odbc_release_obj(obj);
03250          goto yuck;
03251       }
03252       if (sscanf(rowdata, "%30d", &x) != 1)
03253          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03254       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03255       ast_odbc_release_obj(obj);
03256    } else
03257       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03258 yuck: 
03259    return x - 1;
03260 }
03261 
03262 /*!
03263  * \brief Determines if the specified message exists.
03264  * \param dir the folder the mailbox folder to look for messages. 
03265  * \param msgnum the message index to query for.
03266  *
03267  * This method is used when mailboxes are stored in an ODBC back end.
03268  *
03269  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03270  */
03271 static int message_exists(char *dir, int msgnum)
03272 {
03273    int x = 0;
03274    int res;
03275    SQLHSTMT stmt;
03276    char sql[PATH_MAX];
03277    char rowdata[20];
03278    char msgnums[20];
03279    char *argv[] = { dir, msgnums };
03280    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03281 
03282    struct odbc_obj *obj;
03283    obj = ast_odbc_request_obj(odbc_database, 0);
03284    if (obj) {
03285       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03286       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03287       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03288       if (!stmt) {
03289          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03290          ast_odbc_release_obj(obj);
03291          goto yuck;
03292       }
03293       res = SQLFetch(stmt);
03294       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03295          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03296          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03297          ast_odbc_release_obj(obj);
03298          goto yuck;
03299       }
03300       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03301       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03302          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03303          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03304          ast_odbc_release_obj(obj);
03305          goto yuck;
03306       }
03307       if (sscanf(rowdata, "%30d", &x) != 1)
03308          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03309       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03310       ast_odbc_release_obj(obj);
03311    } else
03312       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03313 yuck: 
03314    return x;
03315 }
03316 
03317 /*!
03318  * \brief returns the one-based count for messages.
03319  * \param vmu
03320  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03321  *
03322  * This method is used when mailboxes are stored in an ODBC back end.
03323  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03324  * one-based messages.
03325  * This method just calls last_message_index and returns +1 of its value.
03326  *
03327  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03328  */
03329 static int count_messages(struct ast_vm_user *vmu, char *dir)
03330 {
03331    return last_message_index(vmu, dir) + 1;
03332 }
03333 
03334 /*!
03335  * \brief Deletes a message from the mailbox folder.
03336  * \param sdir The mailbox folder to work in.
03337  * \param smsg The message index to be deleted.
03338  *
03339  * This method is used when mailboxes are stored in an ODBC back end.
03340  * The specified message is directly deleted from the database 'voicemessages' table.
03341  * 
03342  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03343  */
03344 static void delete_file(const char *sdir, int smsg)
03345 {
03346    SQLHSTMT stmt;
03347    char sql[PATH_MAX];
03348    char msgnums[20];
03349    char *argv[] = { NULL, msgnums };
03350    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03351    struct odbc_obj *obj;
03352 
03353    argv[0] = ast_strdupa(sdir);
03354 
03355    obj = ast_odbc_request_obj(odbc_database, 0);
03356    if (obj) {
03357       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03358       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03359       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03360       if (!stmt)
03361          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03362       else
03363          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03364       ast_odbc_release_obj(obj);
03365    } else
03366       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03367    return;  
03368 }
03369 
03370 /*!
03371  * \brief Copies a voicemail from one mailbox to another.
03372  * \param sdir the folder for which to look for the message to be copied.
03373  * \param smsg the index of the message to be copied.
03374  * \param ddir the destination folder to copy the message into.
03375  * \param dmsg the index to be used for the copied message.
03376  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03377  * \param dmailboxcontext The context for the destination user.
03378  *
03379  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03380  */
03381 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03382 {
03383    SQLHSTMT stmt;
03384    char sql[512];
03385    char msgnums[20];
03386    char msgnumd[20];
03387    struct odbc_obj *obj;
03388    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03389    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03390 
03391    delete_file(ddir, dmsg);
03392    obj = ast_odbc_request_obj(odbc_database, 0);
03393    if (obj) {
03394       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03395       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03396       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);
03397       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03398       if (!stmt)
03399          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03400       else
03401          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03402       ast_odbc_release_obj(obj);
03403    } else
03404       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03405    return;  
03406 }
03407 
03408 struct insert_data {
03409    char *sql;
03410    const char *dir;
03411    const char *msgnums;
03412    void *data;
03413    SQLLEN datalen;
03414    SQLLEN indlen;
03415    const char *context;
03416    const char *macrocontext;
03417    const char *callerid;
03418    const char *origtime;
03419    const char *duration;
03420    const char *mailboxuser;
03421    const char *mailboxcontext;
03422    const char *category;
03423    const char *flag;
03424 };
03425 
03426 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03427 {
03428    struct insert_data *data = vdata;
03429    int res;
03430    SQLHSTMT stmt;
03431 
03432    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03433    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03434       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03435       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03436       return NULL;
03437    }
03438 
03439    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03440    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03441    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03442    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03443    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03444    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03445    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03446    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03447    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03448    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03449    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03450    if (!ast_strlen_zero(data->category)) {
03451       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03452    }
03453    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03454    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03455       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03456       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03457       return NULL;
03458    }
03459 
03460    return stmt;
03461 }
03462 
03463 /*!
03464  * \brief Stores a voicemail into the database.
03465  * \param dir the folder the mailbox folder to store the message.
03466  * \param mailboxuser the user owning the mailbox folder.
03467  * \param mailboxcontext
03468  * \param msgnum the message index for the message to be stored.
03469  *
03470  * This method is used when mailboxes are stored in an ODBC back end.
03471  * The message sound file and information file is looked up on the file system. 
03472  * A SQL query is invoked to store the message into the (MySQL) database.
03473  *
03474  * \return the zero on success -1 on error.
03475  */
03476 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03477 {
03478    int res = 0;
03479    int fd = -1;
03480    void *fdm = MAP_FAILED;
03481    size_t fdlen = -1;
03482    SQLHSTMT stmt;
03483    char sql[PATH_MAX];
03484    char msgnums[20];
03485    char fn[PATH_MAX];
03486    char full_fn[PATH_MAX];
03487    char fmt[80]="";
03488    char *c;
03489    struct ast_config *cfg=NULL;
03490    struct odbc_obj *obj;
03491    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03492       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03493    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03494 
03495    delete_file(dir, msgnum);
03496    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03497       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03498       return -1;
03499    }
03500 
03501    do {
03502       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03503       c = strchr(fmt, '|');
03504       if (c)
03505          *c = '\0';
03506       if (!strcasecmp(fmt, "wav49"))
03507          strcpy(fmt, "WAV");
03508       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03509       if (msgnum > -1)
03510          make_file(fn, sizeof(fn), dir, msgnum);
03511       else
03512          ast_copy_string(fn, dir, sizeof(fn));
03513       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03514       cfg = ast_config_load(full_fn, config_flags);
03515       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03516       fd = open(full_fn, O_RDWR);
03517       if (fd < 0) {
03518          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03519          res = -1;
03520          break;
03521       }
03522       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03523          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03524             idata.context = "";
03525          }
03526          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03527             idata.macrocontext = "";
03528          }
03529          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03530             idata.callerid = "";
03531          }
03532          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03533             idata.origtime = "";
03534          }
03535          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03536             idata.duration = "";
03537          }
03538          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03539             idata.category = "";
03540          }
03541          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03542             idata.flag = "";
03543          }
03544       }
03545       fdlen = lseek(fd, 0, SEEK_END);
03546       lseek(fd, 0, SEEK_SET);
03547       printf("Length is %zd\n", fdlen);
03548       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03549       if (fdm == MAP_FAILED) {
03550          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03551          res = -1;
03552          break;
03553       } 
03554       idata.data = fdm;
03555       idata.datalen = idata.indlen = fdlen;
03556 
03557       if (!ast_strlen_zero(idata.category)) 
03558          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03559       else
03560          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03561 
03562       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03563          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03564       } else {
03565          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03566          res = -1;
03567       }
03568    } while (0);
03569    if (obj) {
03570       ast_odbc_release_obj(obj);
03571    }
03572    if (cfg)
03573       ast_config_destroy(cfg);
03574    if (fdm != MAP_FAILED)
03575       munmap(fdm, fdlen);
03576    if (fd > -1)
03577       close(fd);
03578    return res;
03579 }
03580 
03581 /*!
03582  * \brief Renames a message in a mailbox folder.
03583  * \param sdir The folder of the message to be renamed.
03584  * \param smsg The index of the message to be renamed.
03585  * \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.
03586  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03587  * \param ddir The destination folder for the message to be renamed into
03588  * \param dmsg The destination message for the message to be renamed.
03589  *
03590  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03591  * 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.
03592  * 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.
03593  */
03594 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03595 {
03596    SQLHSTMT stmt;
03597    char sql[PATH_MAX];
03598    char msgnums[20];
03599    char msgnumd[20];
03600    struct odbc_obj *obj;
03601    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03602    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03603 
03604    delete_file(ddir, dmsg);
03605    obj = ast_odbc_request_obj(odbc_database, 0);
03606    if (obj) {
03607       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03608       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03609       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03610       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03611       if (!stmt)
03612          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03613       else
03614          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03615       ast_odbc_release_obj(obj);
03616    } else
03617       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03618    return;  
03619 }
03620 
03621 /*!
03622  * \brief Removes a voicemail message file.
03623  * \param dir the path to the message file.
03624  * \param msgnum the unique number for the message within the mailbox.
03625  *
03626  * Removes the message content file and the information file.
03627  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03628  * Typical use is to clean up after a RETRIEVE operation. 
03629  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03630  * \return zero on success, -1 on error.
03631  */
03632 static int remove_file(char *dir, int msgnum)
03633 {
03634    char fn[PATH_MAX];
03635    char full_fn[PATH_MAX];
03636    char msgnums[80];
03637    
03638    if (msgnum > -1) {
03639       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03640       make_file(fn, sizeof(fn), dir, msgnum);
03641    } else
03642       ast_copy_string(fn, dir, sizeof(fn));
03643    ast_filedelete(fn, NULL);  
03644    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03645    unlink(full_fn);
03646    return 0;
03647 }
03648 #else
03649 #ifndef IMAP_STORAGE
03650 /*!
03651  * \brief Find all .txt files - even if they are not in sequence from 0000.
03652  * \param vmu
03653  * \param dir
03654  *
03655  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03656  *
03657  * \return the count of messages, zero or more.
03658  */
03659 static int count_messages(struct ast_vm_user *vmu, char *dir)
03660 {
03661 
03662    int vmcount = 0;
03663    DIR *vmdir = NULL;
03664    struct dirent *vment = NULL;
03665 
03666    if (vm_lock_path(dir))
03667       return ERROR_LOCK_PATH;
03668 
03669    if ((vmdir = opendir(dir))) {
03670       while ((vment = readdir(vmdir))) {
03671          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03672             vmcount++;
03673          }
03674       }
03675       closedir(vmdir);
03676    }
03677    ast_unlock_path(dir);
03678    
03679    return vmcount;
03680 }
03681 
03682 /*!
03683  * \brief Renames a message in a mailbox folder.
03684  * \param sfn The path to the mailbox information and data file to be renamed.
03685  * \param dfn The path for where the message data and information files will be renamed to.
03686  *
03687  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03688  */
03689 static void rename_file(char *sfn, char *dfn)
03690 {
03691    char stxt[PATH_MAX];
03692    char dtxt[PATH_MAX];
03693    ast_filerename(sfn,dfn,NULL);
03694    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03695    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03696    if (ast_check_realtime("voicemail_data")) {
03697       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03698    }
03699    rename(stxt, dtxt);
03700 }
03701 
03702 /*! 
03703  * \brief Determines the highest message number in use for a given user and mailbox folder.
03704  * \param vmu 
03705  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03706  *
03707  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03708  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03709  *
03710  * \note Should always be called with a lock already set on dir.
03711  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03712  */
03713 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03714 {
03715    int x;
03716    unsigned char map[MAXMSGLIMIT] = "";
03717    DIR *msgdir;
03718    struct dirent *msgdirent;
03719    int msgdirint;
03720 
03721    /* Reading the entire directory into a file map scales better than
03722     * doing a stat repeatedly on a predicted sequence.  I suspect this
03723     * is partially due to stat(2) internally doing a readdir(2) itself to
03724     * find each file. */
03725    if (!(msgdir = opendir(dir))) {
03726       return -1;
03727    }
03728 
03729    while ((msgdirent = readdir(msgdir))) {
03730       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03731          map[msgdirint] = 1;
03732    }
03733    closedir(msgdir);
03734 
03735    for (x = 0; x < vmu->maxmsg; x++) {
03736       if (map[x] == 0)
03737          break;
03738    }
03739 
03740    return x - 1;
03741 }
03742 
03743 #endif /* #ifndef IMAP_STORAGE */
03744 #endif /* #else of #ifdef ODBC_STORAGE */
03745 #ifndef IMAP_STORAGE
03746 /*!
03747  * \brief Utility function to copy a file.
03748  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03749  * \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.
03750  *
03751  * 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.
03752  * The copy operation copies up to 4096 bytes at once.
03753  *
03754  * \return zero on success, -1 on error.
03755  */
03756 static int copy(char *infile, char *outfile)
03757 {
03758    int ifd;
03759    int ofd;
03760    int res;
03761    int len;
03762    char buf[4096];
03763 
03764 #ifdef HARDLINK_WHEN_POSSIBLE
03765    /* Hard link if possible; saves disk space & is faster */
03766    if (link(infile, outfile)) {
03767 #endif
03768       if ((ifd = open(infile, O_RDONLY)) < 0) {
03769          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03770          return -1;
03771       }
03772       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03773          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03774          close(ifd);
03775          return -1;
03776       }
03777       do {
03778          len = read(ifd, buf, sizeof(buf));
03779          if (len < 0) {
03780             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03781             close(ifd);
03782             close(ofd);
03783             unlink(outfile);
03784          }
03785          if (len) {
03786             res = write(ofd, buf, len);
03787             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03788                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03789                close(ifd);
03790                close(ofd);
03791                unlink(outfile);
03792             }
03793          }
03794       } while (len);
03795       close(ifd);
03796       close(ofd);
03797       return 0;
03798 #ifdef HARDLINK_WHEN_POSSIBLE
03799    } else {
03800       /* Hard link succeeded */
03801       return 0;
03802    }
03803 #endif
03804 }
03805 
03806 /*!
03807  * \brief Copies a voicemail information (envelope) file.
03808  * \param frompath
03809  * \param topath 
03810  *
03811  * Every voicemail has the data (.wav) file, and the information file.
03812  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03813  * This is used by the COPY macro when not using IMAP storage.
03814  */
03815 static void copy_plain_file(char *frompath, char *topath)
03816 {
03817    char frompath2[PATH_MAX], topath2[PATH_MAX];
03818    struct ast_variable *tmp,*var = NULL;
03819    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03820    ast_filecopy(frompath, topath, NULL);
03821    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03822    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03823    if (ast_check_realtime("voicemail_data")) {
03824       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03825       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03826       for (tmp = var; tmp; tmp = tmp->next) {
03827          if (!strcasecmp(tmp->name, "origmailbox")) {
03828             origmailbox = tmp->value;
03829          } else if (!strcasecmp(tmp->name, "context")) {
03830             context = tmp->value;
03831          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03832             macrocontext = tmp->value;
03833          } else if (!strcasecmp(tmp->name, "exten")) {
03834             exten = tmp->value;
03835          } else if (!strcasecmp(tmp->name, "priority")) {
03836             priority = tmp->value;
03837          } else if (!strcasecmp(tmp->name, "callerchan")) {
03838             callerchan = tmp->value;
03839          } else if (!strcasecmp(tmp->name, "callerid")) {
03840             callerid = tmp->value;
03841          } else if (!strcasecmp(tmp->name, "origdate")) {
03842             origdate = tmp->value;
03843          } else if (!strcasecmp(tmp->name, "origtime")) {
03844             origtime = tmp->value;
03845          } else if (!strcasecmp(tmp->name, "category")) {
03846             category = tmp->value;
03847          } else if (!strcasecmp(tmp->name, "duration")) {
03848             duration = tmp->value;
03849          }
03850       }
03851       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);
03852    }
03853    copy(frompath2, topath2);
03854    ast_variables_destroy(var);
03855 }
03856 #endif
03857 
03858 /*! 
03859  * \brief Removes the voicemail sound and information file.
03860  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03861  *
03862  * This is used by the DELETE macro when voicemails are stored on the file system.
03863  *
03864  * \return zero on success, -1 on error.
03865  */
03866 static int vm_delete(char *file)
03867 {
03868    char *txt;
03869    int txtsize = 0;
03870 
03871    txtsize = (strlen(file) + 5)*sizeof(char);
03872    txt = alloca(txtsize);
03873    /* Sprintf here would safe because we alloca'd exactly the right length,
03874     * but trying to eliminate all sprintf's anyhow
03875     */
03876    if (ast_check_realtime("voicemail_data")) {
03877       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03878    }
03879    snprintf(txt, txtsize, "%s.txt", file);
03880    unlink(txt);
03881    return ast_filedelete(file, NULL);
03882 }
03883 
03884 /*!
03885  * \brief utility used by inchar(), for base_encode()
03886  */
03887 static int inbuf(struct baseio *bio, FILE *fi)
03888 {
03889    int l;
03890 
03891    if (bio->ateof)
03892       return 0;
03893 
03894    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03895       if (ferror(fi))
03896          return -1;
03897 
03898       bio->ateof = 1;
03899       return 0;
03900    }
03901 
03902    bio->iolen= l;
03903    bio->iocp= 0;
03904 
03905    return 1;
03906 }
03907 
03908 /*!
03909  * \brief utility used by base_encode()
03910  */
03911 static int inchar(struct baseio *bio, FILE *fi)
03912 {
03913    if (bio->iocp>=bio->iolen) {
03914       if (!inbuf(bio, fi))
03915          return EOF;
03916    }
03917 
03918    return bio->iobuf[bio->iocp++];
03919 }
03920 
03921 /*!
03922  * \brief utility used by base_encode()
03923  */
03924 static int ochar(struct baseio *bio, int c, FILE *so)
03925 {
03926    if (bio->linelength >= BASELINELEN) {
03927       if (fputs(ENDL, so) == EOF) {
03928          return -1;
03929       }
03930 
03931       bio->linelength= 0;
03932    }
03933 
03934    if (putc(((unsigned char) c), so) == EOF) {
03935       return -1;
03936    }
03937 
03938    bio->linelength++;
03939 
03940    return 1;
03941 }
03942 
03943 /*!
03944  * \brief Performs a base 64 encode algorithm on the contents of a File
03945  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03946  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03947  *
03948  * 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 ?
03949  *
03950  * \return zero on success, -1 on error.
03951  */
03952 static int base_encode(char *filename, FILE *so)
03953 {
03954    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03955       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03956       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03957       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03958    int i,hiteof= 0;
03959    FILE *fi;
03960    struct baseio bio;
03961 
03962    memset(&bio, 0, sizeof(bio));
03963    bio.iocp = BASEMAXINLINE;
03964 
03965    if (!(fi = fopen(filename, "rb"))) {
03966       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03967       return -1;
03968    }
03969 
03970    while (!hiteof){
03971       unsigned char igroup[3], ogroup[4];
03972       int c,n;
03973 
03974       igroup[0]= igroup[1]= igroup[2]= 0;
03975 
03976       for (n= 0;n<3;n++) {
03977          if ((c = inchar(&bio, fi)) == EOF) {
03978             hiteof= 1;
03979             break;
03980          }
03981 
03982          igroup[n]= (unsigned char)c;
03983       }
03984 
03985       if (n> 0) {
03986          ogroup[0]= dtable[igroup[0]>>2];
03987          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03988          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03989          ogroup[3]= dtable[igroup[2]&0x3F];
03990 
03991          if (n<3) {
03992             ogroup[3]= '=';
03993 
03994             if (n<2)
03995                ogroup[2]= '=';
03996          }
03997 
03998          for (i= 0;i<4;i++)
03999             ochar(&bio, ogroup[i], so);
04000       }
04001    }
04002 
04003    fclose(fi);
04004 
04005    if (fputs(ENDL, so) == EOF) {
04006       return 0;
04007    }
04008 
04009    return 1;
04010 }
04011 
04012 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)
04013 {
04014    char callerid[256];
04015    char fromdir[256], fromfile[256];
04016    struct ast_config *msg_cfg;
04017    const char *origcallerid, *origtime;
04018    char origcidname[80], origcidnum[80], origdate[80];
04019    int inttime;
04020    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04021 
04022    /* Prepare variables for substitution in email body and subject */
04023    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04024    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04025    snprintf(passdata, passdatasize, "%d", msgnum);
04026    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
04027    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04028    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04029    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04030       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04031    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04032    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04033    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04034    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04035    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04036 
04037    /* Retrieve info from VM attribute file */
04038    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04039    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04040    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04041       strcat(fromfile, ".txt");
04042    }
04043    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04044       if (option_debug > 0) {
04045          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04046       }
04047       return;
04048    }
04049 
04050    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04051       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04052       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04053       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04054       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04055    }
04056 
04057    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04058       struct timeval tv = { inttime, };
04059       struct ast_tm tm;
04060       ast_localtime(&tv, &tm, NULL);
04061       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04062       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04063    }
04064    ast_config_destroy(msg_cfg);
04065 }
04066 
04067 /*!
04068  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04069  * \param from The string to work with.
04070  * \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.
04071  * 
04072  * \return The destination string with quotes wrapped on it (the to field).
04073  */
04074 static char *quote(const char *from, char *to, size_t len)
04075 {
04076    char *ptr = to;
04077    *ptr++ = '"';
04078    for (; ptr < to + len - 1; from++) {
04079       if (*from == '"')
04080          *ptr++ = '\\';
04081       else if (*from == '\0')
04082          break;
04083       *ptr++ = *from;
04084    }
04085    if (ptr < to + len - 1)
04086       *ptr++ = '"';
04087    *ptr = '\0';
04088    return to;
04089 }
04090 
04091 /*! \brief
04092  * fill in *tm for current time according to the proper timezone, if any.
04093  * Return tm so it can be used as a function argument.
04094  */
04095 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04096 {
04097    const struct vm_zone *z = NULL;
04098    struct timeval t = ast_tvnow();
04099 
04100    /* Does this user have a timezone specified? */
04101    if (!ast_strlen_zero(vmu->zonetag)) {
04102       /* Find the zone in the list */
04103       AST_LIST_LOCK(&zones);
04104       AST_LIST_TRAVERSE(&zones, z, list) {
04105          if (!strcmp(z->name, vmu->zonetag))
04106             break;
04107       }
04108       AST_LIST_UNLOCK(&zones);
04109    }
04110    ast_localtime(&t, tm, z ? z->timezone : NULL);
04111    return tm;
04112 }
04113 
04114 /*!\brief Check if the string would need encoding within the MIME standard, to
04115  * avoid confusing certain mail software that expects messages to be 7-bit
04116  * clean.
04117  */
04118 static int check_mime(const char *str)
04119 {
04120    for (; *str; str++) {
04121       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04122          return 1;
04123       }
04124    }
04125    return 0;
04126 }
04127 
04128 /*!\brief Encode a string according to the MIME rules for encoding strings
04129  * that are not 7-bit clean or contain control characters.
04130  *
04131  * Additionally, if the encoded string would exceed the MIME limit of 76
04132  * characters per line, then the encoding will be broken up into multiple
04133  * sections, separated by a space character, in order to facilitate
04134  * breaking up the associated header across multiple lines.
04135  *
04136  * \param start A string to be encoded
04137  * \param end An expandable buffer for holding the result
04138  * \param preamble The length of the first line already used for this string,
04139  * to ensure that each line maintains a maximum length of 76 chars.
04140  * \param postamble the length of any additional characters appended to the
04141  * line, used to ensure proper field wrapping.
04142  * \retval The encoded string.
04143  */
04144 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
04145 {
04146    char tmp[80];
04147    int first_section = 1;
04148    size_t endlen = 0, tmplen = 0;
04149    *end = '\0';
04150 
04151    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04152    for (; *start; start++) {
04153       int need_encoding = 0;
04154       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04155          need_encoding = 1;
04156       }
04157       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04158          (first_section && !need_encoding && preamble + tmplen > 72) ||
04159          (!first_section && need_encoding && tmplen > 70) ||
04160          (!first_section && !need_encoding && tmplen > 72)) {
04161          /* Start new line */
04162          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04163          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04164          first_section = 0;
04165       }
04166       if (need_encoding && *start == ' ') {
04167          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04168       } else if (need_encoding) {
04169          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04170       } else {
04171          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04172       }
04173    }
04174    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04175    return end;
04176 }
04177 
04178 /*!
04179  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04180  * \param p The output file to generate the email contents into.
04181  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04182  * \param vmu The voicemail user who is sending the voicemail.
04183  * \param msgnum The message index in the mailbox folder.
04184  * \param context 
04185  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04186  * \param cidnum The caller ID number.
04187  * \param cidname The caller ID name.
04188  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04189  * \param format The message sound file format. i.e. .wav
04190  * \param duration The time of the message content, in seconds.
04191  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04192  * \param chan
04193  * \param category
04194  * \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.
04195  *
04196  * 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.
04197  */
04198 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)
04199 {
04200    char date[256];
04201    char host[MAXHOSTNAMELEN] = "";
04202    char who[256];
04203    char bound[256];
04204    char dur[256];
04205    struct ast_tm tm;
04206    char enc_cidnum[256] = "", enc_cidname[256] = "";
04207    char *passdata = NULL, *passdata2;
04208    size_t len_passdata = 0, len_passdata2, tmplen;
04209    char *greeting_attachment; 
04210    char filename[256];
04211 
04212 
04213    /* One alloca for multiple fields */
04214    len_passdata2 = strlen(vmu->fullname);
04215    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04216       len_passdata2 = tmplen;
04217    }
04218    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04219       len_passdata2 = tmplen;
04220    }
04221    len_passdata2 = len_passdata2 * 3 + 200;
04222    passdata2 = alloca(len_passdata2);
04223 
04224    if (cidnum) {
04225       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04226    }
04227    if (cidname) {
04228       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04229    }
04230    gethostname(host, sizeof(host) - 1);
04231 
04232    if (strchr(srcemail, '@'))
04233       ast_copy_string(who, srcemail, sizeof(who));
04234    else 
04235       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04236    
04237    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04238    if (greeting_attachment)
04239       *greeting_attachment++ = '\0';
04240 
04241    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04242    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04243    fprintf(p, "Date: %s" ENDL, date);
04244 
04245    /* Set date format for voicemail mail */
04246    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04247 
04248    if (!ast_strlen_zero(fromstring)) {
04249       struct ast_channel *ast;
04250       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04251          char *ptr;
04252          memset(passdata2, 0, len_passdata2);
04253          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04254          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04255          len_passdata = strlen(passdata2) * 3 + 300;
04256          passdata = alloca(len_passdata);
04257          if (check_mime(passdata2)) {
04258             int first_line = 1;
04259             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04260             while ((ptr = strchr(passdata, ' '))) {
04261                *ptr = '\0';
04262                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04263                first_line = 0;
04264                passdata = ptr + 1;
04265             }
04266             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04267          } else {
04268             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04269          }
04270          ast_channel_free(ast);
04271       } else {
04272          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04273       }
04274    } else {
04275       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04276    }
04277 
04278    if (check_mime(vmu->fullname)) {
04279       int first_line = 1;
04280       char *ptr;
04281       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04282       while ((ptr = strchr(passdata2, ' '))) {
04283          *ptr = '\0';
04284          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04285          first_line = 0;
04286          passdata2 = ptr + 1;
04287       }
04288       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04289    } else {
04290       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04291    }
04292    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04293       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04294       struct ast_channel *ast;
04295       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04296          int vmlen = strlen(e_subj) * 3 + 200;
04297          /* Only allocate more space if the previous was not large enough */
04298          if (vmlen > len_passdata) {
04299             passdata = alloca(vmlen);
04300             len_passdata = vmlen;
04301          }
04302 
04303          memset(passdata, 0, len_passdata);
04304          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04305          pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
04306          if (check_mime(passdata)) {
04307             int first_line = 1;
04308             char *ptr;
04309             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04310             while ((ptr = strchr(passdata2, ' '))) {
04311                *ptr = '\0';
04312                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04313                first_line = 0;
04314                passdata2 = ptr + 1;
04315             }
04316             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04317          } else {
04318             fprintf(p, "Subject: %s" ENDL, passdata);
04319          }
04320          ast_channel_free(ast);
04321       } else {
04322          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04323       }
04324    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04325       if (ast_strlen_zero(flag)) {
04326          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04327       } else {
04328          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04329       }
04330    } else {
04331       if (ast_strlen_zero(flag)) {
04332          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04333       } else {
04334          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04335       }
04336    }
04337 
04338    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04339    if (imap) {
04340       /* additional information needed for IMAP searching */
04341       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04342       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04343       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04344       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04345 #ifdef IMAP_STORAGE
04346       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04347 #else
04348       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04349 #endif
04350       /* flag added for Urgent */
04351       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04352       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04353       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04354       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04355       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04356       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04357       if (!ast_strlen_zero(category)) {
04358          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04359       } else {
04360          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04361       }
04362       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04363       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04364       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04365    }
04366    if (!ast_strlen_zero(cidnum)) {
04367       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04368    }
04369    if (!ast_strlen_zero(cidname)) {
04370       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04371    }
04372    fprintf(p, "MIME-Version: 1.0" ENDL);
04373    if (attach_user_voicemail) {
04374       /* Something unique. */
04375       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04376 
04377       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04378       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04379       fprintf(p, "--%s" ENDL, bound);
04380    }
04381    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04382    if (emailbody || vmu->emailbody) {
04383       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04384       struct ast_channel *ast;
04385       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04386          char *passdata;
04387          int vmlen = strlen(e_body) * 3 + 200;
04388          passdata = alloca(vmlen);
04389          memset(passdata, 0, vmlen);
04390          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04391          pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
04392 #ifdef IMAP_STORAGE
04393          {
04394             /* Convert body to native line terminators for IMAP backend */
04395             char *line = passdata, *next;
04396             do {
04397                /* Terminate line before outputting it to the file */
04398                if ((next = strchr(line, '\n'))) {
04399                   *next++ = '\0';
04400                }
04401                fprintf(p, "%s" ENDL, line);
04402                line = next;
04403             } while (!ast_strlen_zero(line));
04404          }
04405 #else
04406          fprintf(p, "%s" ENDL, passdata);
04407 #endif
04408          ast_channel_free(ast);
04409       } else
04410          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04411    } else if (msgnum > -1) {
04412       if (strcmp(vmu->mailbox, mailbox)) {
04413          /* Forwarded type */
04414          struct ast_config *msg_cfg;
04415          const char *v;
04416          int inttime;
04417          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04418          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04419          /* Retrieve info from VM attribute file */
04420          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04421          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04422          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04423             strcat(fromfile, ".txt");
04424          }
04425          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04426             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04427                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04428             }
04429 
04430             /* You might be tempted to do origdate, except that a) it's in the wrong
04431              * format, and b) it's missing for IMAP recordings. */
04432             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04433                struct timeval tv = { inttime, };
04434                struct ast_tm tm;
04435                ast_localtime(&tv, &tm, NULL);
04436                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04437             }
04438             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04439                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04440                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04441                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04442                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04443                date, origcallerid, origdate);
04444             ast_config_destroy(msg_cfg);
04445          } else {
04446             goto plain_message;
04447          }
04448       } else {
04449 plain_message:
04450          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04451             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04452             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04453             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04454             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04455       }
04456    } else {
04457       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04458             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04459    }
04460 
04461    if (imap || attach_user_voicemail) {
04462       if (!ast_strlen_zero(attach2)) {
04463          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04464          ast_debug(5, "creating second attachment filename %s\n", filename);
04465          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04466          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04467          ast_debug(5, "creating attachment filename %s\n", filename);
04468          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04469       } else {
04470          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04471          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04472          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04473       }
04474    }
04475 }
04476 
04477 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)
04478 {
04479    char tmpdir[256], newtmp[256];
04480    char fname[256];
04481    char tmpcmd[256];
04482    int tmpfd = -1;
04483 
04484    /* Eww. We want formats to tell us their own MIME type */
04485    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04486 
04487    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04488       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04489       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04490       tmpfd = mkstemp(newtmp);
04491       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04492       ast_debug(3, "newtmp: %s\n", newtmp);
04493       if (tmpfd > -1) {
04494          int soxstatus;
04495          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04496          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04497             attach = newtmp;
04498             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04499          } else {
04500             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04501                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04502             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04503          }
04504       }
04505    }
04506    fprintf(p, "--%s" ENDL, bound);
04507    if (msgnum > -1)
04508       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04509    else
04510       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04511    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04512    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04513    if (msgnum > -1)
04514       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04515    else
04516       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04517    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04518    base_encode(fname, p);
04519    if (last)
04520       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04521    if (tmpfd > -1) {
04522       unlink(fname);
04523       close(tmpfd);
04524       unlink(newtmp);
04525    }
04526    return 0;
04527 }
04528 #undef ENDL
04529 
04530 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)
04531 {
04532    FILE *p=NULL;
04533    char tmp[80] = "/tmp/astmail-XXXXXX";
04534    char tmp2[256];
04535 
04536    if (vmu && ast_strlen_zero(vmu->email)) {
04537       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04538       return(0);
04539    }
04540    if (!strcmp(format, "wav49"))
04541       format = "WAV";
04542    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));
04543    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04544       command hangs */
04545    if ((p = vm_mkftemp(tmp)) == NULL) {
04546       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04547       return -1;
04548    } else {
04549       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04550       fclose(p);
04551       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04552       ast_safe_system(tmp2);
04553       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04554    }
04555    return 0;
04556 }
04557 
04558 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)
04559 {
04560    char date[256];
04561    char host[MAXHOSTNAMELEN] = "";
04562    char who[256];
04563    char dur[PATH_MAX];
04564    char tmp[80] = "/tmp/astmail-XXXXXX";
04565    char tmp2[PATH_MAX];
04566    struct ast_tm tm;
04567    FILE *p;
04568 
04569    if ((p = vm_mkftemp(tmp)) == NULL) {
04570       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04571       return -1;
04572    }
04573    gethostname(host, sizeof(host)-1);
04574    if (strchr(srcemail, '@'))
04575       ast_copy_string(who, srcemail, sizeof(who));
04576    else 
04577       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04578    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04579    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04580    fprintf(p, "Date: %s\n", date);
04581 
04582    if (*pagerfromstring) {
04583       struct ast_channel *ast;
04584       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04585          char *passdata;
04586          int vmlen = strlen(fromstring)*3 + 200;
04587          passdata = alloca(vmlen);
04588          memset(passdata, 0, vmlen);
04589          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04590          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04591          fprintf(p, "From: %s <%s>\n", passdata, who);
04592          ast_channel_free(ast);
04593       } else 
04594          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04595    } else
04596       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04597    fprintf(p, "To: %s\n", pager);
04598    if (pagersubject) {
04599       struct ast_channel *ast;
04600       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04601          char *passdata;
04602          int vmlen = strlen(pagersubject) * 3 + 200;
04603          passdata = alloca(vmlen);
04604          memset(passdata, 0, vmlen);
04605          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04606          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04607          fprintf(p, "Subject: %s\n\n", passdata);
04608          ast_channel_free(ast);
04609       } else
04610          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04611    } else {
04612       if (ast_strlen_zero(flag)) {
04613          fprintf(p, "Subject: New VM\n\n");
04614       } else {
04615          fprintf(p, "Subject: New %s VM\n\n", flag);
04616       }
04617    }
04618 
04619    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04620    if (pagerbody) {
04621       struct ast_channel *ast;
04622       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04623          char *passdata;
04624          int vmlen = strlen(pagerbody) * 3 + 200;
04625          passdata = alloca(vmlen);
04626          memset(passdata, 0, vmlen);
04627          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04628          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04629          fprintf(p, "%s\n", passdata);
04630          ast_channel_free(ast);
04631       } else
04632          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04633    } else {
04634       fprintf(p, "New %s long %s msg in box %s\n"
04635             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04636    }
04637    fclose(p);
04638    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04639    ast_safe_system(tmp2);
04640    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04641    return 0;
04642 }
04643 
04644 /*!
04645  * \brief Gets the current date and time, as formatted string.
04646  * \param s The buffer to hold the output formatted date.
04647  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04648  * 
04649  * The date format string used is "%a %b %e %r UTC %Y".
04650  * 
04651  * \return zero on success, -1 on error.
04652  */
04653 static int get_date(char *s, int len)
04654 {
04655    struct ast_tm tm;
04656    struct timeval t = ast_tvnow();
04657    
04658    ast_localtime(&t, &tm, "UTC");
04659 
04660    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04661 }
04662 
04663 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04664 {
04665    int res;
04666    char fn[PATH_MAX];
04667    char dest[PATH_MAX];
04668 
04669    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04670 
04671    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04672       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04673       return -1;
04674    }
04675 
04676    RETRIEVE(fn, -1, ext, context);
04677    if (ast_fileexists(fn, NULL, NULL) > 0) {
04678       res = ast_stream_and_wait(chan, fn, ecodes);
04679       if (res) {
04680          DISPOSE(fn, -1);
04681          return res;
04682       }
04683    } else {
04684       /* Dispose just in case */
04685       DISPOSE(fn, -1);
04686       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04687       if (res)
04688          return res;
04689       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04690       if (res)
04691          return res;
04692    }
04693    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04694    return res;
04695 }
04696 
04697 static void free_zone(struct vm_zone *z)
04698 {
04699    ast_free(z);
04700 }
04701 
04702 #ifdef ODBC_STORAGE
04703 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04704 {
04705    int x = -1;
04706    int res;
04707    SQLHSTMT stmt = NULL;
04708    char sql[PATH_MAX];
04709    char rowdata[20];
04710    char tmp[PATH_MAX] = "";
04711    struct odbc_obj *obj = NULL;
04712    char *context;
04713    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04714 
04715    if (newmsgs)
04716       *newmsgs = 0;
04717    if (oldmsgs)
04718       *oldmsgs = 0;
04719    if (urgentmsgs)
04720       *urgentmsgs = 0;
04721 
04722    /* If no mailbox, return immediately */
04723    if (ast_strlen_zero(mailbox))
04724       return 0;
04725 
04726    ast_copy_string(tmp, mailbox, sizeof(tmp));
04727 
04728    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04729       int u, n, o;
04730       char *next, *remaining = tmp;
04731       while ((next = strsep(&remaining, " ,"))) {
04732          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04733             return -1;
04734          }
04735          if (urgentmsgs) {
04736             *urgentmsgs += u;
04737          }
04738          if (newmsgs) {
04739             *newmsgs += n;
04740          }
04741          if (oldmsgs) {
04742             *oldmsgs += o;
04743          }
04744       }
04745       return 0;
04746    }
04747 
04748    context = strchr(tmp, '@');
04749    if (context) {
04750       *context = '\0';
04751       context++;
04752    } else
04753       context = "default";
04754 
04755    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04756       do {
04757          if (newmsgs) {
04758             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04759             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04760                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04761                break;
04762             }
04763             res = SQLFetch(stmt);
04764             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04765                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04766                break;
04767             }
04768             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04769             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04770                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04771                break;
04772             }
04773             *newmsgs = atoi(rowdata);
04774             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04775          }
04776 
04777          if (oldmsgs) {
04778             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04779             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04780                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04781                break;
04782             }
04783             res = SQLFetch(stmt);
04784             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04785                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04786                break;
04787             }
04788             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04789             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04790                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04791                break;
04792             }
04793             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04794             *oldmsgs = atoi(rowdata);
04795          }
04796 
04797          if (urgentmsgs) {
04798             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04799             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04800                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04801                break;
04802             }
04803             res = SQLFetch(stmt);
04804             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04805                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04806                break;
04807             }
04808             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04809             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04810                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04811                break;
04812             }
04813             *urgentmsgs = atoi(rowdata);
04814          }
04815 
04816          x = 0;
04817       } while (0);
04818    } else {
04819       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04820    }
04821 
04822    if (stmt) {
04823       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04824    }
04825    if (obj) {
04826       ast_odbc_release_obj(obj);
04827    }
04828 
04829    return x;
04830 }
04831 
04832 /*!
04833  * \brief Gets the number of messages that exist in a mailbox folder.
04834  * \param context
04835  * \param mailbox
04836  * \param folder
04837  * 
04838  * This method is used when ODBC backend is used.
04839  * \return The number of messages in this mailbox folder (zero or more).
04840  */
04841 static int messagecount(const char *context, const char *mailbox, const char *folder)
04842 {
04843    struct odbc_obj *obj = NULL;
04844    int nummsgs = 0;
04845    int res;
04846    SQLHSTMT stmt = NULL;
04847    char sql[PATH_MAX];
04848    char rowdata[20];
04849    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04850    if (!folder)
04851       folder = "INBOX";
04852    /* If no mailbox, return immediately */
04853    if (ast_strlen_zero(mailbox))
04854       return 0;
04855 
04856    obj = ast_odbc_request_obj(odbc_database, 0);
04857    if (obj) {
04858       if (!strcmp(folder, "INBOX")) {
04859          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);
04860       } else {
04861          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04862       }
04863       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04864       if (!stmt) {
04865          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04866          goto yuck;
04867       }
04868       res = SQLFetch(stmt);
04869       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04870          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04871          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04872          goto yuck;
04873       }
04874       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04875       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04876          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04877          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04878          goto yuck;
04879       }
04880       nummsgs = atoi(rowdata);
04881       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04882    } else
04883       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04884 
04885 yuck:
04886    if (obj)
04887       ast_odbc_release_obj(obj);
04888    return nummsgs;
04889 }
04890 
04891 /** 
04892  * \brief Determines if the given folder has messages.
04893  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04894  * 
04895  * This function is used when the mailbox is stored in an ODBC back end.
04896  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04897  * \return 1 if the folder has one or more messages. zero otherwise.
04898  */
04899 static int has_voicemail(const char *mailbox, const char *folder)
04900 {
04901    char tmp[256], *tmp2 = tmp, *box, *context;
04902    ast_copy_string(tmp, mailbox, sizeof(tmp));
04903    while ((context = box = strsep(&tmp2, ",&"))) {
04904       strsep(&context, "@");
04905       if (ast_strlen_zero(context))
04906          context = "default";
04907       if (messagecount(context, box, folder))
04908          return 1;
04909    }
04910    return 0;
04911 }
04912 #endif
04913 #ifndef IMAP_STORAGE
04914 /*! 
04915  * \brief Copies a message from one mailbox to another.
04916  * \param chan
04917  * \param vmu
04918  * \param imbox
04919  * \param msgnum
04920  * \param duration
04921  * \param recip
04922  * \param fmt
04923  * \param dir
04924  *
04925  * This is only used by file storage based mailboxes.
04926  *
04927  * \return zero on success, -1 on error.
04928  */
04929 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)
04930 {
04931    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04932    const char *frombox = mbox(imbox);
04933    int recipmsgnum;
04934    int res = 0;
04935 
04936    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04937 
04938    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04939       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04940    } else {
04941       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04942    }
04943    
04944    if (!dir)
04945       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04946    else
04947       ast_copy_string(fromdir, dir, sizeof(fromdir));
04948 
04949    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04950    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04951 
04952    if (vm_lock_path(todir))
04953       return ERROR_LOCK_PATH;
04954 
04955    recipmsgnum = last_message_index(recip, todir) + 1;
04956    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04957       make_file(topath, sizeof(topath), todir, recipmsgnum);
04958       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04959          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04960       } else {
04961          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04962           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04963           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04964           * much worse problem happening and IMAP storage doesn't call this function
04965           */
04966          copy_plain_file(frompath, topath);
04967          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04968          vm_delete(topath);
04969       }
04970    } else {
04971       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04972       res = -1;
04973    }
04974    ast_unlock_path(todir);
04975    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04976    
04977    return res;
04978 }
04979 #endif
04980 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04981 
04982 static int messagecount(const char *context, const char *mailbox, const char *folder)
04983 {
04984    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
04985 }
04986 
04987 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04988 {
04989    DIR *dir;
04990    struct dirent *de;
04991    char fn[256];
04992    int ret = 0;
04993 
04994    /* If no mailbox, return immediately */
04995    if (ast_strlen_zero(mailbox))
04996       return 0;
04997 
04998    if (ast_strlen_zero(folder))
04999       folder = "INBOX";
05000    if (ast_strlen_zero(context))
05001       context = "default";
05002 
05003    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05004 
05005    if (!(dir = opendir(fn)))
05006       return 0;
05007 
05008    while ((de = readdir(dir))) {
05009       if (!strncasecmp(de->d_name, "msg", 3)) {
05010          if (shortcircuit) {
05011             ret = 1;
05012             break;
05013          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05014             ret++;
05015          }
05016       }
05017    }
05018 
05019    closedir(dir);
05020 
05021    return ret;
05022 }
05023 
05024 /** 
05025  * \brief Determines if the given folder has messages.
05026  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05027  * \param folder the folder to look in
05028  *
05029  * This function is used when the mailbox is stored in a filesystem back end.
05030  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05031  * \return 1 if the folder has one or more messages. zero otherwise.
05032  */
05033 static int has_voicemail(const char *mailbox, const char *folder)
05034 {
05035    char tmp[256], *tmp2 = tmp, *box, *context;
05036    ast_copy_string(tmp, mailbox, sizeof(tmp));
05037    if (ast_strlen_zero(folder)) {
05038       folder = "INBOX";
05039    }
05040    while ((box = strsep(&tmp2, ",&"))) {
05041       if ((context = strchr(box, '@')))
05042          *context++ = '\0';
05043       else
05044          context = "default";
05045       if (__has_voicemail(context, box, folder, 1))
05046          return 1;
05047       /* If we are checking INBOX, we should check Urgent as well */
05048       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05049          return 1;
05050       }
05051    }
05052    return 0;
05053 }
05054 
05055 
05056 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05057 {
05058    char tmp[256];
05059    char *context;
05060 
05061    /* If no mailbox, return immediately */
05062    if (ast_strlen_zero(mailbox))
05063       return 0;
05064 
05065    if (newmsgs)
05066       *newmsgs = 0;
05067    if (oldmsgs)
05068       *oldmsgs = 0;
05069    if (urgentmsgs)
05070       *urgentmsgs = 0;
05071 
05072    if (strchr(mailbox, ',')) {
05073       int tmpnew, tmpold, tmpurgent;
05074       char *mb, *cur;
05075 
05076       ast_copy_string(tmp, mailbox, sizeof(tmp));
05077       mb = tmp;
05078       while ((cur = strsep(&mb, ", "))) {
05079          if (!ast_strlen_zero(cur)) {
05080             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05081                return -1;
05082             else {
05083                if (newmsgs)
05084                   *newmsgs += tmpnew; 
05085                if (oldmsgs)
05086                   *oldmsgs += tmpold;
05087                if (urgentmsgs)
05088                   *urgentmsgs += tmpurgent;
05089             }
05090          }
05091       }
05092       return 0;
05093    }
05094 
05095    ast_copy_string(tmp, mailbox, sizeof(tmp));
05096    
05097    if ((context = strchr(tmp, '@')))
05098       *context++ = '\0';
05099    else
05100       context = "default";
05101 
05102    if (newmsgs)
05103       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05104    if (oldmsgs)
05105       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05106    if (urgentmsgs)
05107       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05108 
05109    return 0;
05110 }
05111 
05112 #endif
05113 
05114 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05115 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05116 {
05117    int urgentmsgs = 0;
05118    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05119    if (newmsgs) {
05120       *newmsgs += urgentmsgs;
05121    }
05122    return res;
05123 }
05124 
05125 static void run_externnotify(char *context, char *extension, const char *flag)
05126 {
05127    char arguments[255];
05128    char ext_context[256] = "";
05129    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05130    struct ast_smdi_mwi_message *mwi_msg;
05131 
05132    if (!ast_strlen_zero(context))
05133       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05134    else
05135       ast_copy_string(ext_context, extension, sizeof(ext_context));
05136 
05137    if (smdi_iface) {
05138       if (ast_app_has_voicemail(ext_context, NULL)) 
05139          ast_smdi_mwi_set(smdi_iface, extension);
05140       else
05141          ast_smdi_mwi_unset(smdi_iface, extension);
05142 
05143       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05144          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05145          if (!strncmp(mwi_msg->cause, "INV", 3))
05146             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05147          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05148             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05149          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05150          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05151       } else {
05152          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05153       }
05154    }
05155 
05156    if (!ast_strlen_zero(externnotify)) {
05157       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05158          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05159       } else {
05160          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05161          ast_debug(1, "Executing %s\n", arguments);
05162          ast_safe_system(arguments);
05163       }
05164    }
05165 }
05166 
05167 /*!
05168  * \brief Variables used for saving a voicemail.
05169  *
05170  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05171  */
05172 struct leave_vm_options {
05173    unsigned int flags;
05174    signed char record_gain;
05175    char *exitcontext;
05176 };
05177 
05178 /*!
05179  * \brief Prompts the user and records a voicemail to a mailbox.
05180  * \param chan
05181  * \param ext
05182  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05183  * 
05184  * 
05185  * 
05186  * \return zero on success, -1 on error.
05187  */
05188 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05189 {
05190 #ifdef IMAP_STORAGE
05191    int newmsgs, oldmsgs;
05192 #else
05193    char urgdir[PATH_MAX];
05194 #endif
05195    char txtfile[PATH_MAX];
05196    char tmptxtfile[PATH_MAX];
05197    struct vm_state *vms = NULL;
05198    char callerid[256];
05199    FILE *txt;
05200    char date[256];
05201    int txtdes;
05202    int res = 0;
05203    int msgnum;
05204    int duration = 0;
05205    int ausemacro = 0;
05206    int ousemacro = 0;
05207    int ouseexten = 0;
05208    char tmpdur[16];
05209    char priority[16];
05210    char origtime[16];
05211    char dir[PATH_MAX];
05212    char tmpdir[PATH_MAX];
05213    char fn[PATH_MAX];
05214    char prefile[PATH_MAX] = "";
05215    char tempfile[PATH_MAX] = "";
05216    char ext_context[256] = "";
05217    char fmt[80];
05218    char *context;
05219    char ecodes[17] = "#";
05220    struct ast_str *tmp = ast_str_create(16);
05221    char *tmpptr;
05222    struct ast_vm_user *vmu;
05223    struct ast_vm_user svm;
05224    const char *category = NULL;
05225    const char *code;
05226    const char *alldtmf = "0123456789ABCD*#";
05227    char flag[80];
05228 
05229    ast_str_set(&tmp, 0, "%s", ext);
05230    ext = ast_str_buffer(tmp);
05231    if ((context = strchr(ext, '@'))) {
05232       *context++ = '\0';
05233       tmpptr = strchr(context, '&');
05234    } else {
05235       tmpptr = strchr(ext, '&');
05236    }
05237 
05238    if (tmpptr)
05239       *tmpptr++ = '\0';
05240 
05241    ast_channel_lock(chan);
05242    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05243       category = ast_strdupa(category);
05244    }
05245    ast_channel_unlock(chan);
05246 
05247    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05248       ast_copy_string(flag, "Urgent", sizeof(flag));
05249    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05250       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05251    } else {
05252       flag[0] = '\0';
05253    }
05254 
05255    ast_debug(3, "Before find_user\n");
05256    if (!(vmu = find_user(&svm, context, ext))) {
05257       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05258       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05259       ast_free(tmp);
05260       return res;
05261    }
05262    /* Setup pre-file if appropriate */
05263    if (strcmp(vmu->context, "default"))
05264       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05265    else
05266       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05267 
05268    /* Set the path to the prefile. Will be one of 
05269       VM_SPOOL_DIRcontext/ext/busy
05270       VM_SPOOL_DIRcontext/ext/unavail
05271       Depending on the flag set in options.
05272    */
05273    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05274       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05275    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05276       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05277    }
05278    /* Set the path to the tmpfile as
05279       VM_SPOOL_DIR/context/ext/temp
05280       and attempt to create the folder structure.
05281    */
05282    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05283    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05284       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05285       ast_free(tmp);
05286       return -1;
05287    }
05288    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05289    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05290       ast_copy_string(prefile, tempfile, sizeof(prefile));
05291 
05292    DISPOSE(tempfile, -1);
05293    /* It's easier just to try to make it than to check for its existence */
05294    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05295 
05296    /* Check current or macro-calling context for special extensions */
05297    if (ast_test_flag(vmu, VM_OPERATOR)) {
05298       if (!ast_strlen_zero(vmu->exit)) {
05299          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05300             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05301             ouseexten = 1;
05302          }
05303       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05304          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05305          ouseexten = 1;
05306       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05307          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05308          ousemacro = 1;
05309       }
05310    }
05311 
05312    if (!ast_strlen_zero(vmu->exit)) {
05313       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05314          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05315    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05316       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05317    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05318       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05319       ausemacro = 1;
05320    }
05321 
05322    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05323       for (code = alldtmf; *code; code++) {
05324          char e[2] = "";
05325          e[0] = *code;
05326          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05327             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05328       }
05329    }
05330 
05331    /* Play the beginning intro if desired */
05332    if (!ast_strlen_zero(prefile)) {
05333 #ifdef ODBC_STORAGE
05334       int success = 
05335 #endif
05336          RETRIEVE(prefile, -1, ext, context);
05337       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05338          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05339             res = ast_waitstream(chan, ecodes);
05340 #ifdef ODBC_STORAGE
05341          if (success == -1) {
05342             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05343             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05344             store_file(prefile, vmu->mailbox, vmu->context, -1);
05345          }
05346 #endif
05347       } else {
05348          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05349          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05350       }
05351       DISPOSE(prefile, -1);
05352       if (res < 0) {
05353          ast_debug(1, "Hang up during prefile playback\n");
05354          free_user(vmu);
05355          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05356          ast_free(tmp);
05357          return -1;
05358       }
05359    }
05360    if (res == '#') {
05361       /* On a '#' we skip the instructions */
05362       ast_set_flag(options, OPT_SILENT);
05363       res = 0;
05364    }
05365    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05366       res = ast_stream_and_wait(chan, INTRO, ecodes);
05367       if (res == '#') {
05368          ast_set_flag(options, OPT_SILENT);
05369          res = 0;
05370       }
05371    }
05372    if (res > 0)
05373       ast_stopstream(chan);
05374    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05375     other than the operator -- an automated attendant or mailbox login for example */
05376    if (res == '*') {
05377       chan->exten[0] = 'a';
05378       chan->exten[1] = '\0';
05379       if (!ast_strlen_zero(vmu->exit)) {
05380          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05381       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05382          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05383       }
05384       chan->priority = 0;
05385       free_user(vmu);
05386       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05387       ast_free(tmp);
05388       return 0;
05389    }
05390 
05391    /* Check for a '0' here */
05392    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05393    transfer:
05394       if (ouseexten || ousemacro) {
05395          chan->exten[0] = 'o';
05396          chan->exten[1] = '\0';
05397          if (!ast_strlen_zero(vmu->exit)) {
05398             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05399          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05400             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05401          }
05402          ast_play_and_wait(chan, "transfer");
05403          chan->priority = 0;
05404          free_user(vmu);
05405          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05406       }
05407       ast_free(tmp);
05408       return OPERATOR_EXIT;
05409    }
05410 
05411    /* Allow all other digits to exit Voicemail and return to the dialplan */
05412    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05413       if (!ast_strlen_zero(options->exitcontext))
05414          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05415       free_user(vmu);
05416       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05417       ast_free(tmp);
05418       return res;
05419    }
05420 
05421    if (res < 0) {
05422       free_user(vmu);
05423       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05424       ast_free(tmp);
05425       return -1;
05426    }
05427    /* The meat of recording the message...  All the announcements and beeps have been played*/
05428    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05429    if (!ast_strlen_zero(fmt)) {
05430       msgnum = 0;
05431 
05432 #ifdef IMAP_STORAGE
05433       /* Is ext a mailbox? */
05434       /* must open stream for this user to get info! */
05435       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05436       if (res < 0) {
05437          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05438          ast_free(tmp);
05439          return -1;
05440       }
05441       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05442       /* It is possible under certain circumstances that inboxcount did not
05443        * create a vm_state when it was needed. This is a catchall which will
05444        * rarely be used.
05445        */
05446          if (!(vms = create_vm_state_from_user(vmu))) {
05447             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05448             ast_free(tmp);
05449             return -1;
05450          }
05451       }
05452       vms->newmessages++;
05453       
05454       /* here is a big difference! We add one to it later */
05455       msgnum = newmsgs + oldmsgs;
05456       ast_debug(3, "Messagecount set to %d\n",msgnum);
05457       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05458       /* set variable for compatibility */
05459       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05460 
05461       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05462          goto leave_vm_out;
05463       }
05464 #else
05465       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05466          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05467          if (!res)
05468             res = ast_waitstream(chan, "");
05469          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05470          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05471          inprocess_count(vmu->mailbox, vmu->context, -1);
05472          goto leave_vm_out;
05473       }
05474 
05475 #endif
05476       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05477       txtdes = mkstemp(tmptxtfile);
05478       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05479       if (txtdes < 0) {
05480          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05481          if (!res)
05482             res = ast_waitstream(chan, "");
05483          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05484          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05485          inprocess_count(vmu->mailbox, vmu->context, -1);
05486          goto leave_vm_out;
05487       }
05488 
05489       /* Now play the beep once we have the message number for our next message. */
05490       if (res >= 0) {
05491          /* Unless we're *really* silent, try to send the beep */
05492          res = ast_stream_and_wait(chan, "beep", "");
05493       }
05494             
05495       /* Store information in real-time storage */
05496       if (ast_check_realtime("voicemail_data")) {
05497          snprintf(priority, sizeof(priority), "%d", chan->priority);
05498          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05499          get_date(date, sizeof(date));
05500          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);
05501       }
05502 
05503       /* Store information */
05504       txt = fdopen(txtdes, "w+");
05505       if (txt) {
05506          get_date(date, sizeof(date));
05507          fprintf(txt, 
05508             ";\n"
05509             "; Message Information file\n"
05510             ";\n"
05511             "[message]\n"
05512             "origmailbox=%s\n"
05513             "context=%s\n"
05514             "macrocontext=%s\n"
05515             "exten=%s\n"
05516             "priority=%d\n"
05517             "callerchan=%s\n"
05518             "callerid=%s\n"
05519             "origdate=%s\n"
05520             "origtime=%ld\n"
05521             "category=%s\n",
05522             ext,
05523             chan->context,
05524             chan->macrocontext, 
05525             chan->exten,
05526             chan->priority,
05527             chan->name,
05528             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05529             date, (long)time(NULL),
05530             category ? category : "");
05531       } else {
05532          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05533          inprocess_count(vmu->mailbox, vmu->context, -1);
05534          if (ast_check_realtime("voicemail_data")) {
05535             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05536          }
05537          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05538          goto leave_vm_out;
05539       }
05540       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05541 
05542       if (txt) {
05543          fprintf(txt, "flag=%s\n", flag);
05544          if (duration < vmminsecs) {
05545             fclose(txt);
05546             if (option_verbose > 2) 
05547                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05548             ast_filedelete(tmptxtfile, NULL);
05549             unlink(tmptxtfile);
05550             if (ast_check_realtime("voicemail_data")) {
05551                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05552             }
05553             inprocess_count(vmu->mailbox, vmu->context, -1);
05554          } else {
05555             fprintf(txt, "duration=%d\n", duration);
05556             fclose(txt);
05557             if (vm_lock_path(dir)) {
05558                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05559                /* Delete files */
05560                ast_filedelete(tmptxtfile, NULL);
05561                unlink(tmptxtfile);
05562                inprocess_count(vmu->mailbox, vmu->context, -1);
05563             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05564                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05565                unlink(tmptxtfile);
05566                ast_unlock_path(dir);
05567                inprocess_count(vmu->mailbox, vmu->context, -1);
05568                if (ast_check_realtime("voicemail_data")) {
05569                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05570                }
05571             } else {
05572 #ifndef IMAP_STORAGE
05573                msgnum = last_message_index(vmu, dir) + 1;
05574 #endif
05575                make_file(fn, sizeof(fn), dir, msgnum);
05576 
05577                /* assign a variable with the name of the voicemail file */ 
05578 #ifndef IMAP_STORAGE
05579                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05580 #else
05581                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05582 #endif
05583 
05584                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05585                ast_filerename(tmptxtfile, fn, NULL);
05586                rename(tmptxtfile, txtfile);
05587                inprocess_count(vmu->mailbox, vmu->context, -1);
05588 
05589                /* Properly set permissions on voicemail text descriptor file.
05590                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05591                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05592                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05593 
05594                ast_unlock_path(dir);
05595                if (ast_check_realtime("voicemail_data")) {
05596                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05597                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05598                }
05599                /* We must store the file first, before copying the message, because
05600                 * ODBC storage does the entire copy with SQL.
05601                 */
05602                if (ast_fileexists(fn, NULL, NULL) > 0) {
05603                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05604                }
05605 
05606                /* Are there to be more recipients of this message? */
05607                while (tmpptr) {
05608                   struct ast_vm_user recipu, *recip;
05609                   char *exten, *cntx;
05610                
05611                   exten = strsep(&tmpptr, "&");
05612                   cntx = strchr(exten, '@');
05613                   if (cntx) {
05614                      *cntx = '\0';
05615                      cntx++;
05616                   }
05617                   if ((recip = find_user(&recipu, cntx, exten))) {
05618                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05619                      free_user(recip);
05620                   }
05621                }
05622 #ifndef IMAP_STORAGE
05623                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05624                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05625                   char sfn[PATH_MAX];
05626                   char dfn[PATH_MAX];
05627                   int x;
05628                   /* It's easier just to try to make it than to check for its existence */
05629                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05630                   x = last_message_index(vmu, urgdir) + 1;
05631                   make_file(sfn, sizeof(sfn), dir, msgnum);
05632                   make_file(dfn, sizeof(dfn), urgdir, x);
05633                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05634                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05635                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05636                   ast_copy_string(fn, dfn, sizeof(fn));
05637                   msgnum = x;
05638                }
05639 #endif
05640                /* Notification needs to happen after the copy, though. */
05641                if (ast_fileexists(fn, NULL, NULL)) {
05642 #ifdef IMAP_STORAGE
05643                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05644 #else
05645                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05646 #endif
05647                }
05648 
05649                /* Disposal needs to happen after the optional move and copy */
05650                if (ast_fileexists(fn, NULL, NULL)) {
05651                   DISPOSE(dir, msgnum);
05652                }
05653             }
05654          }
05655       } else {
05656          inprocess_count(vmu->mailbox, vmu->context, -1);
05657       }
05658       if (res == '0') {
05659          goto transfer;
05660       } else if (res > 0)
05661          res = 0;
05662 
05663       if (duration < vmminsecs)
05664          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05665          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05666       else
05667          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05668    } else
05669       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05670 leave_vm_out:
05671    free_user(vmu);
05672 
05673 #ifdef IMAP_STORAGE
05674    /* expunge message - use UID Expunge if supported on IMAP server*/
05675    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05676    if (expungeonhangup == 1) {
05677       ast_mutex_lock(&vms->lock);
05678 #ifdef HAVE_IMAP_TK2006
05679       if (LEVELUIDPLUS (vms->mailstream)) {
05680          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05681       } else 
05682 #endif
05683          mail_expunge(vms->mailstream);
05684       ast_mutex_unlock(&vms->lock);
05685    }
05686 #endif
05687 
05688    ast_free(tmp);
05689    return res;
05690 }
05691 
05692 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05693 {
05694    int d;
05695    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05696    return d;
05697 }
05698 
05699 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05700 {
05701 #ifdef IMAP_STORAGE
05702    /* we must use mbox(x) folder names, and copy the message there */
05703    /* simple. huh? */
05704    char sequence[10];
05705    char mailbox[256];
05706    int res;
05707 
05708    /* get the real IMAP message number for this message */
05709    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05710    
05711    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05712    ast_mutex_lock(&vms->lock);
05713    /* if save to Old folder, put in INBOX as read */
05714    if (box == OLD_FOLDER) {
05715       mail_setflag(vms->mailstream, sequence, "\\Seen");
05716       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05717    } else if (box == NEW_FOLDER) {
05718       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05719       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05720    }
05721    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05722       ast_mutex_unlock(&vms->lock);
05723       return 0;
05724    }
05725    /* Create the folder if it don't exist */
05726    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05727    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05728    if (mail_create(vms->mailstream, mailbox) == NIL) 
05729       ast_debug(5, "Folder exists.\n");
05730    else
05731       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05732    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05733    ast_mutex_unlock(&vms->lock);
05734    return res;
05735 #else
05736    char *dir = vms->curdir;
05737    char *username = vms->username;
05738    char *context = vmu->context;
05739    char sfn[PATH_MAX];
05740    char dfn[PATH_MAX];
05741    char ddir[PATH_MAX];
05742    const char *dbox = mbox(box);
05743    int x, i;
05744    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05745 
05746    if (vm_lock_path(ddir))
05747       return ERROR_LOCK_PATH;
05748 
05749    x = last_message_index(vmu, ddir) + 1;
05750 
05751    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05752       x--;
05753       for (i = 1; i <= x; i++) {
05754          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05755          make_file(sfn, sizeof(sfn), ddir, i);
05756          make_file(dfn, sizeof(dfn), ddir, i - 1);
05757          if (EXISTS(ddir, i, sfn, NULL)) {
05758             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05759          } else
05760             break;
05761       }
05762    } else {
05763       if (x >= vmu->maxmsg) {
05764          ast_unlock_path(ddir);
05765          return -1;
05766       }
05767    }
05768    make_file(sfn, sizeof(sfn), dir, msg);
05769    make_file(dfn, sizeof(dfn), ddir, x);
05770    if (strcmp(sfn, dfn)) {
05771       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05772    }
05773    ast_unlock_path(ddir);
05774 #endif
05775    return 0;
05776 }
05777 
05778 static int adsi_logo(unsigned char *buf)
05779 {
05780    int bytes = 0;
05781    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05782    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05783    return bytes;
05784 }
05785 
05786 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05787 {
05788    unsigned char buf[256];
05789    int bytes=0;
05790    int x;
05791    char num[5];
05792 
05793    *useadsi = 0;
05794    bytes += ast_adsi_data_mode(buf + bytes);
05795    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05796 
05797    bytes = 0;
05798    bytes += adsi_logo(buf);
05799    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05800 #ifdef DISPLAY
05801    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05802 #endif
05803    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05804    bytes += ast_adsi_data_mode(buf + bytes);
05805    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05806 
05807    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05808       bytes = 0;
05809       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05810       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05811       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05812       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05813       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05814       return 0;
05815    }
05816 
05817 #ifdef DISPLAY
05818    /* Add a dot */
05819    bytes = 0;
05820    bytes += ast_adsi_logo(buf);
05821    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05822    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05823    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05824    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05825 #endif
05826    bytes = 0;
05827    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05828    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05829    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05830    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05831    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05832    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05833    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05834 
05835 #ifdef DISPLAY
05836    /* Add another dot */
05837    bytes = 0;
05838    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05839    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05840 
05841    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05842    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05843 #endif
05844 
05845    bytes = 0;
05846    /* These buttons we load but don't use yet */
05847    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05848    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05849    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05850    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05851    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05852    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05853    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05854 
05855 #ifdef DISPLAY
05856    /* Add another dot */
05857    bytes = 0;
05858    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05859    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05860    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05861 #endif
05862 
05863    bytes = 0;
05864    for (x=0;x<5;x++) {
05865       snprintf(num, sizeof(num), "%d", x);
05866       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05867    }
05868    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05869    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05870 
05871 #ifdef DISPLAY
05872    /* Add another dot */
05873    bytes = 0;
05874    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05875    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05876    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05877 #endif
05878 
05879    if (ast_adsi_end_download(chan)) {
05880       bytes = 0;
05881       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05882       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05883       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05884       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05885       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05886       return 0;
05887    }
05888    bytes = 0;
05889    bytes += ast_adsi_download_disconnect(buf + bytes);
05890    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05891    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05892 
05893    ast_debug(1, "Done downloading scripts...\n");
05894 
05895 #ifdef DISPLAY
05896    /* Add last dot */
05897    bytes = 0;
05898    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05899    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05900 #endif
05901    ast_debug(1, "Restarting session...\n");
05902 
05903    bytes = 0;
05904    /* Load the session now */
05905    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05906       *useadsi = 1;
05907       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05908    } else
05909       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05910 
05911    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05912    return 0;
05913 }
05914 
05915 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05916 {
05917    int x;
05918    if (!ast_adsi_available(chan))
05919       return;
05920    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05921    if (x < 0)
05922       return;
05923    if (!x) {
05924       if (adsi_load_vmail(chan, useadsi)) {
05925          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05926          return;
05927       }
05928    } else
05929       *useadsi = 1;
05930 }
05931 
05932 static void adsi_login(struct ast_channel *chan)
05933 {
05934    unsigned char buf[256];
05935    int bytes=0;
05936    unsigned char keys[8];
05937    int x;
05938    if (!ast_adsi_available(chan))
05939       return;
05940 
05941    for (x=0;x<8;x++)
05942       keys[x] = 0;
05943    /* Set one key for next */
05944    keys[3] = ADSI_KEY_APPS + 3;
05945 
05946    bytes += adsi_logo(buf + bytes);
05947    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05948    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05949    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05950    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05951    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05952    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05953    bytes += ast_adsi_set_keys(buf + bytes, keys);
05954    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05955    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05956 }
05957 
05958 static void adsi_password(struct ast_channel *chan)
05959 {
05960    unsigned char buf[256];
05961    int bytes=0;
05962    unsigned char keys[8];
05963    int x;
05964    if (!ast_adsi_available(chan))
05965       return;
05966 
05967    for (x=0;x<8;x++)
05968       keys[x] = 0;
05969    /* Set one key for next */
05970    keys[3] = ADSI_KEY_APPS + 3;
05971 
05972    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05973    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05974    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05975    bytes += ast_adsi_set_keys(buf + bytes, keys);
05976    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05977    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05978 }
05979 
05980 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05981 {
05982    unsigned char buf[256];
05983    int bytes=0;
05984    unsigned char keys[8];
05985    int x,y;
05986 
05987    if (!ast_adsi_available(chan))
05988       return;
05989 
05990    for (x=0;x<5;x++) {
05991       y = ADSI_KEY_APPS + 12 + start + x;
05992       if (y > ADSI_KEY_APPS + 12 + 4)
05993          y = 0;
05994       keys[x] = ADSI_KEY_SKT | y;
05995    }
05996    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05997    keys[6] = 0;
05998    keys[7] = 0;
05999 
06000    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06001    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06002    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06003    bytes += ast_adsi_set_keys(buf + bytes, keys);
06004    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06005 
06006    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06007 }
06008 
06009 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06010 {
06011    int bytes=0;
06012    unsigned char buf[256]; 
06013    char buf1[256], buf2[256];
06014    char fn2[PATH_MAX];
06015 
06016    char cid[256]="";
06017    char *val;
06018    char *name, *num;
06019    char datetime[21]="";
06020    FILE *f;
06021 
06022    unsigned char keys[8];
06023 
06024    int x;
06025 
06026    if (!ast_adsi_available(chan))
06027       return;
06028 
06029    /* Retrieve important info */
06030    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06031    f = fopen(fn2, "r");
06032    if (f) {
06033       while (!feof(f)) {   
06034          if (!fgets((char *)buf, sizeof(buf), f)) {
06035             continue;
06036          }
06037          if (!feof(f)) {
06038             char *stringp=NULL;
06039             stringp = (char *)buf;
06040             strsep(&stringp, "=");
06041             val = strsep(&stringp, "=");
06042             if (!ast_strlen_zero(val)) {
06043                if (!strcmp((char *)buf, "callerid"))
06044                   ast_copy_string(cid, val, sizeof(cid));
06045                if (!strcmp((char *)buf, "origdate"))
06046                   ast_copy_string(datetime, val, sizeof(datetime));
06047             }
06048          }
06049       }
06050       fclose(f);
06051    }
06052    /* New meaning for keys */
06053    for (x=0;x<5;x++)
06054       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06055    keys[6] = 0x0;
06056    keys[7] = 0x0;
06057 
06058    if (!vms->curmsg) {
06059       /* No prev key, provide "Folder" instead */
06060       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06061    }
06062    if (vms->curmsg >= vms->lastmsg) {
06063       /* If last message ... */
06064       if (vms->curmsg) {
06065          /* but not only message, provide "Folder" instead */
06066          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06067          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06068 
06069       } else {
06070          /* Otherwise if only message, leave blank */
06071          keys[3] = 1;
06072       }
06073    }
06074 
06075    if (!ast_strlen_zero(cid)) {
06076       ast_callerid_parse(cid, &name, &num);
06077       if (!name)
06078          name = num;
06079    } else
06080       name = "Unknown Caller";
06081 
06082    /* If deleted, show "undeleted" */
06083 
06084    if (vms->deleted[vms->curmsg])
06085       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06086 
06087    /* Except "Exit" */
06088    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06089    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06090       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06091    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06092 
06093    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06094    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06095    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06096    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06097    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06098    bytes += ast_adsi_set_keys(buf + bytes, keys);
06099    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06100 
06101    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06102 }
06103 
06104 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06105 {
06106    int bytes=0;
06107    unsigned char buf[256];
06108    unsigned char keys[8];
06109 
06110    int x;
06111 
06112    if (!ast_adsi_available(chan))
06113       return;
06114 
06115    /* New meaning for keys */
06116    for (x=0;x<5;x++)
06117       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06118 
06119    keys[6] = 0x0;
06120    keys[7] = 0x0;
06121 
06122    if (!vms->curmsg) {
06123       /* No prev key, provide "Folder" instead */
06124       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06125    }
06126    if (vms->curmsg >= vms->lastmsg) {
06127       /* If last message ... */
06128       if (vms->curmsg) {
06129          /* but not only message, provide "Folder" instead */
06130          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06131       } else {
06132          /* Otherwise if only message, leave blank */
06133          keys[3] = 1;
06134       }
06135    }
06136 
06137    /* If deleted, show "undeleted" */
06138    if (vms->deleted[vms->curmsg]) 
06139       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06140 
06141    /* Except "Exit" */
06142    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06143    bytes += ast_adsi_set_keys(buf + bytes, keys);
06144    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06145 
06146    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06147 }
06148 
06149 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06150 {
06151    unsigned char buf[256] = "";
06152    char buf1[256] = "", buf2[256] = "";
06153    int bytes=0;
06154    unsigned char keys[8];
06155    int x;
06156 
06157    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06158    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06159    if (!ast_adsi_available(chan))
06160       return;
06161    if (vms->newmessages) {
06162       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06163       if (vms->oldmessages) {
06164          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06165          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06166       } else {
06167          snprintf(buf2, sizeof(buf2), "%s.", newm);
06168       }
06169    } else if (vms->oldmessages) {
06170       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06171       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06172    } else {
06173       strcpy(buf1, "You have no messages.");
06174       buf2[0] = ' ';
06175       buf2[1] = '\0';
06176    }
06177    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06178    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06179    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06180 
06181    for (x=0;x<6;x++)
06182       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06183    keys[6] = 0;
06184    keys[7] = 0;
06185 
06186    /* Don't let them listen if there are none */
06187    if (vms->lastmsg < 0)
06188       keys[0] = 1;
06189    bytes += ast_adsi_set_keys(buf + bytes, keys);
06190 
06191    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06192 
06193    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06194 }
06195 
06196 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06197 {
06198    unsigned char buf[256] = "";
06199    char buf1[256] = "", buf2[256] = "";
06200    int bytes=0;
06201    unsigned char keys[8];
06202    int x;
06203 
06204    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06205 
06206    if (!ast_adsi_available(chan))
06207       return;
06208 
06209    /* Original command keys */
06210    for (x=0;x<6;x++)
06211       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06212 
06213    keys[6] = 0;
06214    keys[7] = 0;
06215 
06216    if ((vms->lastmsg + 1) < 1)
06217       keys[0] = 0;
06218 
06219    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06220       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06221 
06222    if (vms->lastmsg + 1)
06223       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06224    else
06225       strcpy(buf2, "no messages.");
06226    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06227    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06228    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06229    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06230    bytes += ast_adsi_set_keys(buf + bytes, keys);
06231 
06232    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06233 
06234    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06235    
06236 }
06237 
06238 /*
06239 static void adsi_clear(struct ast_channel *chan)
06240 {
06241    char buf[256];
06242    int bytes=0;
06243    if (!ast_adsi_available(chan))
06244       return;
06245    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06246    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06247 
06248    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06249 }
06250 */
06251 
06252 static void adsi_goodbye(struct ast_channel *chan)
06253 {
06254    unsigned char buf[256];
06255    int bytes=0;
06256 
06257    if (!ast_adsi_available(chan))
06258       return;
06259    bytes += adsi_logo(buf + bytes);
06260    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06261    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06262    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06263    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06264 
06265    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06266 }
06267 
06268 /*!\brief get_folder: Folder menu
06269  * Plays "press 1 for INBOX messages" etc.
06270  * Should possibly be internationalized
06271  */
06272 static int get_folder(struct ast_channel *chan, int start)
06273 {
06274    int x;
06275    int d;
06276    char fn[PATH_MAX];
06277    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06278    if (d)
06279       return d;
06280    for (x = start; x< 5; x++) {  /* For all folders */
06281       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06282          return d;
06283       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06284       if (d)
06285          return d;
06286       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06287       d = vm_play_folder_name(chan, fn);
06288       if (d)
06289          return d;
06290       d = ast_waitfordigit(chan, 500);
06291       if (d)
06292          return d;
06293    }
06294    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06295    if (d)
06296       return d;
06297    d = ast_waitfordigit(chan, 4000);
06298    return d;
06299 }
06300 
06301 /*!
06302  * \brief plays a prompt and waits for a keypress.
06303  * \param chan
06304  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06305  * \param start Does not appear to be used at this time.
06306  *
06307  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06308  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06309  * prompting for the number inputs that correspond to the available folders.
06310  * 
06311  * \return zero on success, or -1 on error.
06312  */
06313 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06314 {
06315    int res = 0;
06316    int loops = 0;
06317    res = ast_play_and_wait(chan, fn);  /* Folder name */
06318    while (((res < '0') || (res > '9')) &&
06319          (res != '#') && (res >= 0) &&
06320          loops < 4) {
06321       res = get_folder(chan, 0);
06322       loops++;
06323    }
06324    if (loops == 4) { /* give up */
06325       return '#';
06326    }
06327    return res;
06328 }
06329 
06330 /*!
06331  * \brief presents the option to prepend to an existing message when forwarding it.
06332  * \param chan
06333  * \param vmu
06334  * \param curdir
06335  * \param curmsg
06336  * \param vmfmts
06337  * \param context
06338  * \param record_gain
06339  * \param duration
06340  * \param vms
06341  *
06342  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06343  *
06344  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06345  * \return zero on success, -1 on error.
06346  */
06347 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06348          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06349 {
06350 #ifdef IMAP_STORAGE
06351    int res;
06352 #endif
06353    int cmd = 0;
06354    int retries = 0, prepend_duration = 0, already_recorded = 0;
06355    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06356    char textfile[PATH_MAX];
06357    struct ast_config *msg_cfg;
06358    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06359 #ifndef IMAP_STORAGE
06360    signed char zero_gain = 0;
06361 #endif
06362    const char *duration_str;
06363 
06364    /* Must always populate duration correctly */
06365    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06366    strcpy(textfile, msgfile);
06367    strcpy(backup, msgfile);
06368    strcpy(backup_textfile, msgfile);
06369    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06370    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06371    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06372 
06373    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06374       *duration = atoi(duration_str);
06375    } else {
06376       *duration = 0;
06377    }
06378 
06379    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06380       if (cmd)
06381          retries = 0;
06382       switch (cmd) {
06383       case '1': 
06384 
06385 #ifdef IMAP_STORAGE
06386          /* Record new intro file */
06387          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06388          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06389          res = ast_play_and_wait(chan, INTRO);
06390          res = ast_play_and_wait(chan, "beep");
06391          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06392          cmd = 't';
06393 #else
06394 
06395          /* prepend a message to the current message, update the metadata and return */
06396 
06397          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06398          strcpy(textfile, msgfile);
06399          strncat(textfile, ".txt", sizeof(textfile) - 1);
06400          *duration = 0;
06401 
06402          /* if we can't read the message metadata, stop now */
06403          if (!msg_cfg) {
06404             cmd = 0;
06405             break;
06406          }
06407          
06408          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06409          if (already_recorded) {
06410             ast_filecopy(backup, msgfile, NULL);
06411             copy(backup_textfile, textfile);
06412          }
06413          else {
06414             ast_filecopy(msgfile, backup, NULL);
06415             copy(textfile,backup_textfile);
06416          }
06417          already_recorded = 1;
06418 
06419          if (record_gain)
06420             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06421 
06422          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06423          if (record_gain)
06424             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06425 
06426          
06427          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06428             *duration = atoi(duration_str);
06429 
06430          if (prepend_duration) {
06431             struct ast_category *msg_cat;
06432             /* need enough space for a maximum-length message duration */
06433             char duration_buf[12];
06434 
06435             *duration += prepend_duration;
06436             msg_cat = ast_category_get(msg_cfg, "message");
06437             snprintf(duration_buf, 11, "%ld", *duration);
06438             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06439                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06440             }
06441          }
06442 
06443 #endif
06444          break;
06445       case '2': 
06446          /* NULL out introfile so we know there is no intro! */
06447 #ifdef IMAP_STORAGE
06448          *vms->introfn = '\0';
06449 #endif
06450          cmd = 't';
06451          break;
06452       case '*':
06453          cmd = '*';
06454          break;
06455       default: 
06456          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06457             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06458          if (!cmd)
06459             cmd = ast_play_and_wait(chan,"vm-starmain");
06460             /* "press star to return to the main menu" */
06461          if (!cmd)
06462             cmd = ast_waitfordigit(chan,6000);
06463          if (!cmd)
06464             retries++;
06465          if (retries > 3)
06466             cmd = 't';
06467       }
06468    }
06469 
06470    if (msg_cfg)
06471       ast_config_destroy(msg_cfg);
06472    if (prepend_duration)
06473       *duration = prepend_duration;
06474 
06475    if (already_recorded && cmd == -1) {
06476       /* restore original message if prepention cancelled */
06477       ast_filerename(backup, msgfile, NULL);
06478       rename(backup_textfile, textfile);
06479    }
06480 
06481    if (cmd == 't' || cmd == 'S')
06482       cmd = 0;
06483    return cmd;
06484 }
06485 
06486 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06487 {
06488    struct ast_event *event;
06489    char *mailbox, *context;
06490 
06491    /* Strip off @default */
06492    context = mailbox = ast_strdupa(box);
06493    strsep(&context, "@");
06494    if (ast_strlen_zero(context))
06495       context = "default";
06496 
06497    if (!(event = ast_event_new(AST_EVENT_MWI,
06498          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06499          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06500          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06501          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06502          AST_EVENT_IE_END))) {
06503       return;
06504    }
06505 
06506    ast_event_queue_and_cache(event);
06507 }
06508 
06509 /*!
06510  * \brief Sends email notification that a user has a new voicemail waiting for them.
06511  * \param chan
06512  * \param vmu
06513  * \param vms
06514  * \param msgnum
06515  * \param duration
06516  * \param fmt
06517  * \param cidnum The Caller ID phone number value.
06518  * \param cidname The Caller ID name value.
06519  *
06520  * \return zero on success, -1 on error.
06521  */
06522 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)
06523 {
06524    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06525    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06526    const char *category;
06527    char *myserveremail = serveremail;
06528 
06529    ast_channel_lock(chan);
06530    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06531       category = ast_strdupa(category);
06532    }
06533    ast_channel_unlock(chan);
06534 
06535    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06536    make_file(fn, sizeof(fn), todir, msgnum);
06537    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06538 
06539    if (!ast_strlen_zero(vmu->attachfmt)) {
06540       if (strstr(fmt, vmu->attachfmt))
06541          fmt = vmu->attachfmt;
06542       else
06543          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);
06544    }
06545 
06546    /* Attach only the first format */
06547    fmt = ast_strdupa(fmt);
06548    stringp = fmt;
06549    strsep(&stringp, "|");
06550 
06551    if (!ast_strlen_zero(vmu->serveremail))
06552       myserveremail = vmu->serveremail;
06553 
06554    if (!ast_strlen_zero(vmu->email)) {
06555       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06556 
06557       if (attach_user_voicemail)
06558          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06559 
06560       /* XXX possible imap issue, should category be NULL XXX */
06561       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06562 
06563       if (attach_user_voicemail)
06564          DISPOSE(todir, msgnum);
06565    }
06566 
06567    if (!ast_strlen_zero(vmu->pager)) {
06568       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06569    }
06570 
06571    if (ast_test_flag(vmu, VM_DELETE))
06572       DELETE(todir, msgnum, fn, vmu);
06573 
06574    /* Leave voicemail for someone */
06575    if (ast_app_has_voicemail(ext_context, NULL)) 
06576       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06577 
06578    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06579 
06580    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);
06581    run_externnotify(vmu->context, vmu->mailbox, flag);
06582 
06583 #ifdef IMAP_STORAGE
06584    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06585    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06586       vm_imap_delete(NULL, vms->curmsg, vmu);
06587       vms->newmessages--;  /* Fix new message count */
06588    }
06589 #endif
06590 
06591    return 0;
06592 }
06593 
06594 /*!
06595  * \brief Sends a voicemail message to a mailbox recipient.
06596  * \param ast_channel
06597  * \param context
06598  * \param vms
06599  * \param sender
06600  * \param fmt
06601  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06602  *             Will be 0 when called to forward an existing message (option 8)
06603  *             Will be 1 when called to leave a message (option 3->5)
06604  * \param record_gain 
06605  *
06606  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06607  * 
06608  * When in the leave message mode (is_new_message == 1):
06609  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06610  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06611  *
06612  * When in the forward message mode (is_new_message == 0):
06613  *   - retreives the current message to be forwarded
06614  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06615  *   - determines the target mailbox and folders
06616  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06617  *
06618  * \return zero on success, -1 on error.
06619  */
06620 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)
06621 {
06622 #ifdef IMAP_STORAGE
06623    int todircount=0;
06624    struct vm_state *dstvms;
06625 #endif
06626    char username[70]="";
06627    char fn[PATH_MAX]; /* for playback of name greeting */
06628    char ecodes[16] = "#";
06629    int res = 0, cmd = 0;
06630    struct ast_vm_user *receiver = NULL, *vmtmp;
06631    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06632    char *stringp;
06633    const char *s;
06634    int saved_messages = 0, found = 0;
06635    int valid_extensions = 0;
06636    char *dir;
06637    int curmsg;
06638    char urgent_str[7] = "";
06639    char tmptxtfile[PATH_MAX];
06640    int prompt_played = 0;
06641 #ifndef IMAP_STORAGE
06642    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06643 #endif
06644    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06645       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06646    }
06647 
06648    if (vms == NULL) return -1;
06649    dir = vms->curdir;
06650    curmsg = vms->curmsg;
06651 
06652    tmptxtfile[0] = '\0';
06653    while (!res && !valid_extensions) {
06654       int use_directory = 0;
06655       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06656          int done = 0;
06657          int retries = 0;
06658          cmd=0;
06659          while ((cmd >= 0) && !done ){
06660             if (cmd)
06661                retries = 0;
06662             switch (cmd) {
06663             case '1': 
06664                use_directory = 0;
06665                done = 1;
06666                break;
06667             case '2': 
06668                use_directory = 1;
06669                done=1;
06670                break;
06671             case '*': 
06672                cmd = 't';
06673                done = 1;
06674                break;
06675             default: 
06676                /* Press 1 to enter an extension press 2 to use the directory */
06677                cmd = ast_play_and_wait(chan,"vm-forward");
06678                if (!cmd)
06679                   cmd = ast_waitfordigit(chan,3000);
06680                if (!cmd)
06681                   retries++;
06682                if (retries > 3) {
06683                   cmd = 't';
06684                   done = 1;
06685                }
06686                
06687             }
06688          }
06689          if (cmd < 0 || cmd == 't')
06690             break;
06691       }
06692       
06693       if (use_directory) {
06694          /* use app_directory */
06695          
06696          char old_context[sizeof(chan->context)];
06697          char old_exten[sizeof(chan->exten)];
06698          int old_priority;
06699          struct ast_app* directory_app;
06700 
06701          directory_app = pbx_findapp("Directory");
06702          if (directory_app) {
06703             char vmcontext[256];
06704             /* make backup copies */
06705             memcpy(old_context, chan->context, sizeof(chan->context));
06706             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06707             old_priority = chan->priority;
06708             
06709             /* call the the Directory, changes the channel */
06710             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06711             res = pbx_exec(chan, directory_app, vmcontext);
06712             
06713             ast_copy_string(username, chan->exten, sizeof(username));
06714             
06715             /* restore the old context, exten, and priority */
06716             memcpy(chan->context, old_context, sizeof(chan->context));
06717             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06718             chan->priority = old_priority;
06719          } else {
06720             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06721             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06722          }
06723       } else {
06724          /* Ask for an extension */
06725          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06726          prompt_played++;
06727          if (res || prompt_played > 4)
06728             break;
06729          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06730             break;
06731       }
06732       
06733       /* start all over if no username */
06734       if (ast_strlen_zero(username))
06735          continue;
06736       stringp = username;
06737       s = strsep(&stringp, "*");
06738       /* start optimistic */
06739       valid_extensions = 1;
06740       while (s) {
06741          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06742             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06743             found++;
06744          } else {
06745             /* XXX Optimization for the future.  When we encounter a single bad extension,
06746              * bailing out on all of the extensions may not be the way to go.  We should
06747              * probably just bail on that single extension, then allow the user to enter
06748              * several more. XXX
06749              */
06750             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06751                free_user(receiver);
06752             }
06753             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06754             valid_extensions = 0;
06755             break;
06756          }
06757 
06758          /* play name if available, else play extension number */
06759          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06760          RETRIEVE(fn, -1, s, receiver->context);
06761          if (ast_fileexists(fn, NULL, NULL) > 0) {
06762             res = ast_stream_and_wait(chan, fn, ecodes);
06763             if (res) {
06764                DISPOSE(fn, -1);
06765                return res;
06766             }
06767          } else {
06768             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06769          }
06770          DISPOSE(fn, -1);
06771 
06772          s = strsep(&stringp, "*");
06773       }
06774       /* break from the loop of reading the extensions */
06775       if (valid_extensions)
06776          break;
06777       /* "I am sorry, that's not a valid extension.  Please try again." */
06778       res = ast_play_and_wait(chan, "pbx-invalid");
06779    }
06780    /* check if we're clear to proceed */
06781    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06782       return res;
06783    if (is_new_message == 1) {
06784       struct leave_vm_options leave_options;
06785       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06786       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06787 
06788       /* Send VoiceMail */
06789       memset(&leave_options, 0, sizeof(leave_options));
06790       leave_options.record_gain = record_gain;
06791       cmd = leave_voicemail(chan, mailbox, &leave_options);
06792    } else {
06793       /* Forward VoiceMail */
06794       long duration = 0;
06795       struct vm_state vmstmp;
06796       int copy_msg_result = 0;
06797       memcpy(&vmstmp, vms, sizeof(vmstmp));
06798 
06799       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06800 
06801       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06802       if (!cmd) {
06803          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06804 #ifdef IMAP_STORAGE
06805             int attach_user_voicemail;
06806             char *myserveremail = serveremail;
06807             
06808             /* get destination mailbox */
06809             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06810             if (!dstvms) {
06811                dstvms = create_vm_state_from_user(vmtmp);
06812             }
06813             if (dstvms) {
06814                init_mailstream(dstvms, 0);
06815                if (!dstvms->mailstream) {
06816                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06817                } else {
06818                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06819                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06820                }
06821             } else {
06822                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06823             }
06824             if (!ast_strlen_zero(vmtmp->serveremail))
06825                myserveremail = vmtmp->serveremail;
06826             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06827             /* NULL category for IMAP storage */
06828             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);
06829 #else
06830             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06831 #endif
06832             saved_messages++;
06833             AST_LIST_REMOVE_CURRENT(list);
06834             free_user(vmtmp);
06835             if (res)
06836                break;
06837          }
06838          AST_LIST_TRAVERSE_SAFE_END;
06839          if (saved_messages > 0 && !copy_msg_result) {
06840             /* give confirmation that the message was saved */
06841             /* commented out since we can't forward batches yet
06842             if (saved_messages == 1)
06843                res = ast_play_and_wait(chan, "vm-message");
06844             else
06845                res = ast_play_and_wait(chan, "vm-messages");
06846             if (!res)
06847                res = ast_play_and_wait(chan, "vm-saved"); */
06848 #ifdef IMAP_STORAGE
06849             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06850             if (ast_strlen_zero(vmstmp.introfn))
06851 #endif
06852             res = ast_play_and_wait(chan, "vm-msgsaved");
06853          }
06854 #ifndef IMAP_STORAGE
06855          else {
06856             /* with IMAP, mailbox full warning played by imap_check_limits */
06857             res = ast_play_and_wait(chan, "vm-mailboxfull");
06858          }
06859          /* Restore original message without prepended message if backup exists */
06860          make_file(msgfile, sizeof(msgfile), dir, curmsg);
06861          strcpy(textfile, msgfile);
06862          strcpy(backup, msgfile);
06863          strcpy(backup_textfile, msgfile);
06864          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06865          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06866          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06867          if (ast_fileexists(backup, NULL, NULL) > 0) {
06868             ast_filerename(backup, msgfile, NULL);
06869             rename(backup_textfile, textfile);
06870          }
06871 #endif
06872       }
06873       DISPOSE(dir, curmsg);
06874    }
06875 
06876    /* If anything failed above, we still have this list to free */
06877    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06878       free_user(vmtmp);
06879    return res ? res : cmd;
06880 }
06881 
06882 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06883 {
06884    int res;
06885    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06886       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06887    return res;
06888 }
06889 
06890 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06891 {
06892    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);
06893 }
06894 
06895 static int play_message_category(struct ast_channel *chan, const char *category)
06896 {
06897    int res = 0;
06898 
06899    if (!ast_strlen_zero(category))
06900       res = ast_play_and_wait(chan, category);
06901 
06902    if (res) {
06903       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06904       res = 0;
06905    }
06906 
06907    return res;
06908 }
06909 
06910 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06911 {
06912    int res = 0;
06913    struct vm_zone *the_zone = NULL;
06914    time_t t;
06915 
06916    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06917       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06918       return 0;
06919    }
06920 
06921    /* Does this user have a timezone specified? */
06922    if (!ast_strlen_zero(vmu->zonetag)) {
06923       /* Find the zone in the list */
06924       struct vm_zone *z;
06925       AST_LIST_LOCK(&zones);
06926       AST_LIST_TRAVERSE(&zones, z, list) {
06927          if (!strcmp(z->name, vmu->zonetag)) {
06928             the_zone = z;
06929             break;
06930          }
06931       }
06932       AST_LIST_UNLOCK(&zones);
06933    }
06934 
06935 /* No internal variable parsing for now, so we'll comment it out for the time being */
06936 #if 0
06937    /* Set the DIFF_* variables */
06938    ast_localtime(&t, &time_now, NULL);
06939    tv_now = ast_tvnow();
06940    ast_localtime(&tv_now, &time_then, NULL);
06941 
06942    /* Day difference */
06943    if (time_now.tm_year == time_then.tm_year)
06944       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06945    else
06946       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06947    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06948 
06949    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06950 #endif
06951    if (the_zone) {
06952       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06953    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
06954       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06955    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
06956       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06957    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
06958       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);
06959    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
06960       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06961    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
06962       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06963    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
06964       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06965    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
06966       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);
06967    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
06968       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06969    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
06970       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
06971    } else {
06972       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06973    }
06974 #if 0
06975    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06976 #endif
06977    return res;
06978 }
06979 
06980 
06981 
06982 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06983 {
06984    int res = 0;
06985    int i;
06986    char *callerid, *name;
06987    char prefile[PATH_MAX] = "";
06988    
06989 
06990    /* If voicemail cid is not enabled, or we didn't get cid or context from
06991     * the attribute file, leave now.
06992     *
06993     * TODO Still need to change this so that if this function is called by the
06994     * message envelope (and someone is explicitly requesting to hear the CID),
06995     * it does not check to see if CID is enabled in the config file.
06996     */
06997    if ((cid == NULL)||(context == NULL))
06998       return res;
06999 
07000    /* Strip off caller ID number from name */
07001    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07002    ast_callerid_parse(cid, &name, &callerid);
07003    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07004       /* Check for internal contexts and only */
07005       /* say extension when the call didn't come from an internal context in the list */
07006       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07007          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07008          if ((strcmp(cidinternalcontexts[i], context) == 0))
07009             break;
07010       }
07011       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07012          if (!res) {
07013             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07014             if (!ast_strlen_zero(prefile)) {
07015             /* See if we can find a recorded name for this person instead of their extension number */
07016                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07017                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07018                   if (!callback)
07019                      res = wait_file2(chan, vms, "vm-from");
07020                   res = ast_stream_and_wait(chan, prefile, "");
07021                } else {
07022                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07023                   /* Say "from extension" as one saying to sound smoother */
07024                   if (!callback)
07025                      res = wait_file2(chan, vms, "vm-from-extension");
07026                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07027                }
07028             }
07029          }
07030       } else if (!res) {
07031          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07032          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07033          if (!callback)
07034             res = wait_file2(chan, vms, "vm-from-phonenumber");
07035          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07036       }
07037    } else {
07038       /* Number unknown */
07039       ast_debug(1, "VM-CID: From an unknown number\n");
07040       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07041       res = wait_file2(chan, vms, "vm-unknown-caller");
07042    }
07043    return res;
07044 }
07045 
07046 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07047 {
07048    int res = 0;
07049    int durationm;
07050    int durations;
07051    /* Verify that we have a duration for the message */
07052    if (duration == NULL)
07053       return res;
07054 
07055    /* Convert from seconds to minutes */
07056    durations=atoi(duration);
07057    durationm=(durations / 60);
07058 
07059    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07060 
07061    if ((!res) && (durationm >= minduration)) {
07062       res = wait_file2(chan, vms, "vm-duration");
07063 
07064       /* POLISH syntax */
07065       if (!strncasecmp(chan->language, "pl", 2)) {
07066          div_t num = div(durationm, 10);
07067 
07068          if (durationm == 1) {
07069             res = ast_play_and_wait(chan, "digits/1z");
07070             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07071          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07072             if (num.rem == 2) {
07073                if (!num.quot) {
07074                   res = ast_play_and_wait(chan, "digits/2-ie");
07075                } else {
07076                   res = say_and_wait(chan, durationm - 2 , chan->language);
07077                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07078                }
07079             } else {
07080                res = say_and_wait(chan, durationm, chan->language);
07081             }
07082             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07083          } else {
07084             res = say_and_wait(chan, durationm, chan->language);
07085             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07086          }
07087       /* DEFAULT syntax */
07088       } else {
07089          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07090          res = wait_file2(chan, vms, "vm-minutes");
07091       }
07092    }
07093    return res;
07094 }
07095 
07096 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07097 {
07098    int res = 0;
07099    char filename[256], *cid;
07100    const char *origtime, *context, *category, *duration, *flag;
07101    struct ast_config *msg_cfg;
07102    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07103 
07104    vms->starting = 0;
07105    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07106    adsi_message(chan, vms);
07107    if (!vms->curmsg)
07108       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07109    else if (vms->curmsg == vms->lastmsg)
07110       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07111 
07112    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07113    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07114    msg_cfg = ast_config_load(filename, config_flags);
07115    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07116       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07117       return 0;
07118    }
07119    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07120 
07121    /* Play the word urgent if we are listening to urgent messages */
07122    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07123       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07124    }
07125 
07126    if (!res) {
07127       /* POLISH syntax */
07128       if (!strncasecmp(chan->language, "pl", 2)) {
07129          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07130             int ten, one;
07131             char nextmsg[256];
07132             ten = (vms->curmsg + 1) / 10;
07133             one = (vms->curmsg + 1) % 10;
07134 
07135             if (vms->curmsg < 20) {
07136                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07137                res = wait_file2(chan, vms, nextmsg);
07138             } else {
07139                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07140                res = wait_file2(chan, vms, nextmsg);
07141                if (one > 0) {
07142                   if (!res) {
07143                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07144                      res = wait_file2(chan, vms, nextmsg);
07145                   }
07146                }
07147             }
07148          }
07149          if (!res)
07150             res = wait_file2(chan, vms, "vm-message");
07151       /* HEBREW syntax */
07152       } else if (!strncasecmp(chan->language, "he", 2)) {
07153          if (!vms->curmsg) {
07154             res = wait_file2(chan, vms, "vm-message");
07155             res = wait_file2(chan, vms, "vm-first");
07156          } else if (vms->curmsg == vms->lastmsg) {
07157             res = wait_file2(chan, vms, "vm-message");
07158             res = wait_file2(chan, vms, "vm-last");
07159          } else {
07160             res = wait_file2(chan, vms, "vm-message");
07161             res = wait_file2(chan, vms, "vm-number");
07162             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07163          }
07164       } else {
07165          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07166             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07167          } else { /* DEFAULT syntax */
07168             res = wait_file2(chan, vms, "vm-message");
07169          }
07170          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07171             if (!res) {
07172                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07173             }
07174          }
07175       }
07176    }
07177 
07178    if (!msg_cfg) {
07179       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07180       return 0;
07181    }
07182 
07183    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07184       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07185       DISPOSE(vms->curdir, vms->curmsg);
07186       ast_config_destroy(msg_cfg);
07187       return 0;
07188    }
07189 
07190    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07191    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07192    category = ast_variable_retrieve(msg_cfg, "message", "category");
07193 
07194    context = ast_variable_retrieve(msg_cfg, "message", "context");
07195    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07196       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07197    if (!res) {
07198       res = play_message_category(chan, category);
07199    }
07200    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07201       res = play_message_datetime(chan, vmu, origtime, filename);
07202    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07203       res = play_message_callerid(chan, vms, cid, context, 0);
07204    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07205       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07206    /* Allow pressing '1' to skip envelope / callerid */
07207    if (res == '1')
07208       res = 0;
07209    ast_config_destroy(msg_cfg);
07210 
07211    if (!res) {
07212       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07213       vms->heard[vms->curmsg] = 1;
07214 #ifdef IMAP_STORAGE
07215       /*IMAP storage stores any prepended message from a forward
07216        * as a separate file from the rest of the message
07217        */
07218       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07219          wait_file(chan, vms, vms->introfn);
07220       }
07221 #endif
07222       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07223          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07224          res = 0;
07225       }
07226    }
07227    DISPOSE(vms->curdir, vms->curmsg);
07228    return res;
07229 }
07230 
07231 #ifdef IMAP_STORAGE
07232 static int imap_remove_file(char *dir, int msgnum)
07233 {
07234    char fn[PATH_MAX];
07235    char full_fn[PATH_MAX];
07236    char intro[PATH_MAX] = {0,};
07237    
07238    if (msgnum > -1) {
07239       make_file(fn, sizeof(fn), dir, msgnum);
07240       snprintf(intro, sizeof(intro), "%sintro", fn);
07241    } else
07242       ast_copy_string(fn, dir, sizeof(fn));
07243    
07244    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07245       ast_filedelete(fn, NULL);
07246       if (!ast_strlen_zero(intro)) {
07247          ast_filedelete(intro, NULL);
07248       }
07249       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07250       unlink(full_fn);
07251    }
07252    return 0;
07253 }
07254 
07255 
07256 
07257 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07258 {
07259    char *file, *filename;
07260    char *attachment;
07261    char arg[10];
07262    int i;
07263    BODY* body;
07264 
07265    file = strrchr(ast_strdupa(dir), '/');
07266    if (file) {
07267       *file++ = '\0';
07268    } else {
07269       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07270       return -1;
07271    }
07272 
07273    ast_mutex_lock(&vms->lock);
07274    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07275       mail_fetchstructure(vms->mailstream, i + 1, &body);
07276       /* We have the body, now we extract the file name of the first attachment. */
07277       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07278          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07279       } else {
07280          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07281          ast_mutex_unlock(&vms->lock);
07282          return -1;
07283       }
07284       filename = strsep(&attachment, ".");
07285       if (!strcmp(filename, file)) {
07286          sprintf (arg,"%d", i+1);
07287          mail_setflag (vms->mailstream,arg,"\\DELETED");
07288       }
07289    }
07290    mail_expunge(vms->mailstream);
07291    ast_mutex_unlock(&vms->lock);
07292    return 0;
07293 }
07294 
07295 #elif !defined(IMAP_STORAGE)
07296 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07297 {
07298    int count_msg, last_msg;
07299 
07300    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07301 
07302    /* Rename the member vmbox HERE so that we don't try to return before
07303     * we know what's going on.
07304     */
07305    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07306 
07307    /* Faster to make the directory than to check if it exists. */
07308    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07309 
07310    count_msg = count_messages(vmu, vms->curdir);
07311    if (count_msg < 0) {
07312       return count_msg;
07313    } else {
07314       vms->lastmsg = count_msg - 1;
07315    }
07316 
07317    /*
07318    The following test is needed in case sequencing gets messed up.
07319    There appears to be more than one way to mess up sequence, so
07320    we will not try to find all of the root causes--just fix it when
07321    detected.
07322    */
07323 
07324    if (vm_lock_path(vms->curdir)) {
07325       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07326       return -1;
07327    }
07328 
07329    last_msg = last_message_index(vmu, vms->curdir);
07330    ast_unlock_path(vms->curdir);
07331 
07332    if (last_msg < 0) {
07333       return last_msg;
07334    }
07335 
07336    return 0;
07337 }
07338 #endif
07339 
07340 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07341 {
07342    int x = 0;
07343 #ifndef IMAP_STORAGE
07344    int res = 0, nummsg;
07345    char fn2[PATH_MAX];
07346 #endif
07347 
07348    if (vms->lastmsg <= -1) {
07349       goto done;
07350    }
07351 
07352    vms->curmsg = -1;
07353 #ifndef IMAP_STORAGE
07354    /* Get the deleted messages fixed */
07355    if (vm_lock_path(vms->curdir)) {
07356       return ERROR_LOCK_PATH;
07357    }
07358 
07359    for (x = 0; x < vmu->maxmsg; x++) {
07360       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07361          /* Save this message.  It's not in INBOX or hasn't been heard */
07362          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07363          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07364             break;
07365          }
07366          vms->curmsg++;
07367          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07368          if (strcmp(vms->fn, fn2)) {
07369             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07370          }
07371       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07372          /* Move to old folder before deleting */
07373          res = save_to_folder(vmu, vms, x, 1);
07374          if (res == ERROR_LOCK_PATH) {
07375             /* If save failed do not delete the message */
07376             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07377             vms->deleted[x] = 0;
07378             vms->heard[x] = 0;
07379             --x;
07380          }
07381       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07382          /* Move to deleted folder */
07383          res = save_to_folder(vmu, vms, x, 10);
07384          if (res == ERROR_LOCK_PATH) {
07385             /* If save failed do not delete the message */
07386             vms->deleted[x] = 0;
07387             vms->heard[x] = 0;
07388             --x;
07389          }
07390       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07391          /* If realtime storage enabled - we should explicitly delete this message,
07392          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07393          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07394          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07395             DELETE(vms->curdir, x, vms->fn, vmu);
07396          }
07397       }
07398    }
07399 
07400    /* Delete ALL remaining messages */
07401    nummsg = x - 1;
07402    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07403       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07404       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07405          DELETE(vms->curdir, x, vms->fn, vmu);
07406       }
07407    }
07408    ast_unlock_path(vms->curdir);
07409 #else /* defined(IMAP_STORAGE) */
07410    if (vms->deleted) {
07411       /* Since we now expunge after each delete, deleting in reverse order
07412        * ensures that no reordering occurs between each step. */
07413       for (x = vmu->maxmsg - 1; x >= 0; x--) {
07414          if (vms->deleted[x]) {
07415             ast_debug(3, "IMAP delete of %d\n", x);
07416             DELETE(vms->curdir, x, vms->fn, vmu);
07417          }
07418       }
07419    }
07420 #endif
07421 
07422 done:
07423    if (vms->deleted) {
07424       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
07425    }
07426    if (vms->heard) {
07427       memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
07428    }
07429 
07430    return 0;
07431 }
07432 
07433 /* In Greek even though we CAN use a syntax like "friends messages"
07434  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07435  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07436  * syntax for the above three categories which is more elegant.
07437  */
07438 
07439 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07440 {
07441    int cmd;
07442    char *buf;
07443 
07444    buf = alloca(strlen(box) + 2);
07445    strcpy(buf, box);
07446    strcat(buf,"s");
07447 
07448    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07449       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07450       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07451    } else {
07452       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07453       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07454    }
07455 }
07456 
07457 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07458 {
07459    int cmd;
07460 
07461    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07462       if (!strcasecmp(box, "vm-INBOX"))
07463          cmd = ast_play_and_wait(chan, "vm-new-e");
07464       else
07465          cmd = ast_play_and_wait(chan, "vm-old-e");
07466       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07467    } else {
07468       cmd = ast_play_and_wait(chan, "vm-messages");
07469       return cmd ? cmd : ast_play_and_wait(chan, box);
07470    }
07471 }
07472 
07473 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07474 {
07475    int cmd;
07476 
07477    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07478       cmd = ast_play_and_wait(chan, "vm-messages");
07479       return cmd ? cmd : ast_play_and_wait(chan, box);
07480    } else {
07481       cmd = ast_play_and_wait(chan, box);
07482       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07483    }
07484 }
07485 
07486 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07487 {
07488    int cmd;
07489 
07490    if (  !strncasecmp(chan->language, "it", 2) ||
07491         !strncasecmp(chan->language, "es", 2) ||
07492         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07493       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07494       return cmd ? cmd : ast_play_and_wait(chan, box);
07495    } else if (!strncasecmp(chan->language, "gr", 2)) {
07496       return vm_play_folder_name_gr(chan, box);
07497    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07498       return ast_play_and_wait(chan, box);
07499    } else if (!strncasecmp(chan->language, "pl", 2)) {
07500       return vm_play_folder_name_pl(chan, box);
07501    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07502       return vm_play_folder_name_ua(chan, box);
07503    } else {  /* Default English */
07504       cmd = ast_play_and_wait(chan, box);
07505       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07506    }
07507 }
07508 
07509 /* GREEK SYNTAX
07510    In greek the plural for old/new is
07511    different so we need the following files
07512    We also need vm-denExeteMynhmata because
07513    this syntax is different.
07514 
07515    -> vm-Olds.wav : "Palia"
07516    -> vm-INBOXs.wav : "Nea"
07517    -> vm-denExeteMynhmata : "den exete mynhmata"
07518 */
07519 
07520 
07521 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07522 {
07523    int res = 0;
07524 
07525    if (vms->newmessages) {
07526       res = ast_play_and_wait(chan, "vm-youhave");
07527       if (!res) 
07528          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07529       if (!res) {
07530          if ((vms->newmessages == 1)) {
07531             res = ast_play_and_wait(chan, "vm-INBOX");
07532             if (!res)
07533                res = ast_play_and_wait(chan, "vm-message");
07534          } else {
07535             res = ast_play_and_wait(chan, "vm-INBOXs");
07536             if (!res)
07537                res = ast_play_and_wait(chan, "vm-messages");
07538          }
07539       }
07540    } else if (vms->oldmessages){
07541       res = ast_play_and_wait(chan, "vm-youhave");
07542       if (!res)
07543          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07544       if ((vms->oldmessages == 1)){
07545          res = ast_play_and_wait(chan, "vm-Old");
07546          if (!res)
07547             res = ast_play_and_wait(chan, "vm-message");
07548       } else {
07549          res = ast_play_and_wait(chan, "vm-Olds");
07550          if (!res)
07551             res = ast_play_and_wait(chan, "vm-messages");
07552       }
07553    } else if (!vms->oldmessages && !vms->newmessages) 
07554       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07555    return res;
07556 }
07557 
07558 /* Version of vm_intro() designed to work for many languages.
07559  *
07560  * It is hoped that this function can prevent the proliferation of 
07561  * language-specific vm_intro() functions and in time replace the language-
07562  * specific functions which already exist.  An examination of the language-
07563  * specific functions revealed that they all corrected the same deficiencies
07564  * in vm_intro_en() (which was the default function). Namely:
07565  *
07566  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07567  *     wording of the voicemail greeting hides this problem.  For example,
07568  *     vm-INBOX contains only the word "new".  This means that both of these
07569  *     sequences produce valid utterances:
07570  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07571  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07572  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07573  *     in many languages) the first utterance becomes "you have 1 the new message".
07574  *  2) The function contains hardcoded rules for pluralizing the word "message".
07575  *     These rules are correct for English, but not for many other languages.
07576  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07577  *     required in many languages.
07578  *  4) The gender of the word for "message" is not specified. This is a problem
07579  *     because in many languages the gender of the number in phrases such
07580  *     as "you have one new message" must match the gender of the word
07581  *     meaning "message".
07582  *
07583  * Fixing these problems for each new language has meant duplication of effort.
07584  * This new function solves the problems in the following general ways:
07585  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07586  *     and vm-Old respectively for those languages where it makes sense.
07587  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07588  *     on vm-message.
07589  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07590  *     prefix on vm-new and vm-old (none for English).
07591  *  4) Pass the gender of the language's word for "message" as an agument to
07592  *     this function which is can in turn pass on to the functions which 
07593  *     say numbers and put endings on nounds and adjectives.
07594  *
07595  * All languages require these messages:
07596  *  vm-youhave    "You have..."
07597  *  vm-and     "and"
07598  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07599  *
07600  * To use it for English, you will need these additional sound files:
07601  *  vm-new     "new"
07602  *  vm-message    "message", singular
07603  *  vm-messages      "messages", plural
07604  *
07605  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07606  *
07607  *  vm-newn    "novoye" (singular, neuter)
07608  *  vm-newx    "novikh" (counting plural form, genative plural)
07609  *  vm-message    "sobsheniye" (singular form)
07610  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07611  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07612  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07613  *  digits/2n     "dva" (neuter singular)
07614  */
07615 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07616 {
07617    int res;
07618    int lastnum = 0;
07619 
07620    res = ast_play_and_wait(chan, "vm-youhave");
07621 
07622    if (!res && vms->newmessages) {
07623       lastnum = vms->newmessages;
07624 
07625       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07626          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07627       }
07628 
07629       if (!res && vms->oldmessages) {
07630          res = ast_play_and_wait(chan, "vm-and");
07631       }
07632    }
07633 
07634    if (!res && vms->oldmessages) {
07635       lastnum = vms->oldmessages;
07636 
07637       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07638          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07639       }
07640    }
07641 
07642    if (!res) {
07643       if (lastnum == 0) {
07644          res = ast_play_and_wait(chan, "vm-no");
07645       }
07646       if (!res) {
07647          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07648       }
07649    }
07650 
07651    return res;
07652 }
07653 
07654 /* Default Hebrew syntax */
07655 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07656 {
07657    int res = 0;
07658 
07659    /* Introduce messages they have */
07660    if (!res) {
07661       if ((vms->newmessages) || (vms->oldmessages)) {
07662          res = ast_play_and_wait(chan, "vm-youhave");
07663       }
07664       /*
07665        * The word "shtei" refers to the number 2 in hebrew when performing a count
07666        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07667        * an element, this is one of them.
07668        */
07669       if (vms->newmessages) {
07670          if (!res) {
07671             if (vms->newmessages == 1) {
07672                res = ast_play_and_wait(chan, "vm-INBOX1");
07673             } else {
07674                if (vms->newmessages == 2) {
07675                   res = ast_play_and_wait(chan, "vm-shtei");
07676                } else {
07677                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07678                }
07679                res = ast_play_and_wait(chan, "vm-INBOX");
07680             }
07681          }
07682          if (vms->oldmessages && !res) {
07683             res = ast_play_and_wait(chan, "vm-and");
07684             if (vms->oldmessages == 1) {
07685                res = ast_play_and_wait(chan, "vm-Old1");
07686             } else {
07687                if (vms->oldmessages == 2) {
07688                   res = ast_play_and_wait(chan, "vm-shtei");
07689                } else {
07690                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07691                }
07692                res = ast_play_and_wait(chan, "vm-Old");
07693             }
07694          }
07695       }
07696       if (!res && vms->oldmessages && !vms->newmessages) {
07697          if (!res) {
07698             if (vms->oldmessages == 1) {
07699                res = ast_play_and_wait(chan, "vm-Old1");
07700             } else {
07701                if (vms->oldmessages == 2) {
07702                   res = ast_play_and_wait(chan, "vm-shtei");
07703                } else {
07704                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07705                }
07706                res = ast_play_and_wait(chan, "vm-Old");
07707             }
07708          }
07709       }
07710       if (!res) {
07711          if (!vms->oldmessages && !vms->newmessages) {
07712             if (!res) {
07713                res = ast_play_and_wait(chan, "vm-nomessages");
07714             }
07715          }
07716       }
07717    }
07718    return res;
07719 }
07720    
07721 /* Default English syntax */
07722 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07723 {
07724    int res;
07725 
07726    /* Introduce messages they have */
07727    res = ast_play_and_wait(chan, "vm-youhave");
07728    if (!res) {
07729       if (vms->urgentmessages) {
07730          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07731          if (!res)
07732             res = ast_play_and_wait(chan, "vm-Urgent");
07733          if ((vms->oldmessages || vms->newmessages) && !res) {
07734             res = ast_play_and_wait(chan, "vm-and");
07735          } else if (!res) {
07736             if ((vms->urgentmessages == 1))
07737                res = ast_play_and_wait(chan, "vm-message");
07738             else
07739                res = ast_play_and_wait(chan, "vm-messages");
07740          }
07741       }
07742       if (vms->newmessages) {
07743          res = say_and_wait(chan, vms->newmessages, chan->language);
07744          if (!res)
07745             res = ast_play_and_wait(chan, "vm-INBOX");
07746          if (vms->oldmessages && !res)
07747             res = ast_play_and_wait(chan, "vm-and");
07748          else if (!res) {
07749             if ((vms->newmessages == 1))
07750                res = ast_play_and_wait(chan, "vm-message");
07751             else
07752                res = ast_play_and_wait(chan, "vm-messages");
07753          }
07754             
07755       }
07756       if (!res && vms->oldmessages) {
07757          res = say_and_wait(chan, vms->oldmessages, chan->language);
07758          if (!res)
07759             res = ast_play_and_wait(chan, "vm-Old");
07760          if (!res) {
07761             if (vms->oldmessages == 1)
07762                res = ast_play_and_wait(chan, "vm-message");
07763             else
07764                res = ast_play_and_wait(chan, "vm-messages");
07765          }
07766       }
07767       if (!res) {
07768          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07769             res = ast_play_and_wait(chan, "vm-no");
07770             if (!res)
07771                res = ast_play_and_wait(chan, "vm-messages");
07772          }
07773       }
07774    }
07775    return res;
07776 }
07777 
07778 /* ITALIAN syntax */
07779 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07780 {
07781    /* Introduce messages they have */
07782    int res;
07783    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07784       res = ast_play_and_wait(chan, "vm-no") ||
07785          ast_play_and_wait(chan, "vm-message");
07786    else
07787       res = ast_play_and_wait(chan, "vm-youhave");
07788    if (!res && vms->newmessages) {
07789       res = (vms->newmessages == 1) ?
07790          ast_play_and_wait(chan, "digits/un") ||
07791          ast_play_and_wait(chan, "vm-nuovo") ||
07792          ast_play_and_wait(chan, "vm-message") :
07793          /* 2 or more new messages */
07794          say_and_wait(chan, vms->newmessages, chan->language) ||
07795          ast_play_and_wait(chan, "vm-nuovi") ||
07796          ast_play_and_wait(chan, "vm-messages");
07797       if (!res && vms->oldmessages)
07798          res = ast_play_and_wait(chan, "vm-and");
07799    }
07800    if (!res && vms->oldmessages) {
07801       res = (vms->oldmessages == 1) ?
07802          ast_play_and_wait(chan, "digits/un") ||
07803          ast_play_and_wait(chan, "vm-vecchio") ||
07804          ast_play_and_wait(chan, "vm-message") :
07805          /* 2 or more old messages */
07806          say_and_wait(chan, vms->oldmessages, chan->language) ||
07807          ast_play_and_wait(chan, "vm-vecchi") ||
07808          ast_play_and_wait(chan, "vm-messages");
07809    }
07810    return res;
07811 }
07812 
07813 /* POLISH syntax */
07814 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07815 {
07816    /* Introduce messages they have */
07817    int res;
07818    div_t num;
07819 
07820    if (!vms->oldmessages && !vms->newmessages) {
07821       res = ast_play_and_wait(chan, "vm-no");
07822       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07823       return res;
07824    } else {
07825       res = ast_play_and_wait(chan, "vm-youhave");
07826    }
07827 
07828    if (vms->newmessages) {
07829       num = div(vms->newmessages, 10);
07830       if (vms->newmessages == 1) {
07831          res = ast_play_and_wait(chan, "digits/1-a");
07832          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07833          res = res ? res : ast_play_and_wait(chan, "vm-message");
07834       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07835          if (num.rem == 2) {
07836             if (!num.quot) {
07837                res = ast_play_and_wait(chan, "digits/2-ie");
07838             } else {
07839                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07840                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07841             }
07842          } else {
07843             res = say_and_wait(chan, vms->newmessages, chan->language);
07844          }
07845          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07846          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07847       } else {
07848          res = say_and_wait(chan, vms->newmessages, chan->language);
07849          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07850          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07851       }
07852       if (!res && vms->oldmessages)
07853          res = ast_play_and_wait(chan, "vm-and");
07854    }
07855    if (!res && vms->oldmessages) {
07856       num = div(vms->oldmessages, 10);
07857       if (vms->oldmessages == 1) {
07858          res = ast_play_and_wait(chan, "digits/1-a");
07859          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07860          res = res ? res : ast_play_and_wait(chan, "vm-message");
07861       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07862          if (num.rem == 2) {
07863             if (!num.quot) {
07864                res = ast_play_and_wait(chan, "digits/2-ie");
07865             } else {
07866                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07867                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07868             }
07869          } else {
07870             res = say_and_wait(chan, vms->oldmessages, chan->language);
07871          }
07872          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07873          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07874       } else {
07875          res = say_and_wait(chan, vms->oldmessages, chan->language);
07876          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07877          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07878       }
07879    }
07880 
07881    return res;
07882 }
07883 
07884 /* SWEDISH syntax */
07885 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07886 {
07887    /* Introduce messages they have */
07888    int res;
07889 
07890    res = ast_play_and_wait(chan, "vm-youhave");
07891    if (res)
07892       return res;
07893 
07894    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07895       res = ast_play_and_wait(chan, "vm-no");
07896       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07897       return res;
07898    }
07899 
07900    if (vms->newmessages) {
07901       if ((vms->newmessages == 1)) {
07902          res = ast_play_and_wait(chan, "digits/ett");
07903          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07904          res = res ? res : ast_play_and_wait(chan, "vm-message");
07905       } else {
07906          res = say_and_wait(chan, vms->newmessages, chan->language);
07907          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07908          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07909       }
07910       if (!res && vms->oldmessages)
07911          res = ast_play_and_wait(chan, "vm-and");
07912    }
07913    if (!res && vms->oldmessages) {
07914       if (vms->oldmessages == 1) {
07915          res = ast_play_and_wait(chan, "digits/ett");
07916          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07917          res = res ? res : ast_play_and_wait(chan, "vm-message");
07918       } else {
07919          res = say_and_wait(chan, vms->oldmessages, chan->language);
07920          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07921          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07922       }
07923    }
07924 
07925    return res;
07926 }
07927 
07928 /* NORWEGIAN syntax */
07929 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07930 {
07931    /* Introduce messages they have */
07932    int res;
07933 
07934    res = ast_play_and_wait(chan, "vm-youhave");
07935    if (res)
07936       return res;
07937 
07938    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07939       res = ast_play_and_wait(chan, "vm-no");
07940       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07941       return res;
07942    }
07943 
07944    if (vms->newmessages) {
07945       if ((vms->newmessages == 1)) {
07946          res = ast_play_and_wait(chan, "digits/1");
07947          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07948          res = res ? res : ast_play_and_wait(chan, "vm-message");
07949       } else {
07950          res = say_and_wait(chan, vms->newmessages, chan->language);
07951          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07952          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07953       }
07954       if (!res && vms->oldmessages)
07955          res = ast_play_and_wait(chan, "vm-and");
07956    }
07957    if (!res && vms->oldmessages) {
07958       if (vms->oldmessages == 1) {
07959          res = ast_play_and_wait(chan, "digits/1");
07960          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07961          res = res ? res : ast_play_and_wait(chan, "vm-message");
07962       } else {
07963          res = say_and_wait(chan, vms->oldmessages, chan->language);
07964          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07965          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07966       }
07967    }
07968 
07969    return res;
07970 }
07971 
07972 /* GERMAN syntax */
07973 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07974 {
07975    /* Introduce messages they have */
07976    int res;
07977    res = ast_play_and_wait(chan, "vm-youhave");
07978    if (!res) {
07979       if (vms->newmessages) {
07980          if ((vms->newmessages == 1))
07981             res = ast_play_and_wait(chan, "digits/1F");
07982          else
07983             res = say_and_wait(chan, vms->newmessages, chan->language);
07984          if (!res)
07985             res = ast_play_and_wait(chan, "vm-INBOX");
07986          if (vms->oldmessages && !res)
07987             res = ast_play_and_wait(chan, "vm-and");
07988          else if (!res) {
07989             if ((vms->newmessages == 1))
07990                res = ast_play_and_wait(chan, "vm-message");
07991             else
07992                res = ast_play_and_wait(chan, "vm-messages");
07993          }
07994             
07995       }
07996       if (!res && vms->oldmessages) {
07997          if (vms->oldmessages == 1)
07998             res = ast_play_and_wait(chan, "digits/1F");
07999          else
08000             res = say_and_wait(chan, vms->oldmessages, chan->language);
08001          if (!res)
08002             res = ast_play_and_wait(chan, "vm-Old");
08003          if (!res) {
08004             if (vms->oldmessages == 1)
08005                res = ast_play_and_wait(chan, "vm-message");
08006             else
08007                res = ast_play_and_wait(chan, "vm-messages");
08008          }
08009       }
08010       if (!res) {
08011          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08012             res = ast_play_and_wait(chan, "vm-no");
08013             if (!res)
08014                res = ast_play_and_wait(chan, "vm-messages");
08015          }
08016       }
08017    }
08018    return res;
08019 }
08020 
08021 /* SPANISH syntax */
08022 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
08023 {
08024    /* Introduce messages they have */
08025    int res;
08026    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08027       res = ast_play_and_wait(chan, "vm-youhaveno");
08028       if (!res)
08029          res = ast_play_and_wait(chan, "vm-messages");
08030    } else {
08031       res = ast_play_and_wait(chan, "vm-youhave");
08032    }
08033    if (!res) {
08034       if (vms->newmessages) {
08035          if (!res) {
08036             if ((vms->newmessages == 1)) {
08037                res = ast_play_and_wait(chan, "digits/1M");
08038                if (!res)
08039                   res = ast_play_and_wait(chan, "vm-message");
08040                if (!res)
08041                   res = ast_play_and_wait(chan, "vm-INBOXs");
08042             } else {
08043                res = say_and_wait(chan, vms->newmessages, chan->language);
08044                if (!res)
08045                   res = ast_play_and_wait(chan, "vm-messages");
08046                if (!res)
08047                   res = ast_play_and_wait(chan, "vm-INBOX");
08048             }
08049          }
08050          if (vms->oldmessages && !res)
08051             res = ast_play_and_wait(chan, "vm-and");
08052       }
08053       if (vms->oldmessages) {
08054          if (!res) {
08055             if (vms->oldmessages == 1) {
08056                res = ast_play_and_wait(chan, "digits/1M");
08057                if (!res)
08058                   res = ast_play_and_wait(chan, "vm-message");
08059                if (!res)
08060                   res = ast_play_and_wait(chan, "vm-Olds");
08061             } else {
08062                res = say_and_wait(chan, vms->oldmessages, chan->language);
08063                if (!res)
08064                   res = ast_play_and_wait(chan, "vm-messages");
08065                if (!res)
08066                   res = ast_play_and_wait(chan, "vm-Old");
08067             }
08068          }
08069       }
08070    }
08071 return res;
08072 }
08073 
08074 /* BRAZILIAN PORTUGUESE syntax */
08075 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
08076    /* Introduce messages they have */
08077    int res;
08078    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08079       res = ast_play_and_wait(chan, "vm-nomessages");
08080       return res;
08081    } else {
08082       res = ast_play_and_wait(chan, "vm-youhave");
08083    }
08084    if (vms->newmessages) {
08085       if (!res)
08086          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08087       if ((vms->newmessages == 1)) {
08088          if (!res)
08089             res = ast_play_and_wait(chan, "vm-message");
08090          if (!res)
08091             res = ast_play_and_wait(chan, "vm-INBOXs");
08092       } else {
08093          if (!res)
08094             res = ast_play_and_wait(chan, "vm-messages");
08095          if (!res)
08096             res = ast_play_and_wait(chan, "vm-INBOX");
08097       }
08098       if (vms->oldmessages && !res)
08099          res = ast_play_and_wait(chan, "vm-and");
08100    }
08101    if (vms->oldmessages) {
08102       if (!res)
08103          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08104       if (vms->oldmessages == 1) {
08105          if (!res)
08106             res = ast_play_and_wait(chan, "vm-message");
08107          if (!res)
08108             res = ast_play_and_wait(chan, "vm-Olds");
08109       } else {
08110          if (!res)
08111             res = ast_play_and_wait(chan, "vm-messages");
08112          if (!res)
08113             res = ast_play_and_wait(chan, "vm-Old");
08114       }
08115    }
08116    return res;
08117 }
08118 
08119 /* FRENCH syntax */
08120 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
08121 {
08122    /* Introduce messages they have */
08123    int res;
08124    res = ast_play_and_wait(chan, "vm-youhave");
08125    if (!res) {
08126       if (vms->newmessages) {
08127          res = say_and_wait(chan, vms->newmessages, chan->language);
08128          if (!res)
08129             res = ast_play_and_wait(chan, "vm-INBOX");
08130          if (vms->oldmessages && !res)
08131             res = ast_play_and_wait(chan, "vm-and");
08132          else if (!res) {
08133             if ((vms->newmessages == 1))
08134                res = ast_play_and_wait(chan, "vm-message");
08135             else
08136                res = ast_play_and_wait(chan, "vm-messages");
08137          }
08138             
08139       }
08140       if (!res && vms->oldmessages) {
08141          res = say_and_wait(chan, vms->oldmessages, chan->language);
08142          if (!res)
08143             res = ast_play_and_wait(chan, "vm-Old");
08144          if (!res) {
08145             if (vms->oldmessages == 1)
08146                res = ast_play_and_wait(chan, "vm-message");
08147             else
08148                res = ast_play_and_wait(chan, "vm-messages");
08149          }
08150       }
08151       if (!res) {
08152          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08153             res = ast_play_and_wait(chan, "vm-no");
08154             if (!res)
08155                res = ast_play_and_wait(chan, "vm-messages");
08156          }
08157       }
08158    }
08159    return res;
08160 }
08161 
08162 /* DUTCH syntax */
08163 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
08164 {
08165    /* Introduce messages they have */
08166    int res;
08167    res = ast_play_and_wait(chan, "vm-youhave");
08168    if (!res) {
08169       if (vms->newmessages) {
08170          res = say_and_wait(chan, vms->newmessages, chan->language);
08171          if (!res) {
08172             if (vms->newmessages == 1)
08173                res = ast_play_and_wait(chan, "vm-INBOXs");
08174             else
08175                res = ast_play_and_wait(chan, "vm-INBOX");
08176          }
08177          if (vms->oldmessages && !res)
08178             res = ast_play_and_wait(chan, "vm-and");
08179          else if (!res) {
08180             if ((vms->newmessages == 1))
08181                res = ast_play_and_wait(chan, "vm-message");
08182             else
08183                res = ast_play_and_wait(chan, "vm-messages");
08184          }
08185             
08186       }
08187       if (!res && vms->oldmessages) {
08188          res = say_and_wait(chan, vms->oldmessages, chan->language);
08189          if (!res) {
08190             if (vms->oldmessages == 1)
08191                res = ast_play_and_wait(chan, "vm-Olds");
08192             else
08193                res = ast_play_and_wait(chan, "vm-Old");
08194          }
08195          if (!res) {
08196             if (vms->oldmessages == 1)
08197                res = ast_play_and_wait(chan, "vm-message");
08198             else
08199                res = ast_play_and_wait(chan, "vm-messages");
08200          }
08201       }
08202       if (!res) {
08203          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08204             res = ast_play_and_wait(chan, "vm-no");
08205             if (!res)
08206                res = ast_play_and_wait(chan, "vm-messages");
08207          }
08208       }
08209    }
08210    return res;
08211 }
08212 
08213 /* PORTUGUESE syntax */
08214 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08215 {
08216    /* Introduce messages they have */
08217    int res;
08218    res = ast_play_and_wait(chan, "vm-youhave");
08219    if (!res) {
08220       if (vms->newmessages) {
08221          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08222          if (!res) {
08223             if ((vms->newmessages == 1)) {
08224                res = ast_play_and_wait(chan, "vm-message");
08225                if (!res)
08226                   res = ast_play_and_wait(chan, "vm-INBOXs");
08227             } else {
08228                res = ast_play_and_wait(chan, "vm-messages");
08229                if (!res)
08230                   res = ast_play_and_wait(chan, "vm-INBOX");
08231             }
08232          }
08233          if (vms->oldmessages && !res)
08234             res = ast_play_and_wait(chan, "vm-and");
08235       }
08236       if (!res && vms->oldmessages) {
08237          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08238          if (!res) {
08239             if (vms->oldmessages == 1) {
08240                res = ast_play_and_wait(chan, "vm-message");
08241                if (!res)
08242                   res = ast_play_and_wait(chan, "vm-Olds");
08243             } else {
08244                res = ast_play_and_wait(chan, "vm-messages");
08245                if (!res)
08246                   res = ast_play_and_wait(chan, "vm-Old");
08247             }
08248          }
08249       }
08250       if (!res) {
08251          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08252             res = ast_play_and_wait(chan, "vm-no");
08253             if (!res)
08254                res = ast_play_and_wait(chan, "vm-messages");
08255          }
08256       }
08257    }
08258    return res;
08259 }
08260 
08261 
08262 /* CZECH syntax */
08263 /* in czech there must be declension of word new and message
08264  * czech        : english        : czech      : english
08265  * --------------------------------------------------------
08266  * vm-youhave   : you have 
08267  * vm-novou     : one new        : vm-zpravu  : message
08268  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08269  * vm-novych    : 5-infinite new : vm-zprav   : messages
08270  * vm-starou   : one old
08271  * vm-stare     : 2-4 old 
08272  * vm-starych   : 5-infinite old
08273  * jednu        : one   - falling 4. 
08274  * vm-no        : no  ( no messages )
08275  */
08276 
08277 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08278 {
08279    int res;
08280    res = ast_play_and_wait(chan, "vm-youhave");
08281    if (!res) {
08282       if (vms->newmessages) {
08283          if (vms->newmessages == 1) {
08284             res = ast_play_and_wait(chan, "digits/jednu");
08285          } else {
08286             res = say_and_wait(chan, vms->newmessages, chan->language);
08287          }
08288          if (!res) {
08289             if ((vms->newmessages == 1))
08290                res = ast_play_and_wait(chan, "vm-novou");
08291             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08292                res = ast_play_and_wait(chan, "vm-nove");
08293             if (vms->newmessages > 4)
08294                res = ast_play_and_wait(chan, "vm-novych");
08295          }
08296          if (vms->oldmessages && !res)
08297             res = ast_play_and_wait(chan, "vm-and");
08298          else if (!res) {
08299             if ((vms->newmessages == 1))
08300                res = ast_play_and_wait(chan, "vm-zpravu");
08301             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08302                res = ast_play_and_wait(chan, "vm-zpravy");
08303             if (vms->newmessages > 4)
08304                res = ast_play_and_wait(chan, "vm-zprav");
08305          }
08306       }
08307       if (!res && vms->oldmessages) {
08308          res = say_and_wait(chan, vms->oldmessages, chan->language);
08309          if (!res) {
08310             if ((vms->oldmessages == 1))
08311                res = ast_play_and_wait(chan, "vm-starou");
08312             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08313                res = ast_play_and_wait(chan, "vm-stare");
08314             if (vms->oldmessages > 4)
08315                res = ast_play_and_wait(chan, "vm-starych");
08316          }
08317          if (!res) {
08318             if ((vms->oldmessages == 1))
08319                res = ast_play_and_wait(chan, "vm-zpravu");
08320             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08321                res = ast_play_and_wait(chan, "vm-zpravy");
08322             if (vms->oldmessages > 4)
08323                res = ast_play_and_wait(chan, "vm-zprav");
08324          }
08325       }
08326       if (!res) {
08327          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08328             res = ast_play_and_wait(chan, "vm-no");
08329             if (!res)
08330                res = ast_play_and_wait(chan, "vm-zpravy");
08331          }
08332       }
08333    }
08334    return res;
08335 }
08336 
08337 /* CHINESE (Taiwan) syntax */
08338 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08339 {
08340    int res;
08341    /* Introduce messages they have */
08342    res = ast_play_and_wait(chan, "vm-you");
08343 
08344    if (!res && vms->newmessages) {
08345       res = ast_play_and_wait(chan, "vm-have");
08346       if (!res)
08347          res = say_and_wait(chan, vms->newmessages, chan->language);
08348       if (!res)
08349          res = ast_play_and_wait(chan, "vm-tong");
08350       if (!res)
08351          res = ast_play_and_wait(chan, "vm-INBOX");
08352       if (vms->oldmessages && !res)
08353          res = ast_play_and_wait(chan, "vm-and");
08354       else if (!res) 
08355          res = ast_play_and_wait(chan, "vm-messages");
08356    }
08357    if (!res && vms->oldmessages) {
08358       res = ast_play_and_wait(chan, "vm-have");
08359       if (!res)
08360          res = say_and_wait(chan, vms->oldmessages, chan->language);
08361       if (!res)
08362          res = ast_play_and_wait(chan, "vm-tong");
08363       if (!res)
08364          res = ast_play_and_wait(chan, "vm-Old");
08365       if (!res)
08366          res = ast_play_and_wait(chan, "vm-messages");
08367    }
08368    if (!res && !vms->oldmessages && !vms->newmessages) {
08369       res = ast_play_and_wait(chan, "vm-haveno");
08370       if (!res)
08371          res = ast_play_and_wait(chan, "vm-messages");
08372    }
08373    return res;
08374 }
08375 
08376 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08377 {
08378    char prefile[256];
08379    
08380    /* Notify the user that the temp greeting is set and give them the option to remove it */
08381    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08382    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08383       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08384       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08385          ast_play_and_wait(chan, "vm-tempgreetactive");
08386       }
08387       DISPOSE(prefile, -1);
08388    }
08389 
08390    /* Play voicemail intro - syntax is different for different languages */
08391    if (0) {
08392       return 0;
08393    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08394       return vm_intro_cs(chan, vms);
08395    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08396       static int deprecation_warning = 0;
08397       if (deprecation_warning++ % 10 == 0) {
08398          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08399       }
08400       return vm_intro_cs(chan, vms);
08401    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08402       return vm_intro_de(chan, vms);
08403    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08404       return vm_intro_es(chan, vms);
08405    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08406       return vm_intro_fr(chan, vms);
08407    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08408       return vm_intro_gr(chan, vms);
08409    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08410       return vm_intro_he(chan, vms);
08411    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08412       return vm_intro_it(chan, vms);
08413    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08414       return vm_intro_nl(chan, vms);
08415    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08416       return vm_intro_no(chan, vms);
08417    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08418       return vm_intro_pl(chan, vms);
08419    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08420       return vm_intro_pt_BR(chan, vms);
08421    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08422       return vm_intro_pt(chan, vms);
08423    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08424       return vm_intro_multilang(chan, vms, "n");
08425    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08426       return vm_intro_se(chan, vms);
08427    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08428       return vm_intro_multilang(chan, vms, "n");
08429    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08430       return vm_intro_zh(chan, vms);
08431    } else {                                             /* Default to ENGLISH */
08432       return vm_intro_en(chan, vms);
08433    }
08434 }
08435 
08436 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08437 {
08438    int res = 0;
08439    /* Play instructions and wait for new command */
08440    while (!res) {
08441       if (vms->starting) {
08442          if (vms->lastmsg > -1) {
08443             if (skipadvanced)
08444                res = ast_play_and_wait(chan, "vm-onefor-full");
08445             else
08446                res = ast_play_and_wait(chan, "vm-onefor");
08447             if (!res)
08448                res = vm_play_folder_name(chan, vms->vmbox);
08449          }
08450          if (!res) {
08451             if (skipadvanced)
08452                res = ast_play_and_wait(chan, "vm-opts-full");
08453             else
08454                res = ast_play_and_wait(chan, "vm-opts");
08455          }
08456       } else {
08457          /* Added for additional help */
08458          if (skipadvanced) {
08459             res = ast_play_and_wait(chan, "vm-onefor-full");
08460             if (!res)
08461                res = vm_play_folder_name(chan, vms->vmbox);
08462             res = ast_play_and_wait(chan, "vm-opts-full");
08463          }
08464          /* Logic:
08465           * If the current message is not the first OR
08466           * if we're listening to the first new message and there are
08467           * also urgent messages, then prompt for navigation to the
08468           * previous message
08469           */
08470          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08471             res = ast_play_and_wait(chan, "vm-prev");
08472          }
08473          if (!res && !skipadvanced)
08474             res = ast_play_and_wait(chan, "vm-advopts");
08475          if (!res)
08476             res = ast_play_and_wait(chan, "vm-repeat");
08477          /* Logic:
08478           * If we're not listening to the last message OR
08479           * we're listening to the last urgent message and there are
08480           * also new non-urgent messages, then prompt for navigation
08481           * to the next message
08482           */
08483          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08484             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08485             res = ast_play_and_wait(chan, "vm-next");
08486          }
08487          if (!res) {
08488             if (!vms->deleted[vms->curmsg])
08489                res = ast_play_and_wait(chan, "vm-delete");
08490             else
08491                res = ast_play_and_wait(chan, "vm-undelete");
08492             if (!res)
08493                res = ast_play_and_wait(chan, "vm-toforward");
08494             if (!res)
08495                res = ast_play_and_wait(chan, "vm-savemessage");
08496          }
08497       }
08498       if (!res) {
08499          res = ast_play_and_wait(chan, "vm-helpexit");
08500       }
08501       if (!res)
08502          res = ast_waitfordigit(chan, 6000);
08503       if (!res) {
08504          vms->repeats++;
08505          if (vms->repeats > 2) {
08506             res = 't';
08507          }
08508       }
08509    }
08510    return res;
08511 }
08512 
08513 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08514 {
08515    int res = 0;
08516    /* Play instructions and wait for new command */
08517    while (!res) {
08518       if (vms->lastmsg > -1) {
08519          res = ast_play_and_wait(chan, "vm-listen");
08520          if (!res)
08521             res = vm_play_folder_name(chan, vms->vmbox);
08522          if (!res)
08523             res = ast_play_and_wait(chan, "press");
08524          if (!res)
08525             res = ast_play_and_wait(chan, "digits/1");
08526       }
08527       if (!res)
08528          res = ast_play_and_wait(chan, "vm-opts");
08529       if (!res) {
08530          vms->starting = 0;
08531          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08532       }
08533    }
08534    return res;
08535 }
08536 
08537 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08538 {
08539    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08540       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08541    } else {             /* Default to ENGLISH */
08542       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08543    }
08544 }
08545 
08546 
08547 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08548 {
08549    int cmd = 0;
08550    int duration = 0;
08551    int tries = 0;
08552    char newpassword[80] = "";
08553    char newpassword2[80] = "";
08554    char prefile[PATH_MAX] = "";
08555    unsigned char buf[256];
08556    int bytes=0;
08557 
08558    if (ast_adsi_available(chan)) {
08559       bytes += adsi_logo(buf + bytes);
08560       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08561       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08562       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08563       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08564       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08565    }
08566 
08567    /* First, have the user change their password 
08568       so they won't get here again */
08569    for (;;) {
08570       newpassword[1] = '\0';
08571       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08572       if (cmd == '#')
08573          newpassword[0] = '\0';
08574       if (cmd < 0 || cmd == 't' || cmd == '#')
08575          return cmd;
08576       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08577       if (cmd < 0 || cmd == 't' || cmd == '#')
08578          return cmd;
08579       cmd = check_password(vmu, newpassword); /* perform password validation */
08580       if (cmd != 0) {
08581          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08582          cmd = ast_play_and_wait(chan, vm_invalid_password);
08583       } else {
08584          newpassword2[1] = '\0';
08585          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08586          if (cmd == '#')
08587             newpassword2[0] = '\0';
08588          if (cmd < 0 || cmd == 't' || cmd == '#')
08589             return cmd;
08590          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08591          if (cmd < 0 || cmd == 't' || cmd == '#')
08592             return cmd;
08593          if (!strcmp(newpassword, newpassword2))
08594             break;
08595          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08596          cmd = ast_play_and_wait(chan, vm_mismatch);
08597       }
08598       if (++tries == 3)
08599          return -1;
08600       if (cmd != 0) {
08601          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08602       }
08603    }
08604    if (pwdchange & PWDCHANGE_INTERNAL)
08605       vm_change_password(vmu, newpassword);
08606    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08607       vm_change_password_shell(vmu, newpassword);
08608 
08609    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08610    cmd = ast_play_and_wait(chan, vm_passchanged);
08611 
08612    /* If forcename is set, have the user record their name */  
08613    if (ast_test_flag(vmu, VM_FORCENAME)) {
08614       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08615       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08616          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08617          if (cmd < 0 || cmd == 't' || cmd == '#')
08618             return cmd;
08619       }
08620    }
08621 
08622    /* If forcegreetings is set, have the user record their greetings */
08623    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08624       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08625       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08626          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08627          if (cmd < 0 || cmd == 't' || cmd == '#')
08628             return cmd;
08629       }
08630 
08631       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08632       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08633          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08634          if (cmd < 0 || cmd == 't' || cmd == '#')
08635             return cmd;
08636       }
08637    }
08638 
08639    return cmd;
08640 }
08641 
08642 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08643 {
08644    int cmd = 0;
08645    int retries = 0;
08646    int duration = 0;
08647    char newpassword[80] = "";
08648    char newpassword2[80] = "";
08649    char prefile[PATH_MAX] = "";
08650    unsigned char buf[256];
08651    int bytes=0;
08652 
08653    if (ast_adsi_available(chan)) {
08654       bytes += adsi_logo(buf + bytes);
08655       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08656       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08657       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08658       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08659       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08660    }
08661    while ((cmd >= 0) && (cmd != 't')) {
08662       if (cmd)
08663          retries = 0;
08664       switch (cmd) {
08665       case '1': /* Record your unavailable message */
08666          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08667          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08668          break;
08669       case '2':  /* Record your busy message */
08670          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08671          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08672          break;
08673       case '3': /* Record greeting */
08674          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08675          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08676          break;
08677       case '4':  /* manage the temporary greeting */
08678          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08679          break;
08680       case '5': /* change password */
08681          if (vmu->password[0] == '-') {
08682             cmd = ast_play_and_wait(chan, "vm-no");
08683             break;
08684          }
08685          newpassword[1] = '\0';
08686          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08687          if (cmd == '#')
08688             newpassword[0] = '\0';
08689          else {
08690             if (cmd < 0)
08691                break;
08692             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08693                break;
08694             }
08695          }
08696          cmd = check_password(vmu, newpassword); /* perform password validation */
08697          if (cmd != 0) {
08698             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08699             cmd = ast_play_and_wait(chan, vm_invalid_password);
08700             if (!cmd) {
08701                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08702             }
08703             break;
08704          }
08705          newpassword2[1] = '\0';
08706          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08707          if (cmd == '#')
08708             newpassword2[0] = '\0';
08709          else {
08710             if (cmd < 0)
08711                break;
08712 
08713             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08714                break;
08715             }
08716          }
08717          if (strcmp(newpassword, newpassword2)) {
08718             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08719             cmd = ast_play_and_wait(chan, vm_mismatch);
08720             if (!cmd) {
08721                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08722             }
08723             break;
08724          }
08725          if (pwdchange & PWDCHANGE_INTERNAL)
08726             vm_change_password(vmu, newpassword);
08727          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08728             vm_change_password_shell(vmu, newpassword);
08729 
08730          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08731          cmd = ast_play_and_wait(chan, vm_passchanged);
08732          break;
08733       case '*': 
08734          cmd = 't';
08735          break;
08736       default: 
08737          cmd = 0;
08738          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08739          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08740          if (ast_fileexists(prefile, NULL, NULL)) {
08741             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08742          }
08743          DISPOSE(prefile, -1);
08744          if (!cmd) {
08745             cmd = ast_play_and_wait(chan, "vm-options");
08746          }
08747          if (!cmd) {
08748             cmd = ast_waitfordigit(chan,6000);
08749          }
08750          if (!cmd) {
08751             retries++;
08752          }
08753          if (retries > 3) {
08754             cmd = 't';
08755          }
08756       }
08757    }
08758    if (cmd == 't')
08759       cmd = 0;
08760    return cmd;
08761 }
08762 
08763 /*!
08764  * \brief The handler for 'record a temporary greeting'. 
08765  * \param chan
08766  * \param vmu
08767  * \param vms
08768  * \param fmtc
08769  * \param record_gain
08770  *
08771  * This is option 4 from the mailbox options menu.
08772  * This function manages the following promptings:
08773  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08774  * 2: remove (delete) the temporary greeting.
08775  * *: return to the main menu.
08776  *
08777  * \return zero on success, -1 on error.
08778  */
08779 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08780 {
08781    int cmd = 0;
08782    int retries = 0;
08783    int duration = 0;
08784    char prefile[PATH_MAX] = "";
08785    unsigned char buf[256];
08786    int bytes = 0;
08787 
08788    if (ast_adsi_available(chan)) {
08789       bytes += adsi_logo(buf + bytes);
08790       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08791       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08792       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08793       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08794       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08795    }
08796 
08797    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08798    while ((cmd >= 0) && (cmd != 't')) {
08799       if (cmd)
08800          retries = 0;
08801       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08802       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08803          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08804          cmd = 't';  
08805       } else {
08806          switch (cmd) {
08807          case '1':
08808             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08809             break;
08810          case '2':
08811             DELETE(prefile, -1, prefile, vmu);
08812             ast_play_and_wait(chan, "vm-tempremoved");
08813             cmd = 't';  
08814             break;
08815          case '*': 
08816             cmd = 't';
08817             break;
08818          default:
08819             cmd = ast_play_and_wait(chan,
08820                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08821                   "vm-tempgreeting2" : "vm-tempgreeting");
08822             if (!cmd)
08823                cmd = ast_waitfordigit(chan,6000);
08824             if (!cmd)
08825                retries++;
08826             if (retries > 3)
08827                cmd = 't';
08828          }
08829       }
08830       DISPOSE(prefile, -1);
08831    }
08832    if (cmd == 't')
08833       cmd = 0;
08834    return cmd;
08835 }
08836 
08837 /*!
08838  * \brief Greek syntax for 'You have N messages' greeting.
08839  * \param chan
08840  * \param vms
08841  * \param vmu
08842  *
08843  * \return zero on success, -1 on error.
08844  */   
08845 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08846 {
08847    int cmd=0;
08848 
08849    if (vms->lastmsg > -1) {
08850       cmd = play_message(chan, vmu, vms);
08851    } else {
08852       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08853       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08854          if (!cmd) {
08855             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08856             cmd = ast_play_and_wait(chan, vms->fn);
08857          }
08858          if (!cmd)
08859             cmd = ast_play_and_wait(chan, "vm-messages");
08860       } else {
08861          if (!cmd)
08862             cmd = ast_play_and_wait(chan, "vm-messages");
08863          if (!cmd) {
08864             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08865             cmd = ast_play_and_wait(chan, vms->fn);
08866          }
08867       }
08868    } 
08869    return cmd;
08870 }
08871 
08872 /* Hebrew Syntax */
08873 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08874 {
08875    int cmd = 0;
08876 
08877    if (vms->lastmsg > -1) {
08878       cmd = play_message(chan, vmu, vms);
08879    } else {
08880       if (!strcasecmp(vms->fn, "INBOX")) {
08881          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08882       } else {
08883          cmd = ast_play_and_wait(chan, "vm-nomessages");
08884       }
08885    }
08886    return cmd;
08887 }
08888 
08889 /*! 
08890  * \brief Default English syntax for 'You have N messages' greeting.
08891  * \param chan
08892  * \param vms
08893  * \param vmu
08894  *
08895  * \return zero on success, -1 on error.
08896  */
08897 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08898 {
08899    int cmd=0;
08900 
08901    if (vms->lastmsg > -1) {
08902       cmd = play_message(chan, vmu, vms);
08903    } else {
08904       cmd = ast_play_and_wait(chan, "vm-youhave");
08905       if (!cmd) 
08906          cmd = ast_play_and_wait(chan, "vm-no");
08907       if (!cmd) {
08908          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08909          cmd = ast_play_and_wait(chan, vms->fn);
08910       }
08911       if (!cmd)
08912          cmd = ast_play_and_wait(chan, "vm-messages");
08913    }
08914    return cmd;
08915 }
08916 
08917 /*! 
08918  *\brief Italian syntax for 'You have N messages' greeting.
08919  * \param chan
08920  * \param vms
08921  * \param vmu
08922  *
08923  * \return zero on success, -1 on error.
08924  */
08925 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08926 {
08927    int cmd=0;
08928 
08929    if (vms->lastmsg > -1) {
08930       cmd = play_message(chan, vmu, vms);
08931    } else {
08932       cmd = ast_play_and_wait(chan, "vm-no");
08933       if (!cmd)
08934          cmd = ast_play_and_wait(chan, "vm-message");
08935       if (!cmd) {
08936          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08937          cmd = ast_play_and_wait(chan, vms->fn);
08938       }
08939    }
08940    return cmd;
08941 }
08942 
08943 /*! 
08944  * \brief Spanish syntax for 'You have N messages' greeting.
08945  * \param chan
08946  * \param vms
08947  * \param vmu
08948  *
08949  * \return zero on success, -1 on error.
08950  */
08951 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08952 {
08953    int cmd=0;
08954 
08955    if (vms->lastmsg > -1) {
08956       cmd = play_message(chan, vmu, vms);
08957    } else {
08958       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08959       if (!cmd)
08960          cmd = ast_play_and_wait(chan, "vm-messages");
08961       if (!cmd) {
08962          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08963          cmd = ast_play_and_wait(chan, vms->fn);
08964       }
08965    }
08966    return cmd;
08967 }
08968 
08969 /*! 
08970  * \brief Portuguese syntax for 'You have N messages' greeting.
08971  * \param chan
08972  * \param vms
08973  * \param vmu
08974  *
08975  * \return zero on success, -1 on error.
08976  */
08977 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08978 {
08979    int cmd=0;
08980 
08981    if (vms->lastmsg > -1) {
08982       cmd = play_message(chan, vmu, vms);
08983    } else {
08984       cmd = ast_play_and_wait(chan, "vm-no");
08985       if (!cmd) {
08986          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08987          cmd = ast_play_and_wait(chan, vms->fn);
08988       }
08989       if (!cmd)
08990          cmd = ast_play_and_wait(chan, "vm-messages");
08991    }
08992    return cmd;
08993 }
08994 
08995 /*! 
08996  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08997  * \param chan
08998  * \param vms
08999  * \param vmu
09000  *
09001  * \return zero on success, -1 on error.
09002  */
09003 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09004 {
09005    int cmd=0;
09006 
09007    if (vms->lastmsg > -1) {
09008       cmd = play_message(chan, vmu, vms);
09009    } else {
09010       cmd = ast_play_and_wait(chan, "vm-you");
09011       if (!cmd) 
09012          cmd = ast_play_and_wait(chan, "vm-haveno");
09013       if (!cmd)
09014          cmd = ast_play_and_wait(chan, "vm-messages");
09015       if (!cmd) {
09016          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09017          cmd = ast_play_and_wait(chan, vms->fn);
09018       }
09019    }
09020    return cmd;
09021 }
09022 
09023 /*!
09024  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09025  * \param chan The channel for the current user. We read the language property from this.
09026  * \param vms passed into the language-specific vm_browse_messages function.
09027  * \param vmu passed into the language-specific vm_browse_messages function.
09028  * 
09029  * The method to be invoked is determined by the value of language code property in the user's channel.
09030  * The default (when unable to match) is to use english.
09031  *
09032  * \return zero on success, -1 on error.
09033  */
09034 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09035 {
09036    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09037       return vm_browse_messages_es(chan, vms, vmu);
09038    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09039       return vm_browse_messages_gr(chan, vms, vmu);
09040    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09041       return vm_browse_messages_he(chan, vms, vmu);
09042    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09043       return vm_browse_messages_it(chan, vms, vmu);
09044    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09045       return vm_browse_messages_pt(chan, vms, vmu);
09046    } else if (!strncasecmp(chan->language, "zh", 2)) {
09047       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
09048    } else {                                             /* Default to English syntax */
09049       return vm_browse_messages_en(chan, vms, vmu);
09050    }
09051 }
09052 
09053 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09054          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09055          int skipuser, int max_logins, int silent)
09056 {
09057    int useadsi=0, valid=0, logretries=0;
09058    char password[AST_MAX_EXTENSION]="", *passptr;
09059    struct ast_vm_user vmus, *vmu = NULL;
09060 
09061    /* If ADSI is supported, setup login screen */
09062    adsi_begin(chan, &useadsi);
09063    if (!skipuser && useadsi)
09064       adsi_login(chan);
09065    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09066       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09067       return -1;
09068    }
09069    
09070    /* Authenticate them and get their mailbox/password */
09071    
09072    while (!valid && (logretries < max_logins)) {
09073       /* Prompt for, and read in the username */
09074       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09075          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09076          return -1;
09077       }
09078       if (ast_strlen_zero(mailbox)) {
09079          if (chan->cid.cid_num) {
09080             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
09081          } else {
09082             ast_verb(3,"Username not entered\n");  
09083             return -1;
09084          }
09085       }
09086       if (useadsi)
09087          adsi_password(chan);
09088 
09089       if (!ast_strlen_zero(prefix)) {
09090          char fullusername[80] = "";
09091          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09092          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09093          ast_copy_string(mailbox, fullusername, mailbox_size);
09094       }
09095 
09096       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
09097       vmu = find_user(&vmus, context, mailbox);
09098       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09099          /* saved password is blank, so don't bother asking */
09100          password[0] = '\0';
09101       } else {
09102          if (ast_streamfile(chan, vm_password, chan->language)) {
09103             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09104             return -1;
09105          }
09106          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09107             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09108             return -1;
09109          }
09110       }
09111 
09112       if (vmu) {
09113          passptr = vmu->password;
09114          if (passptr[0] == '-') passptr++;
09115       }
09116       if (vmu && !strcmp(passptr, password))
09117          valid++;
09118       else {
09119          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09120          if (!ast_strlen_zero(prefix))
09121             mailbox[0] = '\0';
09122       }
09123       logretries++;
09124       if (!valid) {
09125          if (skipuser || logretries >= max_logins) {
09126             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09127                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09128                return -1;
09129             }
09130          } else {
09131             if (useadsi)
09132                adsi_login(chan);
09133             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09134                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09135                return -1;
09136             }
09137          }
09138          if (ast_waitstream(chan, "")) /* Channel is hung up */
09139             return -1;
09140       }
09141    }
09142    if (!valid && (logretries >= max_logins)) {
09143       ast_stopstream(chan);
09144       ast_play_and_wait(chan, "vm-goodbye");
09145       return -1;
09146    }
09147    if (vmu && !skipuser) {
09148       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09149    }
09150    return 0;
09151 }
09152 
09153 static int vm_execmain(struct ast_channel *chan, void *data)
09154 {
09155    /* XXX This is, admittedly, some pretty horrendous code.  For some
09156       reason it just seemed a lot easier to do with GOTO's.  I feel
09157       like I'm back in my GWBASIC days. XXX */
09158    int res=-1;
09159    int cmd=0;
09160    int valid = 0;
09161    char prefixstr[80] ="";
09162    char ext_context[256]="";
09163    int box;
09164    int useadsi = 0;
09165    int skipuser = 0;
09166    struct vm_state vms;
09167    struct ast_vm_user *vmu = NULL, vmus;
09168    char *context=NULL;
09169    int silentexit = 0;
09170    struct ast_flags flags = { 0 };
09171    signed char record_gain = 0;
09172    int play_auto = 0;
09173    int play_folder = 0;
09174    int in_urgent = 0;
09175 #ifdef IMAP_STORAGE
09176    int deleted = 0;
09177 #endif
09178 
09179    /* Add the vm_state to the active list and keep it active */
09180    memset(&vms, 0, sizeof(vms));
09181 
09182    vms.lastmsg = -1;
09183 
09184    memset(&vmus, 0, sizeof(vmus));
09185 
09186    if (chan->_state != AST_STATE_UP) {
09187       ast_debug(1, "Before ast_answer\n");
09188       ast_answer(chan);
09189    }
09190 
09191    if (!ast_strlen_zero(data)) {
09192       char *opts[OPT_ARG_ARRAY_SIZE];
09193       char *parse;
09194       AST_DECLARE_APP_ARGS(args,
09195          AST_APP_ARG(argv0);
09196          AST_APP_ARG(argv1);
09197       );
09198 
09199       parse = ast_strdupa(data);
09200 
09201       AST_STANDARD_APP_ARGS(args, parse);
09202 
09203       if (args.argc == 2) {
09204          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09205             return -1;
09206          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09207             int gain;
09208             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09209                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09210                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09211                   return -1;
09212                } else {
09213                   record_gain = (signed char) gain;
09214                }
09215             } else {
09216                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09217             }
09218          }
09219          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09220             play_auto = 1;
09221             if (opts[OPT_ARG_PLAYFOLDER]) {
09222                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09223                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09224                }
09225             } else {
09226                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09227             }  
09228             if ( play_folder > 9 || play_folder < 0) {
09229                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09230                play_folder = 0;
09231             }
09232          }
09233       } else {
09234          /* old style options parsing */
09235          while (*(args.argv0)) {
09236             if (*(args.argv0) == 's')
09237                ast_set_flag(&flags, OPT_SILENT);
09238             else if (*(args.argv0) == 'p')
09239                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09240             else 
09241                break;
09242             (args.argv0)++;
09243          }
09244 
09245       }
09246 
09247       valid = ast_test_flag(&flags, OPT_SILENT);
09248 
09249       if ((context = strchr(args.argv0, '@')))
09250          *context++ = '\0';
09251 
09252       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09253          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09254       else
09255          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09256 
09257       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09258          skipuser++;
09259       else
09260          valid = 0;
09261    }
09262 
09263    if (!valid)
09264       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09265 
09266    ast_debug(1, "After vm_authenticate\n");
09267    if (!res) {
09268       valid = 1;
09269       if (!skipuser)
09270          vmu = &vmus;
09271    } else {
09272       res = 0;
09273    }
09274 
09275    /* If ADSI is supported, setup login screen */
09276    adsi_begin(chan, &useadsi);
09277 
09278    if (!valid) {
09279       goto out;
09280    }
09281 
09282 #ifdef IMAP_STORAGE
09283    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09284    pthread_setspecific(ts_vmstate.key, &vms);
09285 
09286    vms.interactive = 1;
09287    vms.updated = 1;
09288    if (vmu)
09289       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09290    vmstate_insert(&vms);
09291    init_vm_state(&vms);
09292 #endif
09293    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09294       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09295       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09296       return -1;
09297    }
09298    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09299       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09300       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09301       return -1;
09302    }
09303    
09304    /* Set language from config to override channel language */
09305    if (!ast_strlen_zero(vmu->language))
09306       ast_string_field_set(chan, language, vmu->language);
09307 
09308    /* Retrieve urgent, old and new message counts */
09309    ast_debug(1, "Before open_mailbox\n");
09310    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09311    if (res == ERROR_LOCK_PATH)
09312       goto out;
09313    vms.oldmessages = vms.lastmsg + 1;
09314    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09315    /* check INBOX */
09316    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09317    if (res == ERROR_LOCK_PATH)
09318       goto out;
09319    vms.newmessages = vms.lastmsg + 1;
09320    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09321    /* Start in Urgent */
09322    in_urgent = 1;
09323    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09324    if (res == ERROR_LOCK_PATH)
09325       goto out;
09326    vms.urgentmessages = vms.lastmsg + 1;
09327    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09328 
09329    /* Select proper mailbox FIRST!! */
09330    if (play_auto) {
09331       if (vms.urgentmessages) {
09332          in_urgent = 1;
09333          res = open_mailbox(&vms, vmu, 11);
09334       } else {
09335          in_urgent = 0;
09336          res = open_mailbox(&vms, vmu, play_folder);
09337       }
09338       if (res == ERROR_LOCK_PATH)
09339          goto out;
09340 
09341       /* If there are no new messages, inform the user and hangup */
09342       if (vms.lastmsg == -1) {
09343          in_urgent = 0;
09344          cmd = vm_browse_messages(chan, &vms, vmu);
09345          res = 0;
09346          goto out;
09347       }
09348    } else {
09349       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09350          /* If we only have old messages start here */
09351          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09352          in_urgent = 0;
09353          play_folder = 1;
09354          if (res == ERROR_LOCK_PATH)
09355             goto out;
09356       } else if (!vms.urgentmessages && vms.newmessages) {
09357          /* If we have new messages but none are urgent */
09358          in_urgent = 0;
09359          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09360          if (res == ERROR_LOCK_PATH)
09361             goto out;
09362       }
09363    }
09364 
09365    if (useadsi)
09366       adsi_status(chan, &vms);
09367    res = 0;
09368 
09369    /* Check to see if this is a new user */
09370    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09371       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09372       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09373          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09374       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09375       if ((cmd == 't') || (cmd == '#')) {
09376          /* Timeout */
09377          res = 0;
09378          goto out;
09379       } else if (cmd < 0) {
09380          /* Hangup */
09381          res = -1;
09382          goto out;
09383       }
09384    }
09385 #ifdef IMAP_STORAGE
09386       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09387       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09388          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09389          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09390       }
09391       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09392       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09393          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09394          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09395       }
09396 #endif
09397    if (play_auto) {
09398       cmd = '1';
09399    } else {
09400       cmd = vm_intro(chan, vmu, &vms);
09401    }
09402 
09403    vms.repeats = 0;
09404    vms.starting = 1;
09405    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09406       /* Run main menu */
09407       switch (cmd) {
09408       case '1': /* First message */
09409          vms.curmsg = 0;
09410          /* Fall through */
09411       case '5': /* Play current message */
09412          cmd = vm_browse_messages(chan, &vms, vmu);
09413          break;
09414       case '2': /* Change folders */
09415          if (useadsi)
09416             adsi_folders(chan, 0, "Change to folder...");
09417          cmd = get_folder2(chan, "vm-changeto", 0);
09418          if (cmd == '#') {
09419             cmd = 0;
09420          } else if (cmd > 0) {
09421             cmd = cmd - '0';
09422             res = close_mailbox(&vms, vmu);
09423             if (res == ERROR_LOCK_PATH)
09424                goto out;
09425             /* If folder is not urgent, set in_urgent to zero! */
09426             if (cmd != 11) in_urgent = 0;
09427             res = open_mailbox(&vms, vmu, cmd);
09428             if (res == ERROR_LOCK_PATH)
09429                goto out;
09430             play_folder = cmd;
09431             cmd = 0;
09432          }
09433          if (useadsi)
09434             adsi_status2(chan, &vms);
09435             
09436          if (!cmd)
09437             cmd = vm_play_folder_name(chan, vms.vmbox);
09438 
09439          vms.starting = 1;
09440          break;
09441       case '3': /* Advanced options */
09442          cmd = 0;
09443          vms.repeats = 0;
09444          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09445             switch (cmd) {
09446             case '1': /* Reply */
09447                if (vms.lastmsg > -1 && !vms.starting) {
09448                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09449                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09450                      res = cmd;
09451                      goto out;
09452                   }
09453                } else
09454                   cmd = ast_play_and_wait(chan, "vm-sorry");
09455                cmd = 't';
09456                break;
09457             case '2': /* Callback */
09458                if (!vms.starting)
09459                   ast_verb(3, "Callback Requested\n");
09460                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09461                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09462                   if (cmd == 9) {
09463                      silentexit = 1;
09464                      goto out;
09465                   } else if (cmd == ERROR_LOCK_PATH) {
09466                      res = cmd;
09467                      goto out;
09468                   }
09469                } else 
09470                   cmd = ast_play_and_wait(chan, "vm-sorry");
09471                cmd = 't';
09472                break;
09473             case '3': /* Envelope */
09474                if (vms.lastmsg > -1 && !vms.starting) {
09475                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09476                   if (cmd == ERROR_LOCK_PATH) {
09477                      res = cmd;
09478                      goto out;
09479                   }
09480                } else
09481                   cmd = ast_play_and_wait(chan, "vm-sorry");
09482                cmd = 't';
09483                break;
09484             case '4': /* Dialout */
09485                if (!ast_strlen_zero(vmu->dialout)) {
09486                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09487                   if (cmd == 9) {
09488                      silentexit = 1;
09489                      goto out;
09490                   }
09491                } else 
09492                   cmd = ast_play_and_wait(chan, "vm-sorry");
09493                cmd = 't';
09494                break;
09495 
09496             case '5': /* Leave VoiceMail */
09497                if (ast_test_flag(vmu, VM_SVMAIL)) {
09498                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09499                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09500                      res = cmd;
09501                      goto out;
09502                   }
09503                } else
09504                   cmd = ast_play_and_wait(chan,"vm-sorry");
09505                cmd='t';
09506                break;
09507                
09508             case '*': /* Return to main menu */
09509                cmd = 't';
09510                break;
09511 
09512             default:
09513                cmd = 0;
09514                if (!vms.starting) {
09515                   cmd = ast_play_and_wait(chan, "vm-toreply");
09516                }
09517                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09518                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09519                }
09520                if (!cmd && !vms.starting) {
09521                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09522                }
09523                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09524                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09525                }
09526                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09527                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09528                if (!cmd)
09529                   cmd = ast_play_and_wait(chan, "vm-starmain");
09530                if (!cmd)
09531                   cmd = ast_waitfordigit(chan,6000);
09532                if (!cmd)
09533                   vms.repeats++;
09534                if (vms.repeats > 3)
09535                   cmd = 't';
09536             }
09537          }
09538          if (cmd == 't') {
09539             cmd = 0;
09540             vms.repeats = 0;
09541          }
09542          break;
09543       case '4': /* Go to the previous message */
09544          if (vms.curmsg > 0) {
09545             vms.curmsg--;
09546             cmd = play_message(chan, vmu, &vms);
09547          } else {
09548             /* Check if we were listening to new
09549                messages.  If so, go to Urgent messages
09550                instead of saying "no more messages"
09551             */
09552             if (in_urgent == 0 && vms.urgentmessages > 0) {
09553                /* Check for Urgent messages */
09554                in_urgent = 1;
09555                res = close_mailbox(&vms, vmu);
09556                if (res == ERROR_LOCK_PATH)
09557                   goto out;
09558                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09559                if (res == ERROR_LOCK_PATH)
09560                   goto out;
09561                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09562                vms.curmsg = vms.lastmsg;
09563                if (vms.lastmsg < 0)
09564                   cmd = ast_play_and_wait(chan, "vm-nomore");
09565             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09566                vms.curmsg = vms.lastmsg;
09567                cmd = play_message(chan, vmu, &vms);
09568             } else {
09569                cmd = ast_play_and_wait(chan, "vm-nomore");
09570             }
09571          }
09572          break;
09573       case '6': /* Go to the next message */
09574          if (vms.curmsg < vms.lastmsg) {
09575             vms.curmsg++;
09576             cmd = play_message(chan, vmu, &vms);
09577          } else {
09578             if (in_urgent && vms.newmessages > 0) {
09579                /* Check if we were listening to urgent
09580                 * messages.  If so, go to regular new messages
09581                 * instead of saying "no more messages"
09582                 */
09583                in_urgent = 0;
09584                res = close_mailbox(&vms, vmu);
09585                if (res == ERROR_LOCK_PATH)
09586                   goto out;
09587                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09588                if (res == ERROR_LOCK_PATH)
09589                   goto out;
09590                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09591                vms.curmsg = -1;
09592                if (vms.lastmsg < 0) {
09593                   cmd = ast_play_and_wait(chan, "vm-nomore");
09594                }
09595             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09596                vms.curmsg = 0;
09597                cmd = play_message(chan, vmu, &vms);
09598             } else {
09599                cmd = ast_play_and_wait(chan, "vm-nomore");
09600             }
09601          }
09602          break;
09603       case '7': /* Delete the current message */
09604          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09605             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09606             if (useadsi)
09607                adsi_delete(chan, &vms);
09608             if (vms.deleted[vms.curmsg]) {
09609                if (play_folder == 0) {
09610                   if (in_urgent) {
09611                      vms.urgentmessages--;
09612                   } else {
09613                      vms.newmessages--;
09614                   }
09615                }
09616                else if (play_folder == 1)
09617                   vms.oldmessages--;
09618                cmd = ast_play_and_wait(chan, "vm-deleted");
09619             } else {
09620                if (play_folder == 0) {
09621                   if (in_urgent) {
09622                      vms.urgentmessages++;
09623                   } else {
09624                      vms.newmessages++;
09625                   }
09626                }
09627                else if (play_folder == 1)
09628                   vms.oldmessages++;
09629                cmd = ast_play_and_wait(chan, "vm-undeleted");
09630             }
09631             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09632                if (vms.curmsg < vms.lastmsg) {
09633                   vms.curmsg++;
09634                   cmd = play_message(chan, vmu, &vms);
09635                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09636                   vms.curmsg = 0;
09637                   cmd = play_message(chan, vmu, &vms);
09638                } else {
09639                   /* Check if we were listening to urgent
09640                      messages.  If so, go to regular new messages
09641                      instead of saying "no more messages"
09642                   */
09643                   if (in_urgent == 1) {
09644                      /* Check for new messages */
09645                      in_urgent = 0;
09646                      res = close_mailbox(&vms, vmu);
09647                      if (res == ERROR_LOCK_PATH)
09648                         goto out;
09649                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09650                      if (res == ERROR_LOCK_PATH)
09651                         goto out;
09652                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09653                      vms.curmsg = -1;
09654                      if (vms.lastmsg < 0)
09655                         cmd = ast_play_and_wait(chan, "vm-nomore");
09656                   } else {
09657                      cmd = ast_play_and_wait(chan, "vm-nomore");
09658                   }
09659                }
09660             }
09661          } else /* Delete not valid if we haven't selected a message */
09662             cmd = 0;
09663 #ifdef IMAP_STORAGE
09664          deleted = 1;
09665 #endif
09666          break;
09667    
09668       case '8': /* Forward the current messgae */
09669          if (vms.lastmsg > -1) {
09670             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09671             if (cmd == ERROR_LOCK_PATH) {
09672                res = cmd;
09673                goto out;
09674             }
09675          } else {
09676             /* Check if we were listening to urgent
09677                messages.  If so, go to regular new messages
09678                instead of saying "no more messages"
09679             */
09680             if (in_urgent == 1 && vms.newmessages > 0) {
09681                /* Check for new messages */
09682                in_urgent = 0;
09683                res = close_mailbox(&vms, vmu);
09684                if (res == ERROR_LOCK_PATH)
09685                   goto out;
09686                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09687                if (res == ERROR_LOCK_PATH)
09688                   goto out;
09689                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09690                vms.curmsg = -1;
09691                if (vms.lastmsg < 0)
09692                   cmd = ast_play_and_wait(chan, "vm-nomore");
09693             } else {
09694                cmd = ast_play_and_wait(chan, "vm-nomore");
09695             }
09696          }
09697          break;
09698       case '9': /* Save message to folder */
09699          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09700             /* No message selected */
09701             cmd = 0;
09702             break;
09703          }
09704          if (useadsi)
09705             adsi_folders(chan, 1, "Save to folder...");
09706          cmd = get_folder2(chan, "vm-savefolder", 1);
09707          box = 0; /* Shut up compiler */
09708          if (cmd == '#') {
09709             cmd = 0;
09710             break;
09711          } else if (cmd > 0) {
09712             box = cmd = cmd - '0';
09713             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09714             if (cmd == ERROR_LOCK_PATH) {
09715                res = cmd;
09716                goto out;
09717 #ifndef IMAP_STORAGE
09718             } else if (!cmd) {
09719                vms.deleted[vms.curmsg] = 1;
09720 #endif
09721             } else {
09722                vms.deleted[vms.curmsg] = 0;
09723                vms.heard[vms.curmsg] = 0;
09724             }
09725          }
09726          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09727          if (useadsi)
09728             adsi_message(chan, &vms);
09729          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09730          if (!cmd) {
09731             cmd = ast_play_and_wait(chan, "vm-message");
09732             if (!cmd) 
09733                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09734             if (!cmd)
09735                cmd = ast_play_and_wait(chan, "vm-savedto");
09736             if (!cmd)
09737                cmd = vm_play_folder_name(chan, vms.fn);
09738          } else {
09739             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09740          }
09741          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09742             if (vms.curmsg < vms.lastmsg) {
09743                vms.curmsg++;
09744                cmd = play_message(chan, vmu, &vms);
09745             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09746                vms.curmsg = 0;
09747                cmd = play_message(chan, vmu, &vms);
09748             } else {
09749                /* Check if we were listening to urgent
09750                   messages.  If so, go to regular new messages
09751                   instead of saying "no more messages"
09752                */
09753                if (in_urgent == 1 && vms.newmessages > 0) {
09754                   /* Check for new messages */
09755                   in_urgent = 0;
09756                   res = close_mailbox(&vms, vmu);
09757                   if (res == ERROR_LOCK_PATH)
09758                      goto out;
09759                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09760                   if (res == ERROR_LOCK_PATH)
09761                      goto out;
09762                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09763                   vms.curmsg = -1;
09764                   if (vms.lastmsg < 0)
09765                      cmd = ast_play_and_wait(chan, "vm-nomore");
09766                } else {
09767                   cmd = ast_play_and_wait(chan, "vm-nomore");
09768                }
09769             }
09770          }
09771          break;
09772       case '*': /* Help */
09773          if (!vms.starting) {
09774             cmd = ast_play_and_wait(chan, "vm-onefor");
09775             if (!strncasecmp(chan->language, "he", 2)) {
09776                cmd = ast_play_and_wait(chan, "vm-for");
09777             }
09778             if (!cmd)
09779                cmd = vm_play_folder_name(chan, vms.vmbox);
09780             if (!cmd)
09781                cmd = ast_play_and_wait(chan, "vm-opts");
09782             if (!cmd)
09783                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09784          } else
09785             cmd = 0;
09786          break;
09787       case '0': /* Mailbox options */
09788          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09789          if (useadsi)
09790             adsi_status(chan, &vms);
09791          break;
09792       default: /* Nothing */
09793          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09794          break;
09795       }
09796    }
09797    if ((cmd == 't') || (cmd == '#')) {
09798       /* Timeout */
09799       res = 0;
09800    } else {
09801       /* Hangup */
09802       res = -1;
09803    }
09804 
09805 out:
09806    if (res > -1) {
09807       ast_stopstream(chan);
09808       adsi_goodbye(chan);
09809       if (valid && res != OPERATOR_EXIT) {
09810          if (silentexit)
09811             res = ast_play_and_wait(chan, "vm-dialout");
09812          else 
09813             res = ast_play_and_wait(chan, "vm-goodbye");
09814       }
09815       if ((valid && res > 0) || res == OPERATOR_EXIT) {
09816          res = 0;
09817       }
09818       if (useadsi)
09819          ast_adsi_unload_session(chan);
09820    }
09821    if (vmu)
09822       close_mailbox(&vms, vmu);
09823    if (valid) {
09824       int new = 0, old = 0, urgent = 0;
09825       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09826       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09827       /* Urgent flag not passwd to externnotify here */
09828       run_externnotify(vmu->context, vmu->mailbox, NULL);
09829       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09830       queue_mwi_event(ext_context, urgent, new, old);
09831    }
09832 #ifdef IMAP_STORAGE
09833    /* expunge message - use UID Expunge if supported on IMAP server*/
09834    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09835    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09836       ast_mutex_lock(&vms.lock);
09837 #ifdef HAVE_IMAP_TK2006
09838       if (LEVELUIDPLUS (vms.mailstream)) {
09839          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09840       } else 
09841 #endif
09842          mail_expunge(vms.mailstream);
09843       ast_mutex_unlock(&vms.lock);
09844    }
09845    /*  before we delete the state, we should copy pertinent info
09846     *  back to the persistent model */
09847    if (vmu) {
09848       vmstate_delete(&vms);
09849    }
09850 #endif
09851    if (vmu)
09852       free_user(vmu);
09853    if (vms.deleted)
09854       ast_free(vms.deleted);
09855    if (vms.heard)
09856       ast_free(vms.heard);
09857 
09858 #ifdef IMAP_STORAGE
09859    pthread_setspecific(ts_vmstate.key, NULL);
09860 #endif
09861    return res;
09862 }
09863 
09864 static int vm_exec(struct ast_channel *chan, void *data)
09865 {
09866    int res = 0;
09867    char *tmp;
09868    struct leave_vm_options leave_options;
09869    struct ast_flags flags = { 0 };
09870    char *opts[OPT_ARG_ARRAY_SIZE];
09871    AST_DECLARE_APP_ARGS(args,
09872       AST_APP_ARG(argv0);
09873       AST_APP_ARG(argv1);
09874    );
09875    
09876    memset(&leave_options, 0, sizeof(leave_options));
09877 
09878    if (chan->_state != AST_STATE_UP)
09879       ast_answer(chan);
09880 
09881    if (!ast_strlen_zero(data)) {
09882       tmp = ast_strdupa(data);
09883       AST_STANDARD_APP_ARGS(args, tmp);
09884       if (args.argc == 2) {
09885          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09886             return -1;
09887          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09888          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09889             int gain;
09890 
09891             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09892                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09893                return -1;
09894             } else {
09895                leave_options.record_gain = (signed char) gain;
09896             }
09897          }
09898          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09899             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09900                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09901          }
09902       }
09903    } else {
09904       char temp[256];
09905       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09906       if (res < 0)
09907          return res;
09908       if (ast_strlen_zero(temp))
09909          return 0;
09910       args.argv0 = ast_strdupa(temp);
09911    }
09912 
09913    res = leave_voicemail(chan, args.argv0, &leave_options);
09914    if (res == OPERATOR_EXIT) {
09915       res = 0;
09916    }
09917 
09918    if (res == ERROR_LOCK_PATH) {
09919       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09920       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09921       res = 0;
09922    }
09923 
09924    return res;
09925 }
09926 
09927 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09928 {
09929    struct ast_vm_user *vmu;
09930 
09931    AST_LIST_TRAVERSE(&users, vmu, list) {
09932       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09933          if (strcasecmp(vmu->context, context)) {
09934             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09935                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09936                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09937                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09938          }
09939          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09940          return NULL;
09941       }
09942       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09943          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09944          return NULL;
09945       }
09946    }
09947    
09948    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09949       return NULL;
09950    
09951    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09952    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09953 
09954    AST_LIST_INSERT_TAIL(&users, vmu, list);
09955    
09956    return vmu;
09957 }
09958 
09959 static int append_mailbox(const char *context, const char *box, const char *data)
09960 {
09961    /* Assumes lock is already held */
09962    char *tmp;
09963    char *stringp;
09964    char *s;
09965    struct ast_vm_user *vmu;
09966    char *mailbox_full;
09967    int new = 0, old = 0, urgent = 0;
09968 
09969    tmp = ast_strdupa(data);
09970 
09971    if (!(vmu = find_or_create(context, box)))
09972       return -1;
09973    
09974    populate_defaults(vmu);
09975 
09976    stringp = tmp;
09977    if ((s = strsep(&stringp, ","))) 
09978       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09979    if (stringp && (s = strsep(&stringp, ","))) 
09980       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09981    if (stringp && (s = strsep(&stringp, ","))) 
09982       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09983    if (stringp && (s = strsep(&stringp, ","))) 
09984       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09985    if (stringp && (s = strsep(&stringp, ","))) 
09986       apply_options(vmu, s);
09987 
09988    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09989    strcpy(mailbox_full, box);
09990    strcat(mailbox_full, "@");
09991    strcat(mailbox_full, context);
09992 
09993    inboxcount2(mailbox_full, &urgent, &new, &old);
09994    queue_mwi_event(mailbox_full, urgent, new, old);
09995 
09996    return 0;
09997 }
09998 
09999 static int vm_box_exists(struct ast_channel *chan, void *data) 
10000 {
10001    struct ast_vm_user svm;
10002    char *context, *box;
10003    AST_DECLARE_APP_ARGS(args,
10004       AST_APP_ARG(mbox);
10005       AST_APP_ARG(options);
10006    );
10007    static int dep_warning = 0;
10008 
10009    if (ast_strlen_zero(data)) {
10010       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10011       return -1;
10012    }
10013 
10014    if (!dep_warning) {
10015       dep_warning = 1;
10016       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
10017    }
10018 
10019    box = ast_strdupa(data);
10020 
10021    AST_STANDARD_APP_ARGS(args, box);
10022 
10023    if (args.options) {
10024    }
10025 
10026    if ((context = strchr(args.mbox, '@'))) {
10027       *context = '\0';
10028       context++;
10029    }
10030 
10031    if (find_user(&svm, context, args.mbox)) {
10032       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10033    } else
10034       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10035 
10036    return 0;
10037 }
10038 
10039 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10040 {
10041    struct ast_vm_user svm;
10042    AST_DECLARE_APP_ARGS(arg,
10043       AST_APP_ARG(mbox);
10044       AST_APP_ARG(context);
10045    );
10046 
10047    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10048 
10049    if (ast_strlen_zero(arg.mbox)) {
10050       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10051       return -1;
10052    }
10053 
10054    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10055    return 0;
10056 }
10057 
10058 static struct ast_custom_function mailbox_exists_acf = {
10059    .name = "MAILBOX_EXISTS",
10060    .read = acf_mailbox_exists,
10061 };
10062 
10063 static int vmauthenticate(struct ast_channel *chan, void *data)
10064 {
10065    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
10066    struct ast_vm_user vmus;
10067    char *options = NULL;
10068    int silent = 0, skipuser = 0;
10069    int res = -1;
10070    
10071    if (s) {
10072       s = ast_strdupa(s);
10073       user = strsep(&s, ",");
10074       options = strsep(&s, ",");
10075       if (user) {
10076          s = user;
10077          user = strsep(&s, "@");
10078          context = strsep(&s, "");
10079          if (!ast_strlen_zero(user))
10080             skipuser++;
10081          ast_copy_string(mailbox, user, sizeof(mailbox));
10082       }
10083    }
10084 
10085    if (options) {
10086       silent = (strchr(options, 's')) != NULL;
10087    }
10088 
10089    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10090       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10091       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10092       ast_play_and_wait(chan, "auth-thankyou");
10093       res = 0;
10094    }
10095 
10096    return res;
10097 }
10098 
10099 static char *show_users_realtime(int fd, const char *context)
10100 {
10101    struct ast_config *cfg;
10102    const char *cat = NULL;
10103 
10104    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10105       "context", context, SENTINEL))) {
10106       return CLI_FAILURE;
10107    }
10108 
10109    ast_cli(fd,
10110       "\n"
10111       "=============================================================\n"
10112       "=== Configured Voicemail Users ==============================\n"
10113       "=============================================================\n"
10114       "===\n");
10115 
10116    while ((cat = ast_category_browse(cfg, cat))) {
10117       struct ast_variable *var = NULL;
10118       ast_cli(fd,
10119          "=== Mailbox ...\n"
10120          "===\n");
10121       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10122          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10123       ast_cli(fd,
10124          "===\n"
10125          "=== ---------------------------------------------------------\n"
10126          "===\n");
10127    }
10128 
10129    ast_cli(fd,
10130       "=============================================================\n"
10131       "\n");
10132 
10133    ast_config_destroy(cfg);
10134 
10135    return CLI_SUCCESS;
10136 }
10137 
10138 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10139 {
10140    int which = 0;
10141    int wordlen;
10142    struct ast_vm_user *vmu;
10143    const char *context = "";
10144 
10145    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10146    if (pos > 4)
10147       return NULL;
10148    if (pos == 3)
10149       return (state == 0) ? ast_strdup("for") : NULL;
10150    wordlen = strlen(word);
10151    AST_LIST_TRAVERSE(&users, vmu, list) {
10152       if (!strncasecmp(word, vmu->context, wordlen)) {
10153          if (context && strcmp(context, vmu->context) && ++which > state)
10154             return ast_strdup(vmu->context);
10155          /* ignore repeated contexts ? */
10156          context = vmu->context;
10157       }
10158    }
10159    return NULL;
10160 }
10161 
10162 /*! \brief Show a list of voicemail users in the CLI */
10163 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10164 {
10165    struct ast_vm_user *vmu;
10166 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10167    const char *context = NULL;
10168    int users_counter = 0;
10169 
10170    switch (cmd) {
10171    case CLI_INIT:
10172       e->command = "voicemail show users";
10173       e->usage =
10174          "Usage: voicemail show users [for <context>]\n"
10175          "       Lists all mailboxes currently set up\n";
10176       return NULL;
10177    case CLI_GENERATE:
10178       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10179    }  
10180 
10181    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10182       return CLI_SHOWUSAGE;
10183    if (a->argc == 5) {
10184       if (strcmp(a->argv[3],"for"))
10185          return CLI_SHOWUSAGE;
10186       context = a->argv[4];
10187    }
10188 
10189    if (ast_check_realtime("voicemail")) {
10190       if (!context) {
10191          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10192          return CLI_SHOWUSAGE;
10193       }
10194       return show_users_realtime(a->fd, context);
10195    }
10196 
10197    AST_LIST_LOCK(&users);
10198    if (AST_LIST_EMPTY(&users)) {
10199       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10200       AST_LIST_UNLOCK(&users);
10201       return CLI_FAILURE;
10202    }
10203    if (a->argc == 3)
10204       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10205    else {
10206       int count = 0;
10207       AST_LIST_TRAVERSE(&users, vmu, list) {
10208          if (!strcmp(context, vmu->context))
10209             count++;
10210       }
10211       if (count) {
10212          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10213       } else {
10214          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10215          AST_LIST_UNLOCK(&users);
10216          return CLI_FAILURE;
10217       }
10218    }
10219    AST_LIST_TRAVERSE(&users, vmu, list) {
10220       int newmsgs = 0, oldmsgs = 0;
10221       char count[12], tmp[256] = "";
10222 
10223       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10224          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10225          inboxcount(tmp, &newmsgs, &oldmsgs);
10226          snprintf(count, sizeof(count), "%d", newmsgs);
10227          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10228          users_counter++;
10229       }
10230    }
10231    AST_LIST_UNLOCK(&users);
10232    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10233    return CLI_SUCCESS;
10234 }
10235 
10236 /*! \brief Show a list of voicemail zones in the CLI */
10237 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10238 {
10239    struct vm_zone *zone;
10240 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10241    char *res = CLI_SUCCESS;
10242 
10243    switch (cmd) {
10244    case CLI_INIT:
10245       e->command = "voicemail show zones";
10246       e->usage =
10247          "Usage: voicemail show zones\n"
10248          "       Lists zone message formats\n";
10249       return NULL;
10250    case CLI_GENERATE:
10251       return NULL;
10252    }
10253 
10254    if (a->argc != 3)
10255       return CLI_SHOWUSAGE;
10256 
10257    AST_LIST_LOCK(&zones);
10258    if (!AST_LIST_EMPTY(&zones)) {
10259       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10260       AST_LIST_TRAVERSE(&zones, zone, list) {
10261          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10262       }
10263    } else {
10264       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10265       res = CLI_FAILURE;
10266    }
10267    AST_LIST_UNLOCK(&zones);
10268 
10269    return res;
10270 }
10271 
10272 /*! \brief Reload voicemail configuration from the CLI */
10273 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10274 {
10275    switch (cmd) {
10276    case CLI_INIT:
10277       e->command = "voicemail reload";
10278       e->usage =
10279          "Usage: voicemail reload\n"
10280          "       Reload voicemail configuration\n";
10281       return NULL;
10282    case CLI_GENERATE:
10283       return NULL;
10284    }
10285 
10286    if (a->argc != 2)
10287       return CLI_SHOWUSAGE;
10288 
10289    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10290    load_config(1);
10291    
10292    return CLI_SUCCESS;
10293 }
10294 
10295 static struct ast_cli_entry cli_voicemail[] = {
10296    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10297    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10298    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10299 };
10300 
10301 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10302 {
10303    int new = 0, old = 0, urgent = 0;
10304 
10305    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10306 
10307    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10308       mwi_sub->old_urgent = urgent;
10309       mwi_sub->old_new = new;
10310       mwi_sub->old_old = old;
10311       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10312    }
10313 }
10314 
10315 static void poll_subscribed_mailboxes(void)
10316 {
10317    struct mwi_sub *mwi_sub;
10318 
10319    AST_RWLIST_RDLOCK(&mwi_subs);
10320    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10321       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10322          poll_subscribed_mailbox(mwi_sub);
10323       }
10324    }
10325    AST_RWLIST_UNLOCK(&mwi_subs);
10326 }
10327 
10328 static void *mb_poll_thread(void *data)
10329 {
10330    while (poll_thread_run) {
10331       struct timespec ts = { 0, };
10332       struct timeval wait;
10333 
10334       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10335       ts.tv_sec = wait.tv_sec;
10336       ts.tv_nsec = wait.tv_usec * 1000;
10337 
10338       ast_mutex_lock(&poll_lock);
10339       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10340       ast_mutex_unlock(&poll_lock);
10341 
10342       if (!poll_thread_run)
10343          break;
10344 
10345       poll_subscribed_mailboxes();
10346    }
10347 
10348    return NULL;
10349 }
10350 
10351 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10352 {
10353    ast_free(mwi_sub);
10354 }
10355 
10356 static int handle_unsubscribe(void *datap)
10357 {
10358    struct mwi_sub *mwi_sub;
10359    uint32_t *uniqueid = datap;
10360    
10361    AST_RWLIST_WRLOCK(&mwi_subs);
10362    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10363       if (mwi_sub->uniqueid == *uniqueid) {
10364          AST_LIST_REMOVE_CURRENT(entry);
10365          break;
10366       }
10367    }
10368    AST_RWLIST_TRAVERSE_SAFE_END
10369    AST_RWLIST_UNLOCK(&mwi_subs);
10370 
10371    if (mwi_sub)
10372       mwi_sub_destroy(mwi_sub);
10373 
10374    ast_free(uniqueid);  
10375    return 0;
10376 }
10377 
10378 static int handle_subscribe(void *datap)
10379 {
10380    unsigned int len;
10381    struct mwi_sub *mwi_sub;
10382    struct mwi_sub_task *p = datap;
10383 
10384    len = sizeof(*mwi_sub);
10385    if (!ast_strlen_zero(p->mailbox))
10386       len += strlen(p->mailbox);
10387 
10388    if (!ast_strlen_zero(p->context))
10389       len += strlen(p->context) + 1; /* Allow for seperator */
10390 
10391    if (!(mwi_sub = ast_calloc(1, len)))
10392       return -1;
10393 
10394    mwi_sub->uniqueid = p->uniqueid;
10395    if (!ast_strlen_zero(p->mailbox))
10396       strcpy(mwi_sub->mailbox, p->mailbox);
10397 
10398    if (!ast_strlen_zero(p->context)) {
10399       strcat(mwi_sub->mailbox, "@");
10400       strcat(mwi_sub->mailbox, p->context);
10401    }
10402 
10403    AST_RWLIST_WRLOCK(&mwi_subs);
10404    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10405    AST_RWLIST_UNLOCK(&mwi_subs);
10406    ast_free((void *) p->mailbox);
10407    ast_free((void *) p->context);
10408    ast_free(p);
10409    poll_subscribed_mailbox(mwi_sub);
10410    return 0;
10411 }
10412 
10413 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10414 {
10415    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10416    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10417       return;
10418 
10419    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10420       return;
10421 
10422    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10423    *uniqueid = u;
10424    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10425       ast_free(uniqueid);
10426    }
10427 }
10428 
10429 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10430 {
10431    struct mwi_sub_task *mwist;
10432    
10433    if (ast_event_get_type(event) != AST_EVENT_SUB)
10434       return;
10435 
10436    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10437       return;
10438 
10439    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10440       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10441       return;
10442    }
10443    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10444    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10445    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10446    
10447    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10448       ast_free(mwist);
10449    }
10450 }
10451 
10452 static void start_poll_thread(void)
10453 {
10454    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10455       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10456       AST_EVENT_IE_END);
10457 
10458    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10459       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10460       AST_EVENT_IE_END);
10461 
10462    if (mwi_sub_sub)
10463       ast_event_report_subs(mwi_sub_sub);
10464 
10465    poll_thread_run = 1;
10466 
10467    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10468 }
10469 
10470 static void stop_poll_thread(void)
10471 {
10472    poll_thread_run = 0;
10473 
10474    if (mwi_sub_sub) {
10475       ast_event_unsubscribe(mwi_sub_sub);
10476       mwi_sub_sub = NULL;
10477    }
10478 
10479    if (mwi_unsub_sub) {
10480       ast_event_unsubscribe(mwi_unsub_sub);
10481       mwi_unsub_sub = NULL;
10482    }
10483 
10484    ast_mutex_lock(&poll_lock);
10485    ast_cond_signal(&poll_cond);
10486    ast_mutex_unlock(&poll_lock);
10487 
10488    pthread_join(poll_thread, NULL);
10489 
10490    poll_thread = AST_PTHREADT_NULL;
10491 }
10492 
10493 /*! \brief Manager list voicemail users command */
10494 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10495 {
10496    struct ast_vm_user *vmu = NULL;
10497    const char *id = astman_get_header(m, "ActionID");
10498    char actionid[128] = "";
10499 
10500    if (!ast_strlen_zero(id))
10501       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10502 
10503    AST_LIST_LOCK(&users);
10504 
10505    if (AST_LIST_EMPTY(&users)) {
10506       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10507       AST_LIST_UNLOCK(&users);
10508       return RESULT_SUCCESS;
10509    }
10510    
10511    astman_send_ack(s, m, "Voicemail user list will follow");
10512    
10513    AST_LIST_TRAVERSE(&users, vmu, list) {
10514       char dirname[256];
10515 
10516 #ifdef IMAP_STORAGE
10517       int new, old;
10518       inboxcount(vmu->mailbox, &new, &old);
10519 #endif
10520       
10521       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10522       astman_append(s,
10523          "%s"
10524          "Event: VoicemailUserEntry\r\n"
10525          "VMContext: %s\r\n"
10526          "VoiceMailbox: %s\r\n"
10527          "Fullname: %s\r\n"
10528          "Email: %s\r\n"
10529          "Pager: %s\r\n"
10530          "ServerEmail: %s\r\n"
10531          "MailCommand: %s\r\n"
10532          "Language: %s\r\n"
10533          "TimeZone: %s\r\n"
10534          "Callback: %s\r\n"
10535          "Dialout: %s\r\n"
10536          "UniqueID: %s\r\n"
10537          "ExitContext: %s\r\n"
10538          "SayDurationMinimum: %d\r\n"
10539          "SayEnvelope: %s\r\n"
10540          "SayCID: %s\r\n"
10541          "AttachMessage: %s\r\n"
10542          "AttachmentFormat: %s\r\n"
10543          "DeleteMessage: %s\r\n"
10544          "VolumeGain: %.2f\r\n"
10545          "CanReview: %s\r\n"
10546          "CallOperator: %s\r\n"
10547          "MaxMessageCount: %d\r\n"
10548          "MaxMessageLength: %d\r\n"
10549          "NewMessageCount: %d\r\n"
10550 #ifdef IMAP_STORAGE
10551          "OldMessageCount: %d\r\n"
10552          "IMAPUser: %s\r\n"
10553 #endif
10554          "\r\n",
10555          actionid,
10556          vmu->context,
10557          vmu->mailbox,
10558          vmu->fullname,
10559          vmu->email,
10560          vmu->pager,
10561          vmu->serveremail,
10562          vmu->mailcmd,
10563          vmu->language,
10564          vmu->zonetag,
10565          vmu->callback,
10566          vmu->dialout,
10567          vmu->uniqueid,
10568          vmu->exit,
10569          vmu->saydurationm,
10570          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10571          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10572          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10573          vmu->attachfmt,
10574          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10575          vmu->volgain,
10576          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10577          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10578          vmu->maxmsg,
10579          vmu->maxsecs,
10580 #ifdef IMAP_STORAGE
10581          new, old, vmu->imapuser
10582 #else
10583          count_messages(vmu, dirname)
10584 #endif
10585          );
10586    }     
10587    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10588 
10589    AST_LIST_UNLOCK(&users);
10590 
10591    return RESULT_SUCCESS;
10592 }
10593 
10594 /*! \brief Free the users structure. */
10595 static void free_vm_users(void) 
10596 {
10597    struct ast_vm_user *current;
10598    AST_LIST_LOCK(&users);
10599    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10600       ast_set_flag(current, VM_ALLOCED);
10601       free_user(current);
10602    }
10603    AST_LIST_UNLOCK(&users);
10604 }
10605 
10606 /*! \brief Free the zones structure. */
10607 static void free_vm_zones(void)
10608 {
10609    struct vm_zone *zcur;
10610    AST_LIST_LOCK(&zones);
10611    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10612       free_zone(zcur);
10613    AST_LIST_UNLOCK(&zones);
10614 }
10615 
10616 static const char *substitute_escapes(const char *value)
10617 {
10618    char *current;
10619 
10620    /* Add 16 for fudge factor */
10621    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10622 
10623    ast_str_reset(str);
10624    
10625    /* Substitute strings \r, \n, and \t into the appropriate characters */
10626    for (current = (char *) value; *current; current++) {
10627       if (*current == '\\') {
10628          current++;
10629          if (!*current) {
10630             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10631             break;
10632          }
10633          switch (*current) {
10634          case 'r':
10635             ast_str_append(&str, 0, "\r");
10636             break;
10637          case 'n':
10638 #ifdef IMAP_STORAGE
10639             if (!str->used || str->str[str->used - 1] != '\r') {
10640                ast_str_append(&str, 0, "\r");
10641             }
10642 #endif
10643             ast_str_append(&str, 0, "\n");
10644             break;
10645          case 't':
10646             ast_str_append(&str, 0, "\t");
10647             break;
10648          default:
10649             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10650             break;
10651          }
10652       } else {
10653          ast_str_append(&str, 0, "%c", *current);
10654       }
10655    }
10656 
10657    return ast_str_buffer(str);
10658 }
10659 
10660 static int load_config(int reload)
10661 {
10662    struct ast_vm_user *current;
10663    struct ast_config *cfg, *ucfg;
10664    char *cat;
10665    struct ast_variable *var;
10666    const char *val;
10667    char *q, *stringp, *tmp;
10668    int x;
10669    int tmpadsi[4];
10670    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10671 
10672    ast_unload_realtime("voicemail");
10673    ast_unload_realtime("voicemail_data");
10674 
10675    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10676       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10677          return 0;
10678       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10679          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10680          ucfg = NULL;
10681       }
10682       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10683       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10684          ast_config_destroy(ucfg);
10685          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10686          return 0;
10687       }
10688    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10689       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10690       return 0;
10691    } else {
10692       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10693       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10694          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10695          ucfg = NULL;
10696       }
10697    }
10698 #ifdef IMAP_STORAGE
10699    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10700 #endif
10701    /* set audio control prompts */
10702    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10703    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10704    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10705    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10706    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10707 
10708    /* Free all the users structure */  
10709    free_vm_users();
10710 
10711    /* Free all the zones structure */
10712    free_vm_zones();
10713 
10714    AST_LIST_LOCK(&users);  
10715 
10716    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10717    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10718 
10719    if (cfg) {
10720       /* General settings */
10721 
10722       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10723          val = "default";
10724       ast_copy_string(userscontext, val, sizeof(userscontext));
10725       /* Attach voice message to mail message ? */
10726       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10727          val = "yes";
10728       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10729 
10730       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10731          val = "no";
10732       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10733 
10734       volgain = 0.0;
10735       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10736          sscanf(val, "%30lf", &volgain);
10737 
10738 #ifdef ODBC_STORAGE
10739       strcpy(odbc_database, "asterisk");
10740       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10741          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10742       }
10743       strcpy(odbc_table, "voicemessages");
10744       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10745          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10746       }
10747 #endif      
10748       /* Mail command */
10749       strcpy(mailcmd, SENDMAIL);
10750       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10751          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10752 
10753       maxsilence = 0;
10754       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10755          maxsilence = atoi(val);
10756          if (maxsilence > 0)
10757             maxsilence *= 1000;
10758       }
10759       
10760       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10761          maxmsg = MAXMSG;
10762       } else {
10763          maxmsg = atoi(val);
10764          if (maxmsg <= 0) {
10765             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10766             maxmsg = MAXMSG;
10767          } else if (maxmsg > MAXMSGLIMIT) {
10768             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10769             maxmsg = MAXMSGLIMIT;
10770          }
10771       }
10772 
10773       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10774          maxdeletedmsg = 0;
10775       } else {
10776          if (sscanf(val, "%30d", &x) == 1)
10777             maxdeletedmsg = x;
10778          else if (ast_true(val))
10779             maxdeletedmsg = MAXMSG;
10780          else
10781             maxdeletedmsg = 0;
10782 
10783          if (maxdeletedmsg < 0) {
10784             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10785             maxdeletedmsg = MAXMSG;
10786          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10787             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10788             maxdeletedmsg = MAXMSGLIMIT;
10789          }
10790       }
10791 
10792       /* Load date format config for voicemail mail */
10793       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10794          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10795       }
10796 
10797       /* External password changing command */
10798       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10799          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10800          pwdchange = PWDCHANGE_EXTERNAL;
10801       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10802          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10803          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10804       }
10805  
10806       /* External password validation command */
10807       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10808          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10809          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10810       }
10811 
10812 #ifdef IMAP_STORAGE
10813       /* IMAP server address */
10814       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10815          ast_copy_string(imapserver, val, sizeof(imapserver));
10816       } else {
10817          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10818       }
10819       /* IMAP server port */
10820       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10821          ast_copy_string(imapport, val, sizeof(imapport));
10822       } else {
10823          ast_copy_string(imapport,"143", sizeof(imapport));
10824       }
10825       /* IMAP server flags */
10826       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10827          ast_copy_string(imapflags, val, sizeof(imapflags));
10828       }
10829       /* IMAP server master username */
10830       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10831          ast_copy_string(authuser, val, sizeof(authuser));
10832       }
10833       /* IMAP server master password */
10834       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10835          ast_copy_string(authpassword, val, sizeof(authpassword));
10836       }
10837       /* Expunge on exit */
10838       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10839          if (ast_false(val))
10840             expungeonhangup = 0;
10841          else
10842             expungeonhangup = 1;
10843       } else {
10844          expungeonhangup = 1;
10845       }
10846       /* IMAP voicemail folder */
10847       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10848          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10849       } else {
10850          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10851       }
10852       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10853          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10854       }
10855       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10856          imapgreetings = ast_true(val);
10857       } else {
10858          imapgreetings = 0;
10859       }
10860       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10861          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10862       } else {
10863          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10864       }
10865 
10866       /* There is some very unorthodox casting done here. This is due
10867        * to the way c-client handles the argument passed in. It expects a 
10868        * void pointer and casts the pointer directly to a long without
10869        * first dereferencing it. */
10870       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10871          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10872       } else {
10873          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10874       }
10875 
10876       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10877          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10878       } else {
10879          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10880       }
10881 
10882       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10883          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10884       } else {
10885          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10886       }
10887 
10888       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10889          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10890       } else {
10891          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10892       }
10893 
10894       /* Increment configuration version */
10895       imapversion++;
10896 #endif
10897       /* External voicemail notify application */
10898       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10899          ast_copy_string(externnotify, val, sizeof(externnotify));
10900          ast_debug(1, "found externnotify: %s\n", externnotify);
10901       } else {
10902          externnotify[0] = '\0';
10903       }
10904 
10905       /* SMDI voicemail notification */
10906       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10907          ast_debug(1, "Enabled SMDI voicemail notification\n");
10908          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10909             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10910          } else {
10911             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10912             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10913          }
10914          if (!smdi_iface) {
10915             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10916          } 
10917       }
10918 
10919       /* Silence treshold */
10920       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10921       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10922          silencethreshold = atoi(val);
10923       
10924       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10925          val = ASTERISK_USERNAME;
10926       ast_copy_string(serveremail, val, sizeof(serveremail));
10927       
10928       vmmaxsecs = 0;
10929       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10930          if (sscanf(val, "%30d", &x) == 1) {
10931             vmmaxsecs = x;
10932          } else {
10933             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10934          }
10935       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10936          static int maxmessage_deprecate = 0;
10937          if (maxmessage_deprecate == 0) {
10938             maxmessage_deprecate = 1;
10939             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10940          }
10941          if (sscanf(val, "%30d", &x) == 1) {
10942             vmmaxsecs = x;
10943          } else {
10944             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10945          }
10946       }
10947 
10948       vmminsecs = 0;
10949       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10950          if (sscanf(val, "%30d", &x) == 1) {
10951             vmminsecs = x;
10952             if (maxsilence / 1000 >= vmminsecs) {
10953                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10954             }
10955          } else {
10956             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10957          }
10958       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10959          static int maxmessage_deprecate = 0;
10960          if (maxmessage_deprecate == 0) {
10961             maxmessage_deprecate = 1;
10962             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10963          }
10964          if (sscanf(val, "%30d", &x) == 1) {
10965             vmminsecs = x;
10966             if (maxsilence / 1000 >= vmminsecs) {
10967                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10968             }
10969          } else {
10970             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10971          }
10972       }
10973 
10974       val = ast_variable_retrieve(cfg, "general", "format");
10975       if (!val) {
10976          val = "wav";   
10977       } else {
10978          tmp = ast_strdupa(val);
10979          val = ast_format_str_reduce(tmp);
10980          if (!val) {
10981             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10982             val = "wav";
10983          }
10984       }
10985       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10986 
10987       skipms = 3000;
10988       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10989          if (sscanf(val, "%30d", &x) == 1) {
10990             maxgreet = x;
10991          } else {
10992             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10993          }
10994       }
10995 
10996       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10997          if (sscanf(val, "%30d", &x) == 1) {
10998             skipms = x;
10999          } else {
11000             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11001          }
11002       }
11003 
11004       maxlogins = 3;
11005       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11006          if (sscanf(val, "%30d", &x) == 1) {
11007             maxlogins = x;
11008          } else {
11009             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11010          }
11011       }
11012 
11013       minpassword = MINPASSWORD;
11014       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11015          if (sscanf(val, "%30d", &x) == 1) {
11016             minpassword = x;
11017          } else {
11018             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11019          }
11020       }
11021 
11022       /* Force new user to record name ? */
11023       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11024          val = "no";
11025       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11026 
11027       /* Force new user to record greetings ? */
11028       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11029          val = "no";
11030       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11031 
11032       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11033          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11034          stringp = ast_strdupa(val);
11035          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11036             if (!ast_strlen_zero(stringp)) {
11037                q = strsep(&stringp, ",");
11038                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11039                   q++;
11040                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11041                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11042             } else {
11043                cidinternalcontexts[x][0] = '\0';
11044             }
11045          }
11046       }
11047       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11048          ast_debug(1,"VM Review Option disabled globally\n");
11049          val = "no";
11050       }
11051       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11052 
11053       /* Temporary greeting reminder */
11054       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11055          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11056          val = "no";
11057       } else {
11058          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11059       }
11060       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11061       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11062          ast_debug(1, "VM next message wrap disabled globally\n");
11063          val = "no";
11064       }
11065       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11066 
11067       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11068          ast_debug(1,"VM Operator break disabled globally\n");
11069          val = "no";
11070       }
11071       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11072 
11073       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11074          ast_debug(1,"VM CID Info before msg disabled globally\n");
11075          val = "no";
11076       } 
11077       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11078 
11079       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
11080          ast_debug(1,"Send Voicemail msg disabled globally\n");
11081          val = "no";
11082       }
11083       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11084    
11085       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11086          ast_debug(1,"ENVELOPE before msg enabled globally\n");
11087          val = "yes";
11088       }
11089       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11090 
11091       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11092          ast_debug(1,"Move Heard enabled globally\n");
11093          val = "yes";
11094       }
11095       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11096 
11097       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11098          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11099          val = "no";
11100       }
11101       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11102 
11103       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11104          ast_debug(1,"Duration info before msg enabled globally\n");
11105          val = "yes";
11106       }
11107       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11108 
11109       saydurationminfo = 2;
11110       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11111          if (sscanf(val, "%30d", &x) == 1) {
11112             saydurationminfo = x;
11113          } else {
11114             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11115          }
11116       }
11117 
11118       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11119          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
11120          val = "no";
11121       }
11122       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11123 
11124       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11125          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11126          ast_debug(1, "found dialout context: %s\n", dialcontext);
11127       } else {
11128          dialcontext[0] = '\0';  
11129       }
11130       
11131       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11132          ast_copy_string(callcontext, val, sizeof(callcontext));
11133          ast_debug(1, "found callback context: %s\n", callcontext);
11134       } else {
11135          callcontext[0] = '\0';
11136       }
11137 
11138       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11139          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11140          ast_debug(1, "found operator context: %s\n", exitcontext);
11141       } else {
11142          exitcontext[0] = '\0';
11143       }
11144       
11145       /* load password sounds configuration */
11146       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11147          ast_copy_string(vm_password, val, sizeof(vm_password));
11148       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11149          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11150       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11151          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11152       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11153          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11154       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11155          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11156       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11157          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11158       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11159          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11160       }
11161       /* load configurable audio prompts */
11162       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11163          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11164       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11165          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11166       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11167          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11168       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11169          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11170       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11171          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11172 
11173       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11174          val = "no";
11175       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11176 
11177       poll_freq = DEFAULT_POLL_FREQ;
11178       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11179          if (sscanf(val, "%30u", &poll_freq) != 1) {
11180             poll_freq = DEFAULT_POLL_FREQ;
11181             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11182          }
11183       }
11184 
11185       poll_mailboxes = 0;
11186       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11187          poll_mailboxes = ast_true(val);
11188 
11189       if (ucfg) { 
11190          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11191             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11192                continue;
11193             if ((current = find_or_create(userscontext, cat))) {
11194                populate_defaults(current);
11195                apply_options_full(current, ast_variable_browse(ucfg, cat));
11196                ast_copy_string(current->context, userscontext, sizeof(current->context));
11197             }
11198          }
11199          ast_config_destroy(ucfg);
11200       }
11201       cat = ast_category_browse(cfg, NULL);
11202       while (cat) {
11203          if (strcasecmp(cat, "general")) {
11204             var = ast_variable_browse(cfg, cat);
11205             if (strcasecmp(cat, "zonemessages")) {
11206                /* Process mailboxes in this context */
11207                while (var) {
11208                   append_mailbox(cat, var->name, var->value);
11209                   var = var->next;
11210                }
11211             } else {
11212                /* Timezones in this context */
11213                while (var) {
11214                   struct vm_zone *z;
11215                   if ((z = ast_malloc(sizeof(*z)))) {
11216                      char *msg_format, *tzone;
11217                      msg_format = ast_strdupa(var->value);
11218                      tzone = strsep(&msg_format, "|,");
11219                      if (msg_format) {
11220                         ast_copy_string(z->name, var->name, sizeof(z->name));
11221                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11222                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11223                         AST_LIST_LOCK(&zones);
11224                         AST_LIST_INSERT_HEAD(&zones, z, list);
11225                         AST_LIST_UNLOCK(&zones);
11226                      } else {
11227                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11228                         ast_free(z);
11229                      }
11230                   } else {
11231                      AST_LIST_UNLOCK(&users);
11232                      ast_config_destroy(cfg);
11233                      return -1;
11234                   }
11235                   var = var->next;
11236                }
11237             }
11238          }
11239          cat = ast_category_browse(cfg, cat);
11240       }
11241       memset(fromstring, 0, sizeof(fromstring));
11242       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11243       strcpy(charset, "ISO-8859-1");
11244       if (emailbody) {
11245          ast_free(emailbody);
11246          emailbody = NULL;
11247       }
11248       if (emailsubject) {
11249          ast_free(emailsubject);
11250          emailsubject = NULL;
11251       }
11252       if (pagerbody) {
11253          ast_free(pagerbody);
11254          pagerbody = NULL;
11255       }
11256       if (pagersubject) {
11257          ast_free(pagersubject);
11258          pagersubject = NULL;
11259       }
11260       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11261          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11262       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11263          ast_copy_string(fromstring, val, sizeof(fromstring));
11264       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11265          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11266       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11267          ast_copy_string(charset, val, sizeof(charset));
11268       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11269          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11270          for (x = 0; x < 4; x++) {
11271             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11272          }
11273       }
11274       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11275          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11276          for (x = 0; x < 4; x++) {
11277             memcpy(&adsisec[x], &tmpadsi[x], 1);
11278          }
11279       }
11280       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11281          if (atoi(val)) {
11282             adsiver = atoi(val);
11283          }
11284       }
11285       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11286          ast_copy_string(zonetag, val, sizeof(zonetag));
11287       }
11288       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11289          emailsubject = ast_strdup(val);
11290       }
11291       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11292          emailbody = ast_strdup(substitute_escapes(val));
11293       }
11294       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11295          pagersubject = ast_strdup(val);
11296       }
11297       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11298          pagerbody = ast_strdup(substitute_escapes(val));
11299       }
11300       AST_LIST_UNLOCK(&users);
11301       ast_config_destroy(cfg);
11302 
11303       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11304          start_poll_thread();
11305       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11306          stop_poll_thread();;
11307 
11308       return 0;
11309    } else {
11310       AST_LIST_UNLOCK(&users);
11311       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11312       if (ucfg)
11313          ast_config_destroy(ucfg);
11314       return 0;
11315    }
11316 }
11317 
11318 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11319 {
11320    int res = -1;
11321    char dir[PATH_MAX];
11322    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11323    ast_debug(2, "About to try retrieving name file %s\n", dir);
11324    RETRIEVE(dir, -1, mailbox, context);
11325    if (ast_fileexists(dir, NULL, NULL)) {
11326       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11327    }
11328    DISPOSE(dir, -1);
11329    return res;
11330 }
11331 
11332 static int reload(void)
11333 {
11334    return load_config(1);
11335 }
11336 
11337 static int unload_module(void)
11338 {
11339    int res;
11340 
11341    res = ast_unregister_application(app);
11342    res |= ast_unregister_application(app2);
11343    res |= ast_unregister_application(app3);
11344    res |= ast_unregister_application(app4);
11345    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11346    res |= ast_manager_unregister("VoicemailUsersList");
11347    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11348    ast_uninstall_vm_functions();
11349    ao2_ref(inprocess_container, -1);
11350 
11351    if (poll_thread != AST_PTHREADT_NULL)
11352       stop_poll_thread();
11353 
11354    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11355    ast_unload_realtime("voicemail");
11356    ast_unload_realtime("voicemail_data");
11357 
11358    free_vm_users();
11359    free_vm_zones();
11360    return res;
11361 }
11362 
11363 static int load_module(void)
11364 {
11365    int res;
11366    my_umask = umask(0);
11367    umask(my_umask);
11368 
11369    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11370       return AST_MODULE_LOAD_DECLINE;
11371    }
11372 
11373    /* compute the location of the voicemail spool directory */
11374    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11375 
11376    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11377       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11378    }
11379 
11380    if ((res = load_config(0)))
11381       return res;
11382 
11383    res = ast_register_application_xml(app, vm_exec);
11384    res |= ast_register_application_xml(app2, vm_execmain);
11385    res |= ast_register_application_xml(app3, vm_box_exists);
11386    res |= ast_register_application_xml(app4, vmauthenticate);
11387    res |= ast_custom_function_register(&mailbox_exists_acf);
11388    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11389    if (res)
11390       return res;
11391 
11392    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11393 
11394    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11395    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11396    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11397 
11398    return res;
11399 }
11400 
11401 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11402 {
11403    int cmd = 0;
11404    char destination[80] = "";
11405    int retries = 0;
11406 
11407    if (!num) {
11408       ast_verb(3, "Destination number will be entered manually\n");
11409       while (retries < 3 && cmd != 't') {
11410          destination[1] = '\0';
11411          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11412          if (!cmd)
11413             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11414          if (!cmd)
11415             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11416          if (!cmd) {
11417             cmd = ast_waitfordigit(chan, 6000);
11418             if (cmd)
11419                destination[0] = cmd;
11420          }
11421          if (!cmd) {
11422             retries++;
11423          } else {
11424 
11425             if (cmd < 0)
11426                return 0;
11427             if (cmd == '*') {
11428                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11429                return 0;
11430             }
11431             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11432                retries++;
11433             else
11434                cmd = 't';
11435          }
11436       }
11437       if (retries >= 3) {
11438          return 0;
11439       }
11440       
11441    } else {
11442       if (option_verbose > 2)
11443          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11444       ast_copy_string(destination, num, sizeof(destination));
11445    }
11446 
11447    if (!ast_strlen_zero(destination)) {
11448       if (destination[strlen(destination) -1 ] == '*')
11449          return 0; 
11450       if (option_verbose > 2)
11451          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11452       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11453       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11454       chan->priority = 0;
11455       return 9;
11456    }
11457    return 0;
11458 }
11459 
11460 /*!
11461  * \brief The advanced options within a message.
11462  * \param chan
11463  * \param vmu 
11464  * \param vms
11465  * \param msg
11466  * \param option
11467  * \param record_gain
11468  *
11469  * Provides handling for the play message envelope, call the person back, or reply to message. 
11470  *
11471  * \return zero on success, -1 on error.
11472  */
11473 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)
11474 {
11475    int res = 0;
11476    char filename[PATH_MAX];
11477    struct ast_config *msg_cfg = NULL;
11478    const char *origtime, *context;
11479    char *name, *num;
11480    int retries = 0;
11481    char *cid;
11482    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11483 
11484    vms->starting = 0; 
11485 
11486    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11487 
11488    /* Retrieve info from VM attribute file */
11489    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11490    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11491    msg_cfg = ast_config_load(filename, config_flags);
11492    DISPOSE(vms->curdir, vms->curmsg);
11493    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11494       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11495       return 0;
11496    }
11497 
11498    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11499       ast_config_destroy(msg_cfg);
11500       return 0;
11501    }
11502 
11503    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11504 
11505    context = ast_variable_retrieve(msg_cfg, "message", "context");
11506    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11507       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11508    switch (option) {
11509    case 3: /* Play message envelope */
11510       if (!res)
11511          res = play_message_datetime(chan, vmu, origtime, filename);
11512       if (!res)
11513          res = play_message_callerid(chan, vms, cid, context, 0);
11514 
11515       res = 't';
11516       break;
11517 
11518    case 2:  /* Call back */
11519 
11520       if (ast_strlen_zero(cid))
11521          break;
11522 
11523       ast_callerid_parse(cid, &name, &num);
11524       while ((res > -1) && (res != 't')) {
11525          switch (res) {
11526          case '1':
11527             if (num) {
11528                /* Dial the CID number */
11529                res = dialout(chan, vmu, num, vmu->callback);
11530                if (res) {
11531                   ast_config_destroy(msg_cfg);
11532                   return 9;
11533                }
11534             } else {
11535                res = '2';
11536             }
11537             break;
11538 
11539          case '2':
11540             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11541             if (!ast_strlen_zero(vmu->dialout)) {
11542                res = dialout(chan, vmu, NULL, vmu->dialout);
11543                if (res) {
11544                   ast_config_destroy(msg_cfg);
11545                   return 9;
11546                }
11547             } else {
11548                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11549                res = ast_play_and_wait(chan, "vm-sorry");
11550             }
11551             ast_config_destroy(msg_cfg);
11552             return res;
11553          case '*':
11554             res = 't';
11555             break;
11556          case '3':
11557          case '4':
11558          case '5':
11559          case '6':
11560          case '7':
11561          case '8':
11562          case '9':
11563          case '0':
11564 
11565             res = ast_play_and_wait(chan, "vm-sorry");
11566             retries++;
11567             break;
11568          default:
11569             if (num) {
11570                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11571                res = ast_play_and_wait(chan, "vm-num-i-have");
11572                if (!res)
11573                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11574                if (!res)
11575                   res = ast_play_and_wait(chan, "vm-tocallnum");
11576                /* Only prompt for a caller-specified number if there is a dialout context specified */
11577                if (!ast_strlen_zero(vmu->dialout)) {
11578                   if (!res)
11579                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11580                }
11581             } else {
11582                res = ast_play_and_wait(chan, "vm-nonumber");
11583                if (!ast_strlen_zero(vmu->dialout)) {
11584                   if (!res)
11585                      res = ast_play_and_wait(chan, "vm-toenternumber");
11586                }
11587             }
11588             if (!res)
11589                res = ast_play_and_wait(chan, "vm-star-cancel");
11590             if (!res)
11591                res = ast_waitfordigit(chan, 6000);
11592             if (!res) {
11593                retries++;
11594                if (retries > 3)
11595                   res = 't';
11596             }
11597             break; 
11598             
11599          }
11600          if (res == 't')
11601             res = 0;
11602          else if (res == '*')
11603             res = -1;
11604       }
11605       break;
11606       
11607    case 1:  /* Reply */
11608       /* Send reply directly to sender */
11609       if (ast_strlen_zero(cid))
11610          break;
11611 
11612       ast_callerid_parse(cid, &name, &num);
11613       if (!num) {
11614          ast_verb(3, "No CID number available, no reply sent\n");
11615          if (!res)
11616             res = ast_play_and_wait(chan, "vm-nonumber");
11617          ast_config_destroy(msg_cfg);
11618          return res;
11619       } else {
11620          struct ast_vm_user vmu2;
11621          if (find_user(&vmu2, vmu->context, num)) {
11622             struct leave_vm_options leave_options;
11623             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11624             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11625 
11626             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11627             
11628             memset(&leave_options, 0, sizeof(leave_options));
11629             leave_options.record_gain = record_gain;
11630             res = leave_voicemail(chan, mailbox, &leave_options);
11631             if (!res)
11632                res = 't';
11633             ast_config_destroy(msg_cfg);
11634             return res;
11635          } else {
11636             /* Sender has no mailbox, can't reply */
11637             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11638             ast_play_and_wait(chan, "vm-nobox");
11639             res = 't';
11640             ast_config_destroy(msg_cfg);
11641             return res;
11642          }
11643       } 
11644       res = 0;
11645 
11646       break;
11647    }
11648 
11649 #ifndef IMAP_STORAGE
11650    ast_config_destroy(msg_cfg);
11651 
11652    if (!res) {
11653       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11654       vms->heard[msg] = 1;
11655       res = wait_file(chan, vms, vms->fn);
11656    }
11657 #endif
11658    return res;
11659 }
11660 
11661 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11662          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11663          signed char record_gain, struct vm_state *vms, char *flag)
11664 {
11665    /* Record message & let caller review or re-record it, or set options if applicable */
11666    int res = 0;
11667    int cmd = 0;
11668    int max_attempts = 3;
11669    int attempts = 0;
11670    int recorded = 0;
11671    int msg_exists = 0;
11672    signed char zero_gain = 0;
11673    char tempfile[PATH_MAX];
11674    char *acceptdtmf = "#";
11675    char *canceldtmf = "";
11676 
11677    /* Note that urgent and private are for flagging messages as such in the future */
11678 
11679    /* barf if no pointer passed to store duration in */
11680    if (duration == NULL) {
11681       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11682       return -1;
11683    }
11684 
11685    if (!outsidecaller)
11686       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11687    else
11688       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11689 
11690    cmd = '3';  /* Want to start by recording */
11691 
11692    while ((cmd >= 0) && (cmd != 't')) {
11693       switch (cmd) {
11694       case '1':
11695          if (!msg_exists) {
11696             /* In this case, 1 is to record a message */
11697             cmd = '3';
11698             break;
11699          } else {
11700             /* Otherwise 1 is to save the existing message */
11701             ast_verb(3, "Saving message as is\n");
11702             if (!outsidecaller) 
11703                ast_filerename(tempfile, recordfile, NULL);
11704             ast_stream_and_wait(chan, "vm-msgsaved", "");
11705             if (!outsidecaller) {
11706                /* Saves to IMAP server only if imapgreeting=yes */
11707                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11708                DISPOSE(recordfile, -1);
11709             }
11710             cmd = 't';
11711             return res;
11712          }
11713       case '2':
11714          /* Review */
11715          ast_verb(3, "Reviewing the message\n");
11716          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11717          break;
11718       case '3':
11719          msg_exists = 0;
11720          /* Record */
11721          if (recorded == 1) 
11722             ast_verb(3, "Re-recording the message\n");
11723          else  
11724             ast_verb(3, "Recording the message\n");
11725          
11726          if (recorded && outsidecaller) {
11727             cmd = ast_play_and_wait(chan, INTRO);
11728             cmd = ast_play_and_wait(chan, "beep");
11729          }
11730          recorded = 1;
11731          /* 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 */
11732          if (record_gain)
11733             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11734          if (ast_test_flag(vmu, VM_OPERATOR))
11735             canceldtmf = "0";
11736          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11737          if (record_gain)
11738             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11739          if (cmd == -1) {
11740             /* User has hung up, no options to give */
11741             if (!outsidecaller) {
11742                /* user was recording a greeting and they hung up, so let's delete the recording. */
11743                ast_filedelete(tempfile, NULL);
11744             }     
11745             return cmd;
11746          }
11747          if (cmd == '0') {
11748             break;
11749          } else if (cmd == '*') {
11750             break;
11751 #if 0
11752          } else if (vmu->review && (*duration < 5)) {
11753             /* Message is too short */
11754             ast_verb(3, "Message too short\n");
11755             cmd = ast_play_and_wait(chan, "vm-tooshort");
11756             cmd = ast_filedelete(tempfile, NULL);
11757             break;
11758          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11759             /* Message is all silence */
11760             ast_verb(3, "Nothing recorded\n");
11761             cmd = ast_filedelete(tempfile, NULL);
11762             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11763             if (!cmd)
11764                cmd = ast_play_and_wait(chan, "vm-speakup");
11765             break;
11766 #endif
11767          } else {
11768             /* If all is well, a message exists */
11769             msg_exists = 1;
11770             cmd = 0;
11771          }
11772          break;
11773       case '4':
11774          if (outsidecaller) {  /* only mark vm messages */
11775             /* Mark Urgent */
11776             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11777                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11778                res = ast_play_and_wait(chan, "vm-marked-urgent");
11779                strcpy(flag, "Urgent");
11780             } else if (flag) {
11781                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11782                res = ast_play_and_wait(chan, "vm-urgent-removed");
11783                strcpy(flag, "");
11784             } else {
11785                ast_play_and_wait(chan, "vm-sorry");
11786             }
11787             cmd = 0;
11788          } else {
11789             cmd = ast_play_and_wait(chan, "vm-sorry");
11790          }
11791          break;
11792       case '5':
11793       case '6':
11794       case '7':
11795       case '8':
11796       case '9':
11797       case '*':
11798       case '#':
11799          cmd = ast_play_and_wait(chan, "vm-sorry");
11800          break;
11801 #if 0 
11802 /*  XXX Commented out for the moment because of the dangers of deleting
11803     a message while recording (can put the message numbers out of sync) */
11804       case '*':
11805          /* Cancel recording, delete message, offer to take another message*/
11806          cmd = ast_play_and_wait(chan, "vm-deleted");
11807          cmd = ast_filedelete(tempfile, NULL);
11808          if (outsidecaller) {
11809             res = vm_exec(chan, NULL);
11810             return res;
11811          }
11812          else
11813             return 1;
11814 #endif
11815       case '0':
11816          if (!ast_test_flag(vmu, VM_OPERATOR) || !outsidecaller) {
11817             cmd = ast_play_and_wait(chan, "vm-sorry");
11818             break;
11819          }
11820          if (msg_exists || recorded) {
11821             cmd = ast_play_and_wait(chan, "vm-saveoper");
11822             if (!cmd)
11823                cmd = ast_waitfordigit(chan, 3000);
11824             if (cmd == '1') {
11825                ast_play_and_wait(chan, "vm-msgsaved");
11826                cmd = '0';
11827             } else if (cmd == '4') {
11828                if (flag) {
11829                   ast_play_and_wait(chan, "vm-marked-urgent");
11830                   strcpy(flag, "Urgent");
11831                }
11832                ast_play_and_wait(chan, "vm-msgsaved");
11833                cmd = '0';
11834             } else {
11835                ast_play_and_wait(chan, "vm-deleted");
11836                DELETE(recordfile, -1, recordfile, vmu);
11837                cmd = '0';
11838             }
11839          }
11840          return cmd;
11841       default:
11842          /* If the caller is an ouside caller, and the review option is enabled,
11843             allow them to review the message, but let the owner of the box review
11844             their OGM's */
11845          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11846             return cmd;
11847          if (msg_exists) {
11848             cmd = ast_play_and_wait(chan, "vm-review");
11849             if (!cmd && outsidecaller) {
11850                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11851                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11852                } else if (flag) {
11853                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11854                }
11855             }
11856          } else {
11857             cmd = ast_play_and_wait(chan, "vm-torerecord");
11858             if (!cmd)
11859                cmd = ast_waitfordigit(chan, 600);
11860          }
11861          
11862          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11863             cmd = ast_play_and_wait(chan, "vm-reachoper");
11864             if (!cmd)
11865                cmd = ast_waitfordigit(chan, 600);
11866          }
11867 #if 0
11868          if (!cmd)
11869             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11870 #endif
11871          if (!cmd)
11872             cmd = ast_waitfordigit(chan, 6000);
11873          if (!cmd) {
11874             attempts++;
11875          }
11876          if (attempts > max_attempts) {
11877             cmd = 't';
11878          }
11879       }
11880    }
11881    if (cmd == 't')
11882       cmd = 0;
11883    else if (outsidecaller) /* won't play if time out occurs */
11884       ast_play_and_wait(chan, "vm-goodbye");
11885    return cmd;
11886 }
11887 
11888 /* This is a workaround so that menuselect displays a proper description
11889  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11890  */
11891 
11892 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11893       .load = load_module,
11894       .unload = unload_module,
11895       .reload = reload,
11896       );