Thu Apr 28 2011 17:15:15

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: 306966 $")
00089 
00090 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00091 #include <sys/time.h>
00092 #include <sys/stat.h>
00093 #include <sys/mman.h>
00094 #include <time.h>
00095 #include <dirent.h>
00096 
00097 #include "asterisk/logger.h"
00098 #include "asterisk/lock.h"
00099 #include "asterisk/file.h"
00100 #include "asterisk/channel.h"
00101 #include "asterisk/pbx.h"
00102 #include "asterisk/config.h"
00103 #include "asterisk/say.h"
00104 #include "asterisk/module.h"
00105 #include "asterisk/adsi.h"
00106 #include "asterisk/app.h"
00107 #include "asterisk/manager.h"
00108 #include "asterisk/dsp.h"
00109 #include "asterisk/localtime.h"
00110 #include "asterisk/cli.h"
00111 #include "asterisk/utils.h"
00112 #include "asterisk/stringfields.h"
00113 #include "asterisk/smdi.h"
00114 #include "asterisk/astobj2.h"
00115 #include "asterisk/event.h"
00116 #include "asterisk/taskprocessor.h"
00117 
00118 #ifdef ODBC_STORAGE
00119 #include "asterisk/res_odbc.h"
00120 #endif
00121 
00122 #ifdef IMAP_STORAGE
00123 #include "asterisk/threadstorage.h"
00124 #endif
00125 
00126 /*** DOCUMENTATION
00127    <application name="VoiceMail" language="en_US">
00128       <synopsis>
00129          Leave a Voicemail message.
00130       </synopsis>
00131       <syntax>
00132          <parameter name="mailboxs" argsep="&amp;" required="true">
00133             <argument name="mailbox1" argsep="@" required="true">
00134                <argument name="mailbox" required="true" />
00135                <argument name="context" />
00136             </argument>
00137             <argument name="mailbox2" argsep="@" multiple="true">
00138                <argument name="mailbox" required="true" />
00139                <argument name="context" />
00140             </argument>
00141          </parameter>
00142          <parameter name="options">
00143             <optionlist>
00144                <option name="b">
00145                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00146                </option>
00147                <option name="d">
00148                   <argument name="c" />
00149                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00150                   if played during the greeting. Context defaults to the current context.</para>
00151                </option>
00152                <option name="g">
00153                   <argument name="#" required="true" />
00154                   <para>Use the specified amount of gain when recording the voicemail
00155                   message. The units are whole-number decibels (dB). Only works on supported
00156                   technologies, which is DAHDI only.</para>
00157                </option>
00158                <option name="s">
00159                   <para>Skip the playback of instructions for leaving a message to the
00160                   calling party.</para>
00161                </option>
00162                <option name="u">
00163                   <para>Play the <literal>unavailable</literal> greeting.</para>
00164                </option>
00165                <option name="U">
00166                   <para>Mark message as <literal>URGENT</literal>.</para>
00167                </option>
00168                <option name="P">
00169                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00170                </option>
00171             </optionlist>
00172          </parameter>
00173       </syntax>
00174       <description>
00175          <para>This application allows the calling party to leave a message for the specified
00176          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00177          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00178          exist.</para>
00179          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00180          <enumlist>
00181             <enum name="0">
00182                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00183             </enum>
00184             <enum name="*">
00185                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00186             </enum>
00187          </enumlist>
00188          <para>This application will set the following channel variable upon completion:</para>
00189          <variablelist>
00190             <variable name="VMSTATUS">
00191                <para>This indicates the status of the execution of the VoiceMail application.</para>
00192                <value name="SUCCESS" />
00193                <value name="USEREXIT" />
00194                <value name="FAILED" />
00195             </variable>
00196          </variablelist>
00197       </description>
00198    </application>
00199    <application name="VoiceMailMain" language="en_US">
00200       <synopsis>
00201          Check Voicemail messages.
00202       </synopsis>
00203       <syntax>
00204          <parameter name="mailbox" required="true" argsep="@">
00205             <argument name="mailbox" />
00206             <argument name="context" />
00207          </parameter>
00208          <parameter name="options">
00209             <optionlist>
00210                <option name="p">
00211                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00212                   the mailbox that is entered by the caller.</para>
00213                </option>
00214                <option name="g">
00215                   <argument name="#" required="true" />
00216                   <para>Use the specified amount of gain when recording a voicemail message.
00217                   The units are whole-number decibels (dB).</para>
00218                </option>
00219                <option name="s">
00220                   <para>Skip checking the passcode for the mailbox.</para>
00221                </option>
00222                <option name="a">
00223                   <argument name="folder" required="true" />
00224                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00225                   Defaults to <literal>0</literal> (INBOX).</para>
00226                   <enumlist>
00227                      <enum name="0"><para>INBOX</para></enum>
00228                      <enum name="1"><para>Old</para></enum>
00229                      <enum name="2"><para>Work</para></enum>
00230                      <enum name="3"><para>Family</para></enum>
00231                      <enum name="4"><para>Friends</para></enum>
00232                      <enum name="5"><para>Cust1</para></enum>
00233                      <enum name="6"><para>Cust2</para></enum>
00234                      <enum name="7"><para>Cust3</para></enum>
00235                      <enum name="8"><para>Cust4</para></enum>
00236                      <enum name="9"><para>Cust5</para></enum>
00237                   </enumlist>
00238                </option>
00239             </optionlist>
00240          </parameter>
00241       </syntax>
00242       <description>
00243          <para>This application allows the calling party to check voicemail messages. A specific
00244          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00245          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00246          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00247          <literal>default</literal> context will be used.</para>
00248       </description>
00249    </application>
00250    <application name="MailboxExists" language="en_US">
00251       <synopsis>
00252          Check to see if Voicemail mailbox exists.
00253       </synopsis>
00254       <syntax>
00255          <parameter name="mailbox" required="true" argsep="@">
00256             <argument name="mailbox" required="true" />
00257             <argument name="context" />
00258          </parameter>
00259          <parameter name="options">
00260             <para>None options.</para>
00261          </parameter>
00262       </syntax>
00263       <description>
00264          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00265          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00266          will be used.</para>
00267          <para>This application will set the following channel variable upon completion:</para>
00268          <variablelist>
00269             <variable name="VMBOXEXISTSSTATUS">
00270                <para>This will contain the status of the execution of the MailboxExists application.
00271                Possible values include:</para>
00272                <value name="SUCCESS" />
00273                <value name="FAILED" />
00274             </variable>
00275          </variablelist>
00276       </description>
00277    </application>
00278    <application name="VMAuthenticate" language="en_US">
00279       <synopsis>
00280          Authenticate with Voicemail passwords.
00281       </synopsis>
00282       <syntax>
00283          <parameter name="mailbox" required="true" argsep="@">
00284             <argument name="mailbox" />
00285             <argument name="context" />
00286          </parameter>
00287          <parameter name="options">
00288             <optionlist>
00289                <option name="s">
00290                   <para>Skip playing the initial prompts.</para>
00291                </option>
00292             </optionlist>
00293          </parameter>
00294       </syntax>
00295       <description>
00296          <para>This application behaves the same way as the Authenticate application, but the passwords
00297          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00298          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00299          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00300          mailbox.</para>
00301       </description>
00302    </application>
00303    <function name="MAILBOX_EXISTS" language="en_US">
00304       <synopsis>
00305          Tell if a mailbox is configured.
00306       </synopsis>
00307       <syntax argsep="@">
00308          <parameter name="mailbox" required="true" />
00309          <parameter name="context" />
00310       </syntax>
00311       <description>
00312          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00313          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00314          context.</para>
00315       </description>
00316    </function>
00317  ***/
00318 
00319 #ifdef IMAP_STORAGE
00320 static char imapserver[48];
00321 static char imapport[8];
00322 static char imapflags[128];
00323 static char imapfolder[64];
00324 static char imapparentfolder[64] = "\0";
00325 static char greetingfolder[64];
00326 static char authuser[32];
00327 static char authpassword[42];
00328 static int imapversion = 1;
00329 
00330 static int expungeonhangup = 1;
00331 static int imapgreetings = 0;
00332 static char delimiter = '\0';
00333 
00334 struct vm_state;
00335 struct ast_vm_user;
00336 
00337 AST_THREADSTORAGE(ts_vmstate);
00338 
00339 /* Forward declarations for IMAP */
00340 static int init_mailstream(struct vm_state *vms, int box);
00341 static void write_file(char *filename, char *buffer, unsigned long len);
00342 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00343 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00344 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00345 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00346 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00347 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00348 static void vmstate_insert(struct vm_state *vms);
00349 static void vmstate_delete(struct vm_state *vms);
00350 static void set_update(MAILSTREAM * stream);
00351 static void init_vm_state(struct vm_state *vms);
00352 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00353 static void get_mailbox_delimiter(MAILSTREAM *stream);
00354 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00355 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00356 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00357 static void update_messages_by_imapuser(const char *user, unsigned long number);
00358 static int vm_delete(char *file);
00359 
00360 static int imap_remove_file (char *dir, int msgnum);
00361 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00362 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00363 static void check_quota(struct vm_state *vms, char *mailbox);
00364 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00365 struct vmstate {
00366    struct vm_state *vms;
00367    AST_LIST_ENTRY(vmstate) list;
00368 };
00369 
00370 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00371 
00372 #endif
00373 
00374 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00375 
00376 #define COMMAND_TIMEOUT 5000
00377 /* Don't modify these here; set your umask at runtime instead */
00378 #define  VOICEMAIL_DIR_MODE   0777
00379 #define  VOICEMAIL_FILE_MODE  0666
00380 #define  CHUNKSIZE   65536
00381 
00382 #define VOICEMAIL_CONFIG "voicemail.conf"
00383 #define ASTERISK_USERNAME "asterisk"
00384 
00385 /* Define fast-forward, pause, restart, and reverse keys
00386    while listening to a voicemail message - these are
00387    strings, not characters */
00388 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00389 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00390 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00391 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00392 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00393 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00394 
00395 /* Default mail command to mail voicemail. Change it with the
00396     mailcmd= command in voicemail.conf */
00397 #define SENDMAIL "/usr/sbin/sendmail -t"
00398 
00399 #define INTRO "vm-intro"
00400 
00401 #define MAXMSG 100
00402 #define MAXMSGLIMIT 9999
00403 
00404 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00405 
00406 #define BASELINELEN 72
00407 #define BASEMAXINLINE 256
00408 #ifdef IMAP_STORAGE
00409 #define ENDL "\r\n"
00410 #else
00411 #define ENDL "\n"
00412 #endif
00413 
00414 #define MAX_DATETIME_FORMAT   512
00415 #define MAX_NUM_CID_CONTEXTS 10
00416 
00417 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00418 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00419 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00420 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00421 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00422 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00423 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00424 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00425 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00426 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00427 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00428 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00429 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00430 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00431 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00432 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00433 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00434 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00435 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00436 #define ERROR_LOCK_PATH  -100
00437 #define OPERATOR_EXIT     300
00438 
00439 
00440 enum {
00441    NEW_FOLDER,
00442    OLD_FOLDER,
00443    WORK_FOLDER,
00444    FAMILY_FOLDER,
00445    FRIENDS_FOLDER,
00446    GREETINGS_FOLDER
00447 } vm_box;
00448 
00449 enum {
00450    OPT_SILENT =           (1 << 0),
00451    OPT_BUSY_GREETING =    (1 << 1),
00452    OPT_UNAVAIL_GREETING = (1 << 2),
00453    OPT_RECORDGAIN =       (1 << 3),
00454    OPT_PREPEND_MAILBOX =  (1 << 4),
00455    OPT_AUTOPLAY =         (1 << 6),
00456    OPT_DTMFEXIT =         (1 << 7),
00457    OPT_MESSAGE_Urgent =   (1 << 8),
00458    OPT_MESSAGE_PRIORITY = (1 << 9)
00459 } vm_option_flags;
00460 
00461 enum {
00462    OPT_ARG_RECORDGAIN = 0,
00463    OPT_ARG_PLAYFOLDER = 1,
00464    OPT_ARG_DTMFEXIT   = 2,
00465    /* This *must* be the last value in this enum! */
00466    OPT_ARG_ARRAY_SIZE = 3,
00467 } vm_option_args;
00468 
00469 AST_APP_OPTIONS(vm_app_options, {
00470    AST_APP_OPTION('s', OPT_SILENT),
00471    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00472    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00473    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00474    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00475    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00476    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00477    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00478    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00479 });
00480 
00481 static int load_config(int reload);
00482 
00483 /*! \page vmlang Voicemail Language Syntaxes Supported
00484 
00485    \par Syntaxes supported, not really language codes.
00486    \arg \b en    - English
00487    \arg \b de    - German
00488    \arg \b es    - Spanish
00489    \arg \b fr    - French
00490    \arg \b it    - Italian
00491    \arg \b nl    - Dutch
00492    \arg \b pt    - Portuguese
00493    \arg \b pt_BR - Portuguese (Brazil)
00494    \arg \b gr    - Greek
00495    \arg \b no    - Norwegian
00496    \arg \b se    - Swedish
00497    \arg \b tw    - Chinese (Taiwan)
00498    \arg \b ua - Ukrainian
00499 
00500 German requires the following additional soundfile:
00501 \arg \b 1F  einE (feminine)
00502 
00503 Spanish requires the following additional soundfile:
00504 \arg \b 1M      un (masculine)
00505 
00506 Dutch, Portuguese & Spanish require the following additional soundfiles:
00507 \arg \b vm-INBOXs singular of 'new'
00508 \arg \b vm-Olds      singular of 'old/heard/read'
00509 
00510 NB these are plural:
00511 \arg \b vm-INBOX  nieuwe (nl)
00512 \arg \b vm-Old    oude (nl)
00513 
00514 Polish uses:
00515 \arg \b vm-new-a  'new', feminine singular accusative
00516 \arg \b vm-new-e  'new', feminine plural accusative
00517 \arg \b vm-new-ych   'new', feminine plural genitive
00518 \arg \b vm-old-a  'old', feminine singular accusative
00519 \arg \b vm-old-e  'old', feminine plural accusative
00520 \arg \b vm-old-ych   'old', feminine plural genitive
00521 \arg \b digits/1-a   'one', not always same as 'digits/1'
00522 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00523 
00524 Swedish uses:
00525 \arg \b vm-nytt      singular of 'new'
00526 \arg \b vm-nya    plural of 'new'
00527 \arg \b vm-gammalt   singular of 'old'
00528 \arg \b vm-gamla  plural of 'old'
00529 \arg \b digits/ett   'one', not always same as 'digits/1'
00530 
00531 Norwegian uses:
00532 \arg \b vm-ny     singular of 'new'
00533 \arg \b vm-nye    plural of 'new'
00534 \arg \b vm-gammel singular of 'old'
00535 \arg \b vm-gamle  plural of 'old'
00536 
00537 Dutch also uses:
00538 \arg \b nl-om     'at'?
00539 
00540 Spanish also uses:
00541 \arg \b vm-youhaveno
00542 
00543 Italian requires the following additional soundfile:
00544 
00545 For vm_intro_it:
00546 \arg \b vm-nuovo  new
00547 \arg \b vm-nuovi  new plural
00548 \arg \b vm-vecchio   old
00549 \arg \b vm-vecchi old plural
00550 
00551 Chinese (Taiwan) requires the following additional soundfile:
00552 \arg \b vm-tong      A class-word for call (tong1)
00553 \arg \b vm-ri     A class-word for day (ri4)
00554 \arg \b vm-you    You (ni3)
00555 \arg \b vm-haveno   Have no (mei2 you3)
00556 \arg \b vm-have     Have (you3)
00557 \arg \b vm-listen   To listen (yao4 ting1)
00558 
00559 
00560 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00561 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00562 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00563 
00564 */
00565 
00566 struct baseio {
00567    int iocp;
00568    int iolen;
00569    int linelength;
00570    int ateof;
00571    unsigned char iobuf[BASEMAXINLINE];
00572 };
00573 
00574 /*! Structure for linked list of users 
00575  * Use ast_vm_user_destroy() to free one of these structures. */
00576 struct ast_vm_user {
00577    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00578    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00579    char password[80];               /*!< Secret pin code, numbers only */
00580    char fullname[80];               /*!< Full name, for directory app */
00581    char email[80];                  /*!< E-mail address */
00582    char *emailsubject;              /*!< E-mail subject */
00583    char *emailbody;                 /*!< E-mail body */
00584    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00585    char serveremail[80];            /*!< From: Mail address */
00586    char mailcmd[160];               /*!< Configurable mail command */
00587    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00588    char zonetag[80];                /*!< Time zone */
00589    char callback[80];
00590    char dialout[80];
00591    char uniqueid[80];               /*!< Unique integer identifier */
00592    char exit[80];
00593    char attachfmt[20];              /*!< Attachment format */
00594    unsigned int flags;              /*!< VM_ flags */ 
00595    int saydurationm;
00596    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00597    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00598    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00599 #ifdef IMAP_STORAGE
00600    char imapuser[80];               /*!< IMAP server login */
00601    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00602    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00603    int imapversion;                 /*!< If configuration changes, use the new values */
00604 #endif
00605    double volgain;                  /*!< Volume gain for voicemails sent via email */
00606    AST_LIST_ENTRY(ast_vm_user) list;
00607 };
00608 
00609 /*! Voicemail time zones */
00610 struct vm_zone {
00611    AST_LIST_ENTRY(vm_zone) list;
00612    char name[80];
00613    char timezone[80];
00614    char msg_format[512];
00615 };
00616 
00617 #define VMSTATE_MAX_MSG_ARRAY 256
00618 
00619 /*! Voicemail mailbox state */
00620 struct vm_state {
00621    char curbox[80];
00622    char username[80];
00623    char context[80];
00624    char curdir[PATH_MAX];
00625    char vmbox[PATH_MAX];
00626    char fn[PATH_MAX];
00627    char intro[PATH_MAX];
00628    int *deleted;
00629    int *heard;
00630    int dh_arraysize; /* used for deleted / heard allocation */
00631    int curmsg;
00632    int lastmsg;
00633    int newmessages;
00634    int oldmessages;
00635    int urgentmessages;
00636    int starting;
00637    int repeats;
00638 #ifdef IMAP_STORAGE
00639    ast_mutex_t lock;
00640    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00641    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00642    MAILSTREAM *mailstream;
00643    int vmArrayIndex;
00644    char imapuser[80];                   /*!< IMAP server login */
00645    int imapversion;
00646    int interactive;
00647    char introfn[PATH_MAX];              /*!< Name of prepended file */
00648    unsigned int quota_limit;
00649    unsigned int quota_usage;
00650    struct vm_state *persist_vms;
00651 #endif
00652 };
00653 
00654 #ifdef ODBC_STORAGE
00655 static char odbc_database[80];
00656 static char odbc_table[80];
00657 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00658 #define DISPOSE(a,b) remove_file(a,b)
00659 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00660 #define EXISTS(a,b,c,d) (message_exists(a,b))
00661 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00662 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00663 #define DELETE(a,b,c,d) (delete_file(a,b))
00664 #else
00665 #ifdef IMAP_STORAGE
00666 #define DISPOSE(a,b) (imap_remove_file(a,b))
00667 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
00668 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00669 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00670 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00671 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00672 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00673 #else
00674 #define RETRIEVE(a,b,c,d)
00675 #define DISPOSE(a,b)
00676 #define STORE(a,b,c,d,e,f,g,h,i,j)
00677 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00678 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00679 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00680 #define DELETE(a,b,c,d) (vm_delete(c))
00681 #endif
00682 #endif
00683 
00684 static char VM_SPOOL_DIR[PATH_MAX];
00685 
00686 static char ext_pass_cmd[128];
00687 static char ext_pass_check_cmd[128];
00688 
00689 static int my_umask;
00690 
00691 #define PWDCHANGE_INTERNAL (1 << 1)
00692 #define PWDCHANGE_EXTERNAL (1 << 2)
00693 static int pwdchange = PWDCHANGE_INTERNAL;
00694 
00695 #ifdef ODBC_STORAGE
00696 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00697 #else
00698 # ifdef IMAP_STORAGE
00699 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00700 # else
00701 # define tdesc "Comedian Mail (Voicemail System)"
00702 # endif
00703 #endif
00704 
00705 static char userscontext[AST_MAX_EXTENSION] = "default";
00706 
00707 static char *addesc = "Comedian Mail";
00708 
00709 /* Leave a message */
00710 static char *app = "VoiceMail";
00711 
00712 /* Check mail, control, etc */
00713 static char *app2 = "VoiceMailMain";
00714 
00715 static char *app3 = "MailboxExists";
00716 static char *app4 = "VMAuthenticate";
00717 
00718 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00719 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00720 static char zonetag[80];
00721 static int maxsilence;
00722 static int maxmsg;
00723 static int maxdeletedmsg;
00724 static int silencethreshold = 128;
00725 static char serveremail[80];
00726 static char mailcmd[160];  /* Configurable mail cmd */
00727 static char externnotify[160]; 
00728 static struct ast_smdi_interface *smdi_iface = NULL;
00729 static char vmfmts[80];
00730 static double volgain;
00731 static int vmminsecs;
00732 static int vmmaxsecs;
00733 static int maxgreet;
00734 static int skipms;
00735 static int maxlogins;
00736 static int minpassword;
00737 
00738 /*! Poll mailboxes for changes since there is something external to
00739  *  app_voicemail that may change them. */
00740 static unsigned int poll_mailboxes;
00741 
00742 /*! Polling frequency */
00743 static unsigned int poll_freq;
00744 /*! By default, poll every 30 seconds */
00745 #define DEFAULT_POLL_FREQ 30
00746 
00747 AST_MUTEX_DEFINE_STATIC(poll_lock);
00748 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00749 static pthread_t poll_thread = AST_PTHREADT_NULL;
00750 static unsigned char poll_thread_run;
00751 
00752 /*! Subscription to ... MWI event subscriptions */
00753 static struct ast_event_sub *mwi_sub_sub;
00754 /*! Subscription to ... MWI event un-subscriptions */
00755 static struct ast_event_sub *mwi_unsub_sub;
00756 
00757 /*!
00758  * \brief An MWI subscription
00759  *
00760  * This is so we can keep track of which mailboxes are subscribed to.
00761  * This way, we know which mailboxes to poll when the pollmailboxes
00762  * option is being used.
00763  */
00764 struct mwi_sub {
00765    AST_RWLIST_ENTRY(mwi_sub) entry;
00766    int old_urgent;
00767    int old_new;
00768    int old_old;
00769    uint32_t uniqueid;
00770    char mailbox[1];
00771 };
00772 
00773 struct mwi_sub_task {
00774    const char *mailbox;
00775    const char *context;
00776    uint32_t uniqueid;
00777 };
00778 
00779 static struct ast_taskprocessor *mwi_subscription_tps;
00780 
00781 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00782 
00783 /* custom audio control prompts for voicemail playback */
00784 static char listen_control_forward_key[12];
00785 static char listen_control_reverse_key[12];
00786 static char listen_control_pause_key[12];
00787 static char listen_control_restart_key[12];
00788 static char listen_control_stop_key[12];
00789 
00790 /* custom password sounds */
00791 static char vm_password[80] = "vm-password";
00792 static char vm_newpassword[80] = "vm-newpassword";
00793 static char vm_passchanged[80] = "vm-passchanged";
00794 static char vm_reenterpassword[80] = "vm-reenterpassword";
00795 static char vm_mismatch[80] = "vm-mismatch";
00796 static char vm_invalid_password[80] = "vm-invalid-password";
00797 static char vm_pls_try_again[80] = "vm-pls-try-again";
00798 
00799 static struct ast_flags globalflags = {0};
00800 
00801 static int saydurationminfo;
00802 
00803 static char dialcontext[AST_MAX_CONTEXT] = "";
00804 static char callcontext[AST_MAX_CONTEXT] = "";
00805 static char exitcontext[AST_MAX_CONTEXT] = "";
00806 
00807 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00808 
00809 
00810 static char *emailbody = NULL;
00811 static char *emailsubject = NULL;
00812 static char *pagerbody = NULL;
00813 static char *pagersubject = NULL;
00814 static char fromstring[100];
00815 static char pagerfromstring[100];
00816 static char charset[32] = "ISO-8859-1";
00817 
00818 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00819 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00820 static int adsiver = 1;
00821 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00822 
00823 /* Forward declarations - generic */
00824 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00825 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
00826 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00827 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00828          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00829          signed char record_gain, struct vm_state *vms, char *flag);
00830 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00831 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00832 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
00833 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
00834 static void apply_options(struct ast_vm_user *vmu, const char *options);
00835 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
00836 static int is_valid_dtmf(const char *key);
00837 
00838 struct ao2_container *inprocess_container;
00839 
00840 struct inprocess {
00841    int count;
00842    char *context;
00843    char mailbox[0];
00844 };
00845 
00846 static int inprocess_hash_fn(const void *obj, const int flags)
00847 {
00848    const struct inprocess *i = obj;
00849    return atoi(i->mailbox);
00850 }
00851 
00852 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00853 {
00854    struct inprocess *i = obj, *j = arg;
00855    if (strcmp(i->mailbox, j->mailbox)) {
00856       return 0;
00857    }
00858    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00859 }
00860 
00861 static int inprocess_count(const char *context, const char *mailbox, int delta)
00862 {
00863    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00864    arg->context = arg->mailbox + strlen(mailbox) + 1;
00865    strcpy(arg->mailbox, mailbox); /* SAFE */
00866    strcpy(arg->context, context); /* SAFE */
00867    ao2_lock(inprocess_container);
00868    if ((i = ao2_find(inprocess_container, arg, 0))) {
00869       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00870       ao2_unlock(inprocess_container);
00871       ao2_ref(i, -1);
00872       return ret;
00873    }
00874    if (delta < 0) {
00875       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00876    }
00877    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00878       ao2_unlock(inprocess_container);
00879       return 0;
00880    }
00881    i->context = i->mailbox + strlen(mailbox) + 1;
00882    strcpy(i->mailbox, mailbox); /* SAFE */
00883    strcpy(i->context, context); /* SAFE */
00884    i->count = delta;
00885    ao2_link(inprocess_container, i);
00886    ao2_unlock(inprocess_container);
00887    ao2_ref(i, -1);
00888    return 0;
00889 }
00890 
00891 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00892 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00893 #endif
00894 
00895 /*!
00896  * \brief Strips control and non 7-bit clean characters from input string.
00897  *
00898  * \note To map control and none 7-bit characters to a 7-bit clean characters
00899  *  please use ast_str_encode_mine().
00900  */
00901 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00902 {
00903    char *bufptr = buf;
00904    for (; *input; input++) {
00905       if (*input < 32) {
00906          continue;
00907       }
00908       *bufptr++ = *input;
00909       if (bufptr == buf + buflen - 1) {
00910          break;
00911       }
00912    }
00913    *bufptr = '\0';
00914    return buf;
00915 }
00916 
00917 
00918 /*!
00919  * \brief Sets default voicemail system options to a voicemail user.
00920  *
00921  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00922  * - all the globalflags
00923  * - the saydurationminfo
00924  * - the callcontext
00925  * - the dialcontext
00926  * - the exitcontext
00927  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00928  * - volume gain.
00929  */
00930 static void populate_defaults(struct ast_vm_user *vmu)
00931 {
00932    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00933    if (saydurationminfo)
00934       vmu->saydurationm = saydurationminfo;
00935    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00936    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00937    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00938    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00939    if (vmmaxsecs)
00940       vmu->maxsecs = vmmaxsecs;
00941    if (maxmsg)
00942       vmu->maxmsg = maxmsg;
00943    if (maxdeletedmsg)
00944       vmu->maxdeletedmsg = maxdeletedmsg;
00945    vmu->volgain = volgain;
00946    vmu->emailsubject = NULL;
00947    vmu->emailbody = NULL;
00948 }
00949 
00950 /*!
00951  * \brief Sets a a specific property value.
00952  * \param vmu The voicemail user object to work with.
00953  * \param var The name of the property to be set.
00954  * \param value The value to be set to the property.
00955  * 
00956  * The property name must be one of the understood properties. See the source for details.
00957  */
00958 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00959 {
00960    int x;
00961    if (!strcasecmp(var, "attach")) {
00962       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00963    } else if (!strcasecmp(var, "attachfmt")) {
00964       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00965    } else if (!strcasecmp(var, "serveremail")) {
00966       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00967    } else if (!strcasecmp(var, "language")) {
00968       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00969    } else if (!strcasecmp(var, "tz")) {
00970       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00971 #ifdef IMAP_STORAGE
00972    } else if (!strcasecmp(var, "imapuser")) {
00973       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00974       vmu->imapversion = imapversion;
00975    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00976       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00977       vmu->imapversion = imapversion;
00978    } else if (!strcasecmp(var, "imapvmshareid")) {
00979       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00980       vmu->imapversion = imapversion;
00981 #endif
00982    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00983       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00984    } else if (!strcasecmp(var, "saycid")){
00985       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00986    } else if (!strcasecmp(var,"sendvoicemail")){
00987       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00988    } else if (!strcasecmp(var, "review")){
00989       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00990    } else if (!strcasecmp(var, "tempgreetwarn")){
00991       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00992    } else if (!strcasecmp(var, "messagewrap")){
00993       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00994    } else if (!strcasecmp(var, "operator")) {
00995       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00996    } else if (!strcasecmp(var, "envelope")){
00997       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00998    } else if (!strcasecmp(var, "moveheard")){
00999       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01000    } else if (!strcasecmp(var, "sayduration")){
01001       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01002    } else if (!strcasecmp(var, "saydurationm")){
01003       if (sscanf(value, "%30d", &x) == 1) {
01004          vmu->saydurationm = x;
01005       } else {
01006          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01007       }
01008    } else if (!strcasecmp(var, "forcename")){
01009       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01010    } else if (!strcasecmp(var, "forcegreetings")){
01011       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01012    } else if (!strcasecmp(var, "callback")) {
01013       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01014    } else if (!strcasecmp(var, "dialout")) {
01015       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01016    } else if (!strcasecmp(var, "exitcontext")) {
01017       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01018    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01019       vmu->maxsecs = atoi(value);
01020       if (vmu->maxsecs <= 0) {
01021          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01022          vmu->maxsecs = vmmaxsecs;
01023       } else {
01024          vmu->maxsecs = atoi(value);
01025       }
01026       if (!strcasecmp(var, "maxmessage"))
01027          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01028    } else if (!strcasecmp(var, "maxmsg")) {
01029       vmu->maxmsg = atoi(value);
01030       if (vmu->maxmsg <= 0) {
01031          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01032          vmu->maxmsg = MAXMSG;
01033       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01034          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01035          vmu->maxmsg = MAXMSGLIMIT;
01036       }
01037    } else if (!strcasecmp(var, "backupdeleted")) {
01038       if (sscanf(value, "%30d", &x) == 1)
01039          vmu->maxdeletedmsg = x;
01040       else if (ast_true(value))
01041          vmu->maxdeletedmsg = MAXMSG;
01042       else
01043          vmu->maxdeletedmsg = 0;
01044 
01045       if (vmu->maxdeletedmsg < 0) {
01046          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01047          vmu->maxdeletedmsg = MAXMSG;
01048       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01049          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01050          vmu->maxdeletedmsg = MAXMSGLIMIT;
01051       }
01052    } else if (!strcasecmp(var, "volgain")) {
01053       sscanf(value, "%30lf", &vmu->volgain);
01054    } else if (!strcasecmp(var, "options")) {
01055       apply_options(vmu, value);
01056    }
01057 }
01058 
01059 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01060 {
01061    int fds[2], pid = 0;
01062 
01063    memset(buf, 0, len);
01064 
01065    if (pipe(fds)) {
01066       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01067    } else {
01068       /* good to go*/
01069       pid = ast_safe_fork(0);
01070 
01071       if (pid < 0) {
01072          /* ok maybe not */
01073          close(fds[0]);
01074          close(fds[1]);
01075          snprintf(buf, len, "FAILURE: Fork failed");
01076       } else if (pid) {
01077          /* parent */
01078          close(fds[1]);
01079          if (read(fds[0], buf, len) < 0) {
01080             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01081          }
01082          close(fds[0]);
01083       } else {
01084          /*  child */
01085          AST_DECLARE_APP_ARGS(arg,
01086             AST_APP_ARG(v)[20];
01087          );
01088          char *mycmd = ast_strdupa(command);
01089 
01090          close(fds[0]);
01091          dup2(fds[1], STDOUT_FILENO);
01092          close(fds[1]);
01093          ast_close_fds_above_n(STDOUT_FILENO);
01094 
01095          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01096 
01097          execv(arg.v[0], arg.v); 
01098          printf("FAILURE: %s", strerror(errno));
01099          _exit(0);
01100       }
01101    }
01102    return buf;
01103 }
01104 
01105 /*!
01106  * \brief Check that password meets minimum required length
01107  * \param vmu The voicemail user to change the password for.
01108  * \param password The password string to check
01109  *
01110  * \return zero on ok, 1 on not ok.
01111  */
01112 static int check_password(struct ast_vm_user *vmu, char *password)
01113 {
01114    /* check minimum length */
01115    if (strlen(password) < minpassword)
01116       return 1;
01117    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01118       char cmd[255], buf[255];
01119 
01120       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01121 
01122       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01123       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01124          ast_debug(5, "Result: %s\n", buf);
01125          if (!strncasecmp(buf, "VALID", 5)) {
01126             ast_debug(3, "Passed password check: '%s'\n", buf);
01127             return 0;
01128          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01129             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01130             return 0;
01131          } else {
01132             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01133             return 1;
01134          }
01135       }
01136    }
01137    return 0;
01138 }
01139 
01140 /*! 
01141  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01142  * \param vmu The voicemail user to change the password for.
01143  * \param password The new value to be set to the password for this user.
01144  * 
01145  * This only works if there is a realtime engine configured.
01146  * This is called from the (top level) vm_change_password.
01147  *
01148  * \return zero on success, -1 on error.
01149  */
01150 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01151 {
01152    int res = -1;
01153    if (!strcmp(vmu->password, password)) {
01154       /* No change (but an update would return 0 rows updated, so we opt out here) */
01155       return 0;
01156    }
01157 
01158    if (strlen(password) > 10) {
01159       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01160    }
01161    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01162       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01163       res = 0;
01164    }
01165    return res;
01166 }
01167 
01168 /*!
01169  * \brief Destructively Parse options and apply.
01170  */
01171 static void apply_options(struct ast_vm_user *vmu, const char *options)
01172 {  
01173    char *stringp;
01174    char *s;
01175    char *var, *value;
01176    stringp = ast_strdupa(options);
01177    while ((s = strsep(&stringp, "|"))) {
01178       value = s;
01179       if ((var = strsep(&value, "=")) && value) {
01180          apply_option(vmu, var, value);
01181       }
01182    }  
01183 }
01184 
01185 /*!
01186  * \brief Loads the options specific to a voicemail user.
01187  * 
01188  * This is called when a vm_user structure is being set up, such as from load_options.
01189  */
01190 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01191 {
01192    for (; var; var = var->next) {
01193       if (!strcasecmp(var->name, "vmsecret")) {
01194          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01195       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01196          if (ast_strlen_zero(retval->password))
01197             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01198       } else if (!strcasecmp(var->name, "uniqueid")) {
01199          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01200       } else if (!strcasecmp(var->name, "pager")) {
01201          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01202       } else if (!strcasecmp(var->name, "email")) {
01203          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01204       } else if (!strcasecmp(var->name, "fullname")) {
01205          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01206       } else if (!strcasecmp(var->name, "context")) {
01207          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01208       } else if (!strcasecmp(var->name, "emailsubject")) {
01209          retval->emailsubject = ast_strdup(var->value);
01210       } else if (!strcasecmp(var->name, "emailbody")) {
01211          retval->emailbody = ast_strdup(var->value);
01212 #ifdef IMAP_STORAGE
01213       } else if (!strcasecmp(var->name, "imapuser")) {
01214          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01215          retval->imapversion = imapversion;
01216       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01217          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01218          retval->imapversion = imapversion;
01219       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01220          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01221          retval->imapversion = imapversion;
01222 #endif
01223       } else
01224          apply_option(retval, var->name, var->value);
01225    }
01226 }
01227 
01228 /*!
01229  * \brief Determines if a DTMF key entered is valid.
01230  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
01231  *
01232  * Tests the character entered against the set of valid DTMF characters. 
01233  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01234  */
01235 static int is_valid_dtmf(const char *key)
01236 {
01237    int i;
01238    char *local_key = ast_strdupa(key);
01239 
01240    for (i = 0; i < strlen(key); ++i) {
01241       if (!strchr(VALID_DTMF, *local_key)) {
01242          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01243          return 0;
01244       }
01245       local_key++;
01246    }
01247    return 1;
01248 }
01249 
01250 /*!
01251  * \brief Finds a voicemail user from the realtime engine.
01252  * \param ivm
01253  * \param context
01254  * \param mailbox
01255  *
01256  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
01257  *
01258  * \return The ast_vm_user structure for the user that was found.
01259  */
01260 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01261 {
01262    struct ast_variable *var;
01263    struct ast_vm_user *retval;
01264 
01265    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01266       if (!ivm)
01267          ast_set_flag(retval, VM_ALLOCED);   
01268       else
01269          memset(retval, 0, sizeof(*retval));
01270       if (mailbox) 
01271          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01272       populate_defaults(retval);
01273       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01274          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01275       else
01276          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01277       if (var) {
01278          apply_options_full(retval, var);
01279          ast_variables_destroy(var);
01280       } else { 
01281          if (!ivm) 
01282             ast_free(retval);
01283          retval = NULL;
01284       }  
01285    } 
01286    return retval;
01287 }
01288 
01289 /*!
01290  * \brief Finds a voicemail user from the users file or the realtime engine.
01291  * \param ivm
01292  * \param context
01293  * \param mailbox
01294  * 
01295  * \return The ast_vm_user structure for the user that was found.
01296  */
01297 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01298 {
01299    /* This function could be made to generate one from a database, too */
01300    struct ast_vm_user *vmu=NULL, *cur;
01301    AST_LIST_LOCK(&users);
01302 
01303    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01304       context = "default";
01305 
01306    AST_LIST_TRAVERSE(&users, cur, list) {
01307 #ifdef IMAP_STORAGE
01308       if (cur->imapversion != imapversion) {
01309          continue;
01310       }
01311 #endif
01312       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01313          break;
01314       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01315          break;
01316    }
01317    if (cur) {
01318       /* Make a copy, so that on a reload, we have no race */
01319       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01320          memcpy(vmu, cur, sizeof(*vmu));
01321          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01322          AST_LIST_NEXT(vmu, list) = NULL;
01323       }
01324    } else
01325       vmu = find_user_realtime(ivm, context, mailbox);
01326    AST_LIST_UNLOCK(&users);
01327    return vmu;
01328 }
01329 
01330 /*!
01331  * \brief Resets a user password to a specified password.
01332  * \param context
01333  * \param mailbox
01334  * \param newpass
01335  *
01336  * This does the actual change password work, called by the vm_change_password() function.
01337  *
01338  * \return zero on success, -1 on error.
01339  */
01340 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01341 {
01342    /* This function could be made to generate one from a database, too */
01343    struct ast_vm_user *cur;
01344    int res = -1;
01345    AST_LIST_LOCK(&users);
01346    AST_LIST_TRAVERSE(&users, cur, list) {
01347       if ((!context || !strcasecmp(context, cur->context)) &&
01348          (!strcasecmp(mailbox, cur->mailbox)))
01349             break;
01350    }
01351    if (cur) {
01352       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01353       res = 0;
01354    }
01355    AST_LIST_UNLOCK(&users);
01356    return res;
01357 }
01358 
01359 /*! 
01360  * \brief The handler for the change password option.
01361  * \param vmu The voicemail user to work with.
01362  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01363  * This is called when a new user logs in for the first time and the option to force them to change their password is set.
01364  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01365  */
01366 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01367 {
01368    struct ast_config   *cfg=NULL;
01369    struct ast_variable *var=NULL;
01370    struct ast_category *cat=NULL;
01371    char *category=NULL, *value=NULL, *new=NULL;
01372    const char *tmp=NULL;
01373    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01374    if (!change_password_realtime(vmu, newpassword))
01375       return;
01376 
01377    /* check voicemail.conf */
01378    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01379       while ((category = ast_category_browse(cfg, category))) {
01380          if (!strcasecmp(category, vmu->context)) {
01381             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01382                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01383                break;
01384             }
01385             value = strstr(tmp,",");
01386             if (!value) {
01387                new = alloca(strlen(newpassword)+1);
01388                sprintf(new, "%s", newpassword);
01389             } else {
01390                new = alloca((strlen(value)+strlen(newpassword)+1));
01391                sprintf(new,"%s%s", newpassword, value);
01392             }
01393             if (!(cat = ast_category_get(cfg, category))) {
01394                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01395                break;
01396             }
01397             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01398          }
01399       }
01400       /* save the results */
01401       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01402       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01403       ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01404    }
01405    category = NULL;
01406    var = NULL;
01407    /* check users.conf and update the password stored for the mailbox*/
01408    /* if no vmsecret entry exists create one. */
01409    if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01410       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01411       while ((category = ast_category_browse(cfg, category))) {
01412          ast_debug(4, "users.conf: %s\n", category);
01413          if (!strcasecmp(category, vmu->mailbox)) {
01414             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01415                ast_debug(3, "looks like we need to make vmsecret!\n");
01416                var = ast_variable_new("vmsecret", newpassword, "");
01417             } 
01418             new = alloca(strlen(newpassword)+1);
01419             sprintf(new, "%s", newpassword);
01420             if (!(cat = ast_category_get(cfg, category))) {
01421                ast_debug(4, "failed to get category!\n");
01422                break;
01423             }
01424             if (!var)      
01425                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01426             else
01427                ast_variable_append(cat, var);
01428          }
01429       }
01430       /* save the results and clean things up */
01431       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01432       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01433       ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01434    }
01435 }
01436 
01437 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01438 {
01439    char buf[255];
01440    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01441    if (!ast_safe_system(buf)) {
01442       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01443       /* Reset the password in memory, too */
01444       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01445    }
01446 }
01447 
01448 /*! 
01449  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01450  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01451  * \param len The length of the path string that was written out.
01452  * 
01453  * The path is constructed as 
01454  *    VM_SPOOL_DIRcontext/ext/folder
01455  *
01456  * \return zero on success, -1 on error.
01457  */
01458 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01459 {
01460    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01461 }
01462 
01463 /*! 
01464  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01465  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01466  * \param len The length of the path string that was written out.
01467  * 
01468  * The path is constructed as 
01469  *    VM_SPOOL_DIRcontext/ext/folder
01470  *
01471  * \return zero on success, -1 on error.
01472  */
01473 static int make_file(char *dest, const int len, const char *dir, const int num)
01474 {
01475    return snprintf(dest, len, "%s/msg%04d", dir, num);
01476 }
01477 
01478 /* same as mkstemp, but return a FILE * */
01479 static FILE *vm_mkftemp(char *template)
01480 {
01481    FILE *p = NULL;
01482    int pfd = mkstemp(template);
01483    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01484    if (pfd > -1) {
01485       p = fdopen(pfd, "w+");
01486       if (!p) {
01487          close(pfd);
01488          pfd = -1;
01489       }
01490    }
01491    return p;
01492 }
01493 
01494 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01495  * \param dest    String. base directory.
01496  * \param len     Length of dest.
01497  * \param context String. Ignored if is null or empty string.
01498  * \param ext     String. Ignored if is null or empty string.
01499  * \param folder  String. Ignored if is null or empty string. 
01500  * \return -1 on failure, 0 on success.
01501  */
01502 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01503 {
01504    mode_t   mode = VOICEMAIL_DIR_MODE;
01505    int res;
01506 
01507    make_dir(dest, len, context, ext, folder);
01508    if ((res = ast_mkdir(dest, mode))) {
01509       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01510       return -1;
01511    }
01512    return 0;
01513 }
01514 
01515 static const char *mbox(int id)
01516 {
01517    static const char *msgs[] = {
01518 #ifdef IMAP_STORAGE
01519       imapfolder,
01520 #else
01521       "INBOX",
01522 #endif
01523       "Old",
01524       "Work",
01525       "Family",
01526       "Friends",
01527       "Cust1",
01528       "Cust2",
01529       "Cust3",
01530       "Cust4",
01531       "Cust5",
01532       "Deleted",
01533       "Urgent"
01534    };
01535    return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
01536 }
01537 
01538 static void free_user(struct ast_vm_user *vmu)
01539 {
01540    if (ast_test_flag(vmu, VM_ALLOCED)) {
01541       if (vmu->emailbody != NULL) {
01542          ast_free(vmu->emailbody);
01543          vmu->emailbody = NULL;
01544       }
01545       if (vmu->emailsubject != NULL) {
01546          ast_free(vmu->emailsubject);
01547          vmu->emailsubject = NULL;
01548       }
01549       ast_free(vmu);
01550    }
01551 }
01552 
01553 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01554 
01555    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01556    if (!vms->dh_arraysize) {
01557       /* initial allocation */
01558       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01559          return -1;
01560       }
01561       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01562          return -1;
01563       }
01564       vms->dh_arraysize = arraysize;
01565    } else if (vms->dh_arraysize < arraysize) {
01566       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01567          return -1;
01568       }
01569       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01570          return -1;
01571       }
01572       memset(vms->deleted, 0, arraysize * sizeof(int));
01573       memset(vms->heard, 0, arraysize * sizeof(int));
01574       vms->dh_arraysize = arraysize;
01575    }
01576 
01577    return 0;
01578 }
01579 
01580 /* All IMAP-specific functions should go in this block. This
01581  * keeps them from being spread out all over the code */
01582 #ifdef IMAP_STORAGE
01583 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01584 {
01585    char arg[10];
01586    struct vm_state *vms;
01587    unsigned long messageNum;
01588 
01589    /* If greetings aren't stored in IMAP, just delete the file */
01590    if (msgnum < 0 && !imapgreetings) {
01591       ast_filedelete(file, NULL);
01592       return;
01593    }
01594 
01595    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01596       ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
01597       return;
01598    }
01599 
01600    /* find real message number based on msgnum */
01601    /* this may be an index into vms->msgArray based on the msgnum. */
01602    messageNum = vms->msgArray[msgnum];
01603    if (messageNum == 0) {
01604       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01605       return;
01606    }
01607    if (option_debug > 2)
01608       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01609    /* delete message */
01610    snprintf (arg, sizeof(arg), "%lu",messageNum);
01611    ast_mutex_lock(&vms->lock);
01612    mail_setflag (vms->mailstream,arg,"\\DELETED");
01613    mail_expunge(vms->mailstream);
01614    ast_mutex_unlock(&vms->lock);
01615 }
01616 
01617 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01618 {
01619    struct vm_state *vms_p;
01620    char *file, *filename;
01621    char *attachment;
01622    int ret = 0, i;
01623    BODY *body;
01624 
01625    /* This function is only used for retrieval of IMAP greetings
01626     * regular messages are not retrieved this way, nor are greetings
01627     * if they are stored locally*/
01628    if (msgnum > -1 || !imapgreetings) {
01629       return 0;
01630    } else {
01631       file = strrchr(ast_strdupa(dir), '/');
01632       if (file)
01633          *file++ = '\0';
01634       else {
01635          ast_debug (1, "Failed to procure file name from directory passed.\n");
01636          return -1;
01637       }
01638    }
01639 
01640    /* check if someone is accessing this box right now... */
01641    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01642       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01643       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01644       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01645       * that's all we need to do.
01646       */
01647       if (!(vms_p = create_vm_state_from_user(vmu))) {
01648          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01649          return -1;
01650       }
01651    }
01652    
01653    /* Greetings will never have a prepended message */
01654    *vms_p->introfn = '\0';
01655 
01656    ast_mutex_lock(&vms_p->lock);
01657    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01658    if (!vms_p->mailstream) {
01659       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01660       ast_mutex_unlock(&vms_p->lock);
01661       return -1;
01662    }
01663 
01664    /*XXX Yuck, this could probably be done a lot better */
01665    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01666       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01667       /* We have the body, now we extract the file name of the first attachment. */
01668       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01669          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01670       } else {
01671          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01672          ast_mutex_unlock(&vms_p->lock);
01673          return -1;
01674       }
01675       filename = strsep(&attachment, ".");
01676       if (!strcmp(filename, file)) {
01677          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01678          vms_p->msgArray[vms_p->curmsg] = i + 1;
01679          save_body(body, vms_p, "2", attachment, 0);
01680          ast_mutex_unlock(&vms_p->lock);
01681          return 0;
01682       }
01683    }
01684    ast_mutex_unlock(&vms_p->lock);
01685 
01686    return -1;
01687 }
01688 
01689 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01690 {
01691    BODY *body;
01692    char *header_content;
01693    char *attachedfilefmt;
01694    char buf[80];
01695    struct vm_state *vms;
01696    char text_file[PATH_MAX];
01697    FILE *text_file_ptr;
01698    int res = 0;
01699    struct ast_vm_user *vmu;
01700 
01701    if (!(vmu = find_user(NULL, context, mailbox))) {
01702       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01703       return -1;
01704    }
01705    
01706    if (msgnum < 0) {
01707       if (imapgreetings) {
01708          res = imap_retrieve_greeting(dir, msgnum, vmu);
01709          goto exit;
01710       } else {
01711          res = 0;
01712          goto exit;
01713       }
01714    }
01715 
01716    /* Before anything can happen, we need a vm_state so that we can
01717     * actually access the imap server through the vms->mailstream
01718     */
01719    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01720       /* This should not happen. If it does, then I guess we'd
01721        * need to create the vm_state, extract which mailbox to
01722        * open, and then set up the msgArray so that the correct
01723        * IMAP message could be accessed. If I have seen correctly
01724        * though, the vms should be obtainable from the vmstates list
01725        * and should have its msgArray properly set up.
01726        */
01727       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01728       res = -1;
01729       goto exit;
01730    }
01731    
01732    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01733    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01734 
01735    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01736    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01737       res = 0;
01738       goto exit;
01739    }
01740 
01741    if (option_debug > 2)
01742       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01743    if (vms->msgArray[msgnum] == 0) {
01744       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01745       res = -1;
01746       goto exit;
01747    }
01748 
01749    /* This will only work for new messages... */
01750    ast_mutex_lock(&vms->lock);
01751    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01752    ast_mutex_unlock(&vms->lock);
01753    /* empty string means no valid header */
01754    if (ast_strlen_zero(header_content)) {
01755       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01756       res = -1;
01757       goto exit;
01758    }
01759 
01760    ast_mutex_lock(&vms->lock);
01761    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01762    ast_mutex_unlock(&vms->lock);
01763 
01764    /* We have the body, now we extract the file name of the first attachment. */
01765    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01766       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01767    } else {
01768       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01769       res = -1;
01770       goto exit;
01771    }
01772    
01773    /* Find the format of the attached file */
01774 
01775    strsep(&attachedfilefmt, ".");
01776    if (!attachedfilefmt) {
01777       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01778       res = -1;
01779       goto exit;
01780    }
01781    
01782    save_body(body, vms, "2", attachedfilefmt, 0);
01783    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01784       *vms->introfn = '\0';
01785    }
01786 
01787    /* Get info from headers!! */
01788    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01789 
01790    if (!(text_file_ptr = fopen(text_file, "w"))) {
01791       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01792    }
01793 
01794    fprintf(text_file_ptr, "%s\n", "[message]");
01795 
01796    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01797    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01798    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01799    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01800    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01801    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01802    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01803    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01804    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01805    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01806    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01807    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01808    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01809    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01810    fclose(text_file_ptr);
01811 
01812 exit:
01813    free_user(vmu);
01814    return res;
01815 }
01816 
01817 static int folder_int(const char *folder)
01818 {
01819    /*assume a NULL folder means INBOX*/
01820    if (!folder) {
01821       return 0;
01822    }
01823    if (!strcasecmp(folder, imapfolder)) {
01824       return 0;
01825    } else if (!strcasecmp(folder, "Old")) {
01826       return 1;
01827    } else if (!strcasecmp(folder, "Work")) {
01828       return 2;
01829    } else if (!strcasecmp(folder, "Family")) {
01830       return 3;
01831    } else if (!strcasecmp(folder, "Friends")) {
01832       return 4;
01833    } else if (!strcasecmp(folder, "Cust1")) {
01834       return 5;
01835    } else if (!strcasecmp(folder, "Cust2")) {
01836       return 6;
01837    } else if (!strcasecmp(folder, "Cust3")) {
01838       return 7;
01839    } else if (!strcasecmp(folder, "Cust4")) {
01840       return 8;
01841    } else if (!strcasecmp(folder, "Cust5")) {
01842       return 9;
01843    } else if (!strcasecmp(folder, "Urgent")) {
01844       return 11;
01845    } else { /*assume they meant INBOX if folder is not found otherwise*/
01846       return 0;
01847    }
01848 }
01849 
01850 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01851 {
01852    SEARCHPGM *pgm;
01853    SEARCHHEADER *hdr;
01854 
01855    struct ast_vm_user *vmu, vmus;
01856    struct vm_state *vms_p;
01857    int ret = 0;
01858    int fold = folder_int(folder);
01859    int urgent = 0;
01860    
01861    /* If URGENT, then look at INBOX */
01862    if (fold == 11) {
01863       fold = NEW_FOLDER;
01864       urgent = 1;
01865    }
01866 
01867    if (ast_strlen_zero(mailbox))
01868       return 0;
01869 
01870    /* We have to get the user before we can open the stream! */
01871    vmu = find_user(&vmus, context, mailbox);
01872    if (!vmu) {
01873       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01874       return -1;
01875    } else {
01876       /* No IMAP account available */
01877       if (vmu->imapuser[0] == '\0') {
01878          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01879          return -1;
01880       }
01881    }
01882    
01883    /* No IMAP account available */
01884    if (vmu->imapuser[0] == '\0') {
01885       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01886       free_user(vmu);
01887       return -1;
01888    }
01889 
01890    /* check if someone is accessing this box right now... */
01891    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01892    if (!vms_p) {
01893       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01894    }
01895    if (vms_p) {
01896       ast_debug(3, "Returning before search - user is logged in\n");
01897       if (fold == 0) { /* INBOX */
01898          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
01899       }
01900       if (fold == 1) { /* Old messages */
01901          return vms_p->oldmessages;
01902       }
01903    }
01904 
01905    /* add one if not there... */
01906    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01907    if (!vms_p) {
01908       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01909    }
01910 
01911    if (!vms_p) {
01912       vms_p = create_vm_state_from_user(vmu);
01913    }
01914    ret = init_mailstream(vms_p, fold);
01915    if (!vms_p->mailstream) {
01916       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01917       return -1;
01918    }
01919    if (ret == 0) {
01920       ast_mutex_lock(&vms_p->lock);
01921       pgm = mail_newsearchpgm ();
01922       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01923       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01924       pgm->header = hdr;
01925       if (fold != OLD_FOLDER) {
01926          pgm->unseen = 1;
01927          pgm->seen = 0;
01928       }
01929       /* In the special case where fold is 1 (old messages) we have to do things a bit
01930        * differently. Old messages are stored in the INBOX but are marked as "seen"
01931        */
01932       else {
01933          pgm->unseen = 0;
01934          pgm->seen = 1;
01935       }
01936       /* look for urgent messages */
01937       if (fold == NEW_FOLDER) {
01938          if (urgent) {
01939             pgm->flagged = 1;
01940             pgm->unflagged = 0;
01941          } else {
01942             pgm->flagged = 0;
01943             pgm->unflagged = 1;
01944          }
01945       }
01946       pgm->undeleted = 1;
01947       pgm->deleted = 0;
01948 
01949       vms_p->vmArrayIndex = 0;
01950       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01951       if (fold == 0 && urgent == 0)
01952          vms_p->newmessages = vms_p->vmArrayIndex;
01953       if (fold == 1)
01954          vms_p->oldmessages = vms_p->vmArrayIndex;
01955       if (fold == 0 && urgent == 1)
01956          vms_p->urgentmessages = vms_p->vmArrayIndex;
01957       /*Freeing the searchpgm also frees the searchhdr*/
01958       mail_free_searchpgm(&pgm);
01959       ast_mutex_unlock(&vms_p->lock);
01960       vms_p->updated = 0;
01961       return vms_p->vmArrayIndex;
01962    } else {
01963       ast_mutex_lock(&vms_p->lock);
01964       mail_ping(vms_p->mailstream);
01965       ast_mutex_unlock(&vms_p->lock);
01966    }
01967    return 0;
01968 }
01969 
01970 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01971 {
01972    /* Check if mailbox is full */
01973    check_quota(vms, imapfolder);
01974    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01975       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01976       ast_play_and_wait(chan, "vm-mailboxfull");
01977       return -1;
01978    }
01979    
01980    /* Check if we have exceeded maxmsg */
01981    ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
01982    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
01983       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
01984       ast_play_and_wait(chan, "vm-mailboxfull");
01985       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
01986       return -1;
01987    }
01988 
01989    return 0;
01990 }
01991 
01992 /*!
01993  * \brief Gets the number of messages that exist in a mailbox folder.
01994  * \param context
01995  * \param mailbox
01996  * \param folder
01997  * 
01998  * This method is used when IMAP backend is used.
01999  * \return The number of messages in this mailbox folder (zero or more).
02000  */
02001 static int messagecount(const char *context, const char *mailbox, const char *folder)
02002 {
02003    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02004       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02005    } else {
02006       return __messagecount(context, mailbox, folder);
02007    }
02008 }
02009 
02010 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
02011 {
02012    char *myserveremail = serveremail;
02013    char fn[PATH_MAX];
02014    char introfn[PATH_MAX];
02015    char mailbox[256];
02016    char *stringp;
02017    FILE *p=NULL;
02018    char tmp[80] = "/tmp/astmail-XXXXXX";
02019    long len;
02020    void *buf;
02021    int tempcopy = 0;
02022    STRING str;
02023    int ret; /* for better error checking */
02024    char *imap_flags = NIL;
02025    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02026 
02027     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02028     if (msgnum < 0 && !imapgreetings) {
02029         return 0;
02030     }
02031    
02032    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02033       return -1;
02034    }
02035 
02036    /* Set urgent flag for IMAP message */
02037    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02038       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02039       imap_flags="\\FLAGGED";
02040    }
02041    
02042    /* Attach only the first format */
02043    fmt = ast_strdupa(fmt);
02044    stringp = fmt;
02045    strsep(&stringp, "|");
02046 
02047    if (!ast_strlen_zero(vmu->serveremail))
02048       myserveremail = vmu->serveremail;
02049 
02050    if (msgnum > -1)
02051       make_file(fn, sizeof(fn), dir, msgnum);
02052    else
02053       ast_copy_string (fn, dir, sizeof(fn));
02054 
02055    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02056    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02057       *introfn = '\0';
02058    }
02059    
02060    if (ast_strlen_zero(vmu->email)) {
02061       /* We need the vmu->email to be set when we call make_email_file, but
02062        * if we keep it set, a duplicate e-mail will be created. So at the end
02063        * of this function, we will revert back to an empty string if tempcopy
02064        * is 1.
02065        */
02066       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02067       tempcopy = 1;
02068    }
02069 
02070    if (!strcmp(fmt, "wav49"))
02071       fmt = "WAV";
02072    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02073 
02074    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02075       command hangs. */
02076    if (!(p = vm_mkftemp(tmp))) {
02077       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02078       if (tempcopy)
02079          *(vmu->email) = '\0';
02080       return -1;
02081    }
02082 
02083    if (msgnum < 0 && imapgreetings) {
02084       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02085          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02086          return -1;
02087       }
02088       imap_delete_old_greeting(fn, vms);
02089    }
02090 
02091    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02092    /* read mail file to memory */
02093    len = ftell(p);
02094    rewind(p);
02095    if (!(buf = ast_malloc(len + 1))) {
02096       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02097       fclose(p);
02098       if (tempcopy)
02099          *(vmu->email) = '\0';
02100       return -1;
02101    }
02102    if (fread(buf, len, 1, p) < len) {
02103       if (ferror(p)) {
02104          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02105          return -1;
02106       }
02107    }
02108    ((char *)buf)[len] = '\0';
02109    INIT(&str, mail_string, buf, len);
02110    ret = init_mailstream(vms, NEW_FOLDER);
02111    if (ret == 0) {
02112       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02113       ast_mutex_lock(&vms->lock);
02114       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02115          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02116       ast_mutex_unlock(&vms->lock);
02117       fclose(p);
02118       unlink(tmp);
02119       ast_free(buf);
02120    } else {
02121       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
02122       fclose(p);
02123       unlink(tmp);
02124       ast_free(buf);
02125       return -1;
02126    }
02127    ast_debug(3, "%s stored\n", fn);
02128    
02129    if (tempcopy)
02130       *(vmu->email) = '\0';
02131    inprocess_count(vmu->mailbox, vmu->context, -1);
02132    return 0;
02133 
02134 }
02135 
02136 /*!
02137  * \brief Gets the number of messages that exist in the inbox folder.
02138  * \param mailbox_context
02139  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02140  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02141  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02142  * 
02143  * This method is used when IMAP backend is used.
02144  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02145  *
02146  * \return zero on success, -1 on error.
02147  */
02148 
02149 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02150 {
02151    char tmp[PATH_MAX] = "";
02152    char *mailboxnc;
02153    char *context;
02154    char *mb;
02155    char *cur;
02156    if (newmsgs)
02157       *newmsgs = 0;
02158    if (oldmsgs)
02159       *oldmsgs = 0;
02160    if (urgentmsgs)
02161       *urgentmsgs = 0;
02162 
02163    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
02164    /* If no mailbox, return immediately */
02165    if (ast_strlen_zero(mailbox_context))
02166       return 0;
02167    
02168    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02169    context = strchr(tmp, '@');
02170    if (strchr(mailbox_context, ',')) {
02171       int tmpnew, tmpold, tmpurgent;
02172       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02173       mb = tmp;
02174       while ((cur = strsep(&mb, ", "))) {
02175          if (!ast_strlen_zero(cur)) {
02176             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02177                return -1;
02178             else {
02179                if (newmsgs)
02180                   *newmsgs += tmpnew; 
02181                if (oldmsgs)
02182                   *oldmsgs += tmpold;
02183                if (urgentmsgs)
02184                   *urgentmsgs += tmpurgent;
02185             }
02186          }
02187       }
02188       return 0;
02189    }
02190    if (context) {
02191       *context = '\0';
02192       mailboxnc = tmp;
02193       context++;
02194    } else {
02195       context = "default";
02196       mailboxnc = (char *)mailbox_context;
02197    }
02198    if (newmsgs) {
02199       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02200          return -1;
02201       }
02202    }
02203    if (oldmsgs) {
02204       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02205          return -1;
02206       }
02207    }
02208    if (urgentmsgs) {
02209       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02210          return -1;
02211       }
02212    }
02213    return 0;
02214 }
02215 
02216 /** 
02217  * \brief Determines if the given folder has messages.
02218  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02219  * \param folder the folder to look in
02220  *
02221  * This function is used when the mailbox is stored in an IMAP back end.
02222  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02223  * \return 1 if the folder has one or more messages. zero otherwise.
02224  */
02225 
02226 static int has_voicemail(const char *mailbox, const char *folder)
02227 {
02228    char tmp[256], *tmp2, *box, *context;
02229    ast_copy_string(tmp, mailbox, sizeof(tmp));
02230    tmp2 = tmp;
02231    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02232       while ((box = strsep(&tmp2, ",&"))) {
02233          if (!ast_strlen_zero(box)) {
02234             if (has_voicemail(box, folder)) {
02235                return 1;
02236             }
02237          }
02238       }
02239    }
02240    if ((context = strchr(tmp, '@'))) {
02241       *context++ = '\0';
02242    } else {
02243       context = "default";
02244    }
02245    return __messagecount(context, tmp, folder) ? 1 : 0;
02246 }
02247 
02248 /*!
02249  * \brief Copies a message from one mailbox to another.
02250  * \param chan
02251  * \param vmu
02252  * \param imbox
02253  * \param msgnum
02254  * \param duration
02255  * \param recip
02256  * \param fmt
02257  * \param dir
02258  *
02259  * This works with IMAP storage based mailboxes.
02260  *
02261  * \return zero on success, -1 on error.
02262  */
02263 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag)
02264 {
02265    struct vm_state *sendvms = NULL, *destvms = NULL;
02266    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02267    if (msgnum >= recip->maxmsg) {
02268       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02269       return -1;
02270    }
02271    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02272       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02273       return -1;
02274    }
02275    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02276       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02277       return -1;
02278    }
02279    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02280    ast_mutex_lock(&sendvms->lock);
02281    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02282       ast_mutex_unlock(&sendvms->lock);
02283       return 0;
02284    }
02285    ast_mutex_unlock(&sendvms->lock);
02286    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02287    return -1;
02288 }
02289 
02290 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02291 {
02292    char tmp[256], *t = tmp;
02293    size_t left = sizeof(tmp);
02294    
02295    if (box == OLD_FOLDER) {
02296       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02297    } else {
02298       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02299    }
02300 
02301    if (box == NEW_FOLDER) {
02302       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02303    } else {
02304       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02305    }
02306 
02307    /* Build up server information */
02308    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02309 
02310    /* Add authentication user if present */
02311    if (!ast_strlen_zero(authuser))
02312       ast_build_string(&t, &left, "/authuser=%s", authuser);
02313 
02314    /* Add flags if present */
02315    if (!ast_strlen_zero(imapflags))
02316       ast_build_string(&t, &left, "/%s", imapflags);
02317 
02318    /* End with username */
02319 #if 1
02320    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02321 #else
02322    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02323 #endif
02324    if (box == NEW_FOLDER || box == OLD_FOLDER)
02325       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02326    else if (box == GREETINGS_FOLDER)
02327       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02328    else {   /* Other folders such as Friends, Family, etc... */
02329       if (!ast_strlen_zero(imapparentfolder)) {
02330          /* imapparentfolder would typically be set to INBOX */
02331          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02332       } else {
02333          snprintf(spec, len, "%s%s", tmp, mbox(box));
02334       }
02335    }
02336 }
02337 
02338 static int init_mailstream(struct vm_state *vms, int box)
02339 {
02340    MAILSTREAM *stream = NIL;
02341    long debug;
02342    char tmp[256];
02343    
02344    if (!vms) {
02345       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02346       return -1;
02347    }
02348    if (option_debug > 2)
02349       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02350    if (vms->mailstream == NIL || !vms->mailstream) {
02351       if (option_debug)
02352          ast_log (LOG_DEBUG,"mailstream not set.\n");
02353    } else {
02354       stream = vms->mailstream;
02355    }
02356    /* debug = T;  user wants protocol telemetry? */
02357    debug = NIL;  /* NO protocol telemetry? */
02358 
02359    if (delimiter == '\0') {      /* did not probe the server yet */
02360       char *cp;
02361 #ifdef USE_SYSTEM_IMAP
02362 #include <imap/linkage.c>
02363 #elif defined(USE_SYSTEM_CCLIENT)
02364 #include <c-client/linkage.c>
02365 #else
02366 #include "linkage.c"
02367 #endif
02368       /* Connect to INBOX first to get folders delimiter */
02369       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02370       ast_mutex_lock(&vms->lock);
02371       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02372       ast_mutex_unlock(&vms->lock);
02373       if (stream == NIL) {
02374          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02375          return -1;
02376       }
02377       get_mailbox_delimiter(stream);
02378       /* update delimiter in imapfolder */
02379       for (cp = imapfolder; *cp; cp++)
02380          if (*cp == '/')
02381             *cp = delimiter;
02382    }
02383    /* Now connect to the target folder */
02384    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02385    if (option_debug > 2)
02386       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02387    ast_mutex_lock(&vms->lock);
02388    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02389    ast_mutex_unlock(&vms->lock);
02390    if (vms->mailstream == NIL) {
02391       return -1;
02392    } else {
02393       return 0;
02394    }
02395 }
02396 
02397 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02398 {
02399    SEARCHPGM *pgm;
02400    SEARCHHEADER *hdr;
02401    int ret, urgent = 0;
02402 
02403    /* If Urgent, then look at INBOX */
02404    if (box == 11) {
02405       box = NEW_FOLDER;
02406       urgent = 1;
02407    }
02408 
02409    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02410    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02411    vms->imapversion = vmu->imapversion;
02412 
02413    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02414       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02415       return -1;
02416    }
02417    
02418    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02419    
02420    /* Check Quota */
02421    if  (box == 0)  {
02422       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02423       check_quota(vms,(char *)mbox(box));
02424    }
02425 
02426    ast_mutex_lock(&vms->lock);
02427    pgm = mail_newsearchpgm();
02428 
02429    /* Check IMAP folder for Asterisk messages only... */
02430    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02431    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02432    pgm->header = hdr;
02433    pgm->deleted = 0;
02434    pgm->undeleted = 1;
02435 
02436    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02437    if (box == NEW_FOLDER && urgent == 1) {
02438       pgm->unseen = 1;
02439       pgm->seen = 0;
02440       pgm->flagged = 1;
02441       pgm->unflagged = 0;
02442    } else if (box == NEW_FOLDER && urgent == 0) {
02443       pgm->unseen = 1;
02444       pgm->seen = 0;
02445       pgm->flagged = 0;
02446       pgm->unflagged = 1;
02447    } else if (box == OLD_FOLDER) {
02448       pgm->seen = 1;
02449       pgm->unseen = 0;
02450    }
02451 
02452    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02453 
02454    vms->vmArrayIndex = 0;
02455    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02456    vms->lastmsg = vms->vmArrayIndex - 1;
02457    mail_free_searchpgm(&pgm);
02458    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02459     * ensure to allocate enough space to account for all of them. Warn if old messages
02460     * have not been checked first as that is required.
02461     */
02462    if (box == 0 && !vms->dh_arraysize) {
02463       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02464    }
02465    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02466       ast_mutex_unlock(&vms->lock);
02467       return -1;
02468    }
02469 
02470    ast_mutex_unlock(&vms->lock);
02471    return 0;
02472 }
02473 
02474 static void write_file(char *filename, char *buffer, unsigned long len)
02475 {
02476    FILE *output;
02477 
02478    output = fopen (filename, "w");
02479    if (fwrite(buffer, len, 1, output) != 1) {
02480       if (ferror(output)) {
02481          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02482       }
02483    }
02484    fclose (output);
02485 }
02486 
02487 static void update_messages_by_imapuser(const char *user, unsigned long number)
02488 {
02489    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02490 
02491    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02492       return;
02493    }
02494 
02495    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02496    vms->msgArray[vms->vmArrayIndex++] = number;
02497 }
02498 
02499 void mm_searched(MAILSTREAM *stream, unsigned long number)
02500 {
02501    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02502 
02503    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02504       return;
02505 
02506    update_messages_by_imapuser(user, number);
02507 }
02508 
02509 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02510 {
02511    struct ast_variable *var;
02512    struct ast_vm_user *vmu;
02513 
02514    vmu = ast_calloc(1, sizeof *vmu);
02515    if (!vmu)
02516       return NULL;
02517    ast_set_flag(vmu, VM_ALLOCED);
02518    populate_defaults(vmu);
02519 
02520    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02521    if (var) {
02522       apply_options_full(vmu, var);
02523       ast_variables_destroy(var);
02524       return vmu;
02525    } else {
02526       ast_free(vmu);
02527       return NULL;
02528    }
02529 }
02530 
02531 /* Interfaces to C-client */
02532 
02533 void mm_exists(MAILSTREAM * stream, unsigned long number)
02534 {
02535    /* mail_ping will callback here if new mail! */
02536    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02537    if (number == 0) return;
02538    set_update(stream);
02539 }
02540 
02541 
02542 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02543 {
02544    /* mail_ping will callback here if expunged mail! */
02545    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02546    if (number == 0) return;
02547    set_update(stream);
02548 }
02549 
02550 
02551 void mm_flags(MAILSTREAM * stream, unsigned long number)
02552 {
02553    /* mail_ping will callback here if read mail! */
02554    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02555    if (number == 0) return;
02556    set_update(stream);
02557 }
02558 
02559 
02560 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02561 {
02562    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02563    mm_log (string, errflg);
02564 }
02565 
02566 
02567 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02568 {
02569    if (delimiter == '\0') {
02570       delimiter = delim;
02571    }
02572 
02573    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02574    if (attributes & LATT_NOINFERIORS)
02575       ast_debug(5, "no inferiors\n");
02576    if (attributes & LATT_NOSELECT)
02577       ast_debug(5, "no select\n");
02578    if (attributes & LATT_MARKED)
02579       ast_debug(5, "marked\n");
02580    if (attributes & LATT_UNMARKED)
02581       ast_debug(5, "unmarked\n");
02582 }
02583 
02584 
02585 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02586 {
02587    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02588    if (attributes & LATT_NOINFERIORS)
02589       ast_debug(5, "no inferiors\n");
02590    if (attributes & LATT_NOSELECT)
02591       ast_debug(5, "no select\n");
02592    if (attributes & LATT_MARKED)
02593       ast_debug(5, "marked\n");
02594    if (attributes & LATT_UNMARKED)
02595       ast_debug(5, "unmarked\n");
02596 }
02597 
02598 
02599 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02600 {
02601    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02602    if (status->flags & SA_MESSAGES)
02603       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02604    if (status->flags & SA_RECENT)
02605       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02606    if (status->flags & SA_UNSEEN)
02607       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02608    if (status->flags & SA_UIDVALIDITY)
02609       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02610    if (status->flags & SA_UIDNEXT)
02611       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02612    ast_log(AST_LOG_NOTICE, "\n");
02613 }
02614 
02615 
02616 void mm_log(char *string, long errflg)
02617 {
02618    switch ((short) errflg) {
02619       case NIL:
02620          ast_debug(1,"IMAP Info: %s\n", string);
02621          break;
02622       case PARSE:
02623       case WARN:
02624          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02625          break;
02626       case ERROR:
02627          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02628          break;
02629    }
02630 }
02631 
02632 
02633 void mm_dlog(char *string)
02634 {
02635    ast_log(AST_LOG_NOTICE, "%s\n", string);
02636 }
02637 
02638 
02639 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02640 {
02641    struct ast_vm_user *vmu;
02642 
02643    ast_debug(4, "Entering callback mm_login\n");
02644 
02645    ast_copy_string(user, mb->user, MAILTMPLEN);
02646 
02647    /* We should only do this when necessary */
02648    if (!ast_strlen_zero(authpassword)) {
02649       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02650    } else {
02651       AST_LIST_TRAVERSE(&users, vmu, list) {
02652          if (!strcasecmp(mb->user, vmu->imapuser)) {
02653             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02654             break;
02655          }
02656       }
02657       if (!vmu) {
02658          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02659             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02660             free_user(vmu);
02661          }
02662       }
02663    }
02664 }
02665 
02666 
02667 void mm_critical(MAILSTREAM * stream)
02668 {
02669 }
02670 
02671 
02672 void mm_nocritical(MAILSTREAM * stream)
02673 {
02674 }
02675 
02676 
02677 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02678 {
02679    kill (getpid (), SIGSTOP);
02680    return NIL;
02681 }
02682 
02683 
02684 void mm_fatal(char *string)
02685 {
02686    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02687 }
02688 
02689 /* C-client callback to handle quota */
02690 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02691 {
02692    struct vm_state *vms;
02693    char *mailbox = stream->mailbox, *user;
02694    char buf[1024] = "";
02695    unsigned long usage = 0, limit = 0;
02696    
02697    while (pquota) {
02698       usage = pquota->usage;
02699       limit = pquota->limit;
02700       pquota = pquota->next;
02701    }
02702    
02703    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
02704       ast_log(AST_LOG_ERROR, "No state found.\n");
02705       return;
02706    }
02707 
02708    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02709 
02710    vms->quota_usage = usage;
02711    vms->quota_limit = limit;
02712 }
02713 
02714 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02715 {
02716    char *start, *eol_pnt;
02717    int taglen;
02718 
02719    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02720       return NULL;
02721 
02722    taglen = strlen(tag) + 1;
02723    if (taglen < 1)
02724       return NULL;
02725 
02726    if (!(start = strstr(header, tag)))
02727       return NULL;
02728 
02729    /* Since we can be called multiple times we should clear our buffer */
02730    memset(buf, 0, len);
02731 
02732    ast_copy_string(buf, start+taglen, len);
02733    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02734       *eol_pnt = '\0';
02735    return buf;
02736 }
02737 
02738 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02739 {
02740    char *start, *quote, *eol_pnt;
02741 
02742    if (ast_strlen_zero(mailbox))
02743       return NULL;
02744 
02745    if (!(start = strstr(mailbox, "/user=")))
02746       return NULL;
02747 
02748    ast_copy_string(buf, start+6, len);
02749 
02750    if (!(quote = strchr(buf, '\"'))) {
02751       if (!(eol_pnt = strchr(buf, '/')))
02752          eol_pnt = strchr(buf,'}');
02753       *eol_pnt = '\0';
02754       return buf;
02755    } else {
02756       eol_pnt = strchr(buf+1,'\"');
02757       *eol_pnt = '\0';
02758       return buf+1;
02759    }
02760 }
02761 
02762 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02763 {
02764    struct vm_state *vms_p;
02765 
02766    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02767    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02768       return vms_p;
02769    }
02770    if (option_debug > 4)
02771       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02772    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02773       return NULL;
02774    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02775    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02776    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02777    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02778    vms_p->imapversion = vmu->imapversion;
02779    if (option_debug > 4)
02780       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02781    vms_p->updated = 1;
02782    /* set mailbox to INBOX! */
02783    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02784    init_vm_state(vms_p);
02785    vmstate_insert(vms_p);
02786    return vms_p;
02787 }
02788 
02789 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02790 {
02791    struct vmstate *vlist = NULL;
02792 
02793    if (interactive) {
02794       struct vm_state *vms;
02795       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02796       vms = pthread_getspecific(ts_vmstate.key);
02797       return vms;
02798    }
02799 
02800    AST_LIST_LOCK(&vmstates);
02801    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02802       if (!vlist->vms) {
02803          ast_debug(3, "error: vms is NULL for %s\n", user);
02804          continue;
02805       }
02806       if (vlist->vms->imapversion != imapversion) {
02807          continue;
02808       }
02809       if (!vlist->vms->imapuser) {
02810          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02811          continue;
02812       }
02813 
02814       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02815          AST_LIST_UNLOCK(&vmstates);
02816          return vlist->vms;
02817       }
02818    }
02819    AST_LIST_UNLOCK(&vmstates);
02820 
02821    ast_debug(3, "%s not found in vmstates\n", user);
02822 
02823    return NULL;
02824 }
02825 
02826 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02827 {
02828 
02829    struct vmstate *vlist = NULL;
02830    const char *local_context = S_OR(context, "default");
02831 
02832    if (interactive) {
02833       struct vm_state *vms;
02834       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02835       vms = pthread_getspecific(ts_vmstate.key);
02836       return vms;
02837    }
02838 
02839    AST_LIST_LOCK(&vmstates);
02840    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02841       if (!vlist->vms) {
02842          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02843          continue;
02844       }
02845       if (vlist->vms->imapversion != imapversion) {
02846          continue;
02847       }
02848       if (!vlist->vms->username || !vlist->vms->context) {
02849          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02850          continue;
02851       }
02852 
02853       ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
02854       
02855       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02856          ast_debug(3, "Found it!\n");
02857          AST_LIST_UNLOCK(&vmstates);
02858          return vlist->vms;
02859       }
02860    }
02861    AST_LIST_UNLOCK(&vmstates);
02862 
02863    ast_debug(3, "%s not found in vmstates\n", mailbox);
02864 
02865    return NULL;
02866 }
02867 
02868 static void vmstate_insert(struct vm_state *vms) 
02869 {
02870    struct vmstate *v;
02871    struct vm_state *altvms;
02872 
02873    /* If interactive, it probably already exists, and we should
02874       use the one we already have since it is more up to date.
02875       We can compare the username to find the duplicate */
02876    if (vms->interactive == 1) {
02877       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02878       if (altvms) {  
02879          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02880          vms->newmessages = altvms->newmessages;
02881          vms->oldmessages = altvms->oldmessages;
02882          vms->vmArrayIndex = altvms->vmArrayIndex;
02883          vms->lastmsg = altvms->lastmsg;
02884          vms->curmsg = altvms->curmsg;
02885          /* get a pointer to the persistent store */
02886          vms->persist_vms = altvms;
02887          /* Reuse the mailstream? */
02888 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02889          vms->mailstream = altvms->mailstream;
02890 #else
02891          vms->mailstream = NIL;
02892 #endif
02893       }
02894       return;
02895    }
02896 
02897    if (!(v = ast_calloc(1, sizeof(*v))))
02898       return;
02899    
02900    v->vms = vms;
02901 
02902    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02903 
02904    AST_LIST_LOCK(&vmstates);
02905    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02906    AST_LIST_UNLOCK(&vmstates);
02907 }
02908 
02909 static void vmstate_delete(struct vm_state *vms) 
02910 {
02911    struct vmstate *vc = NULL;
02912    struct vm_state *altvms = NULL;
02913 
02914    /* If interactive, we should copy pertinent info
02915       back to the persistent state (to make update immediate) */
02916    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02917       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02918       altvms->newmessages = vms->newmessages;
02919       altvms->oldmessages = vms->oldmessages;
02920       altvms->updated = 1;
02921       vms->mailstream = mail_close(vms->mailstream);
02922 
02923       /* Interactive states are not stored within the persistent list */
02924       return;
02925    }
02926    
02927    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02928    
02929    AST_LIST_LOCK(&vmstates);
02930    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02931       if (vc->vms == vms) {
02932          AST_LIST_REMOVE_CURRENT(list);
02933          break;
02934       }
02935    }
02936    AST_LIST_TRAVERSE_SAFE_END
02937    AST_LIST_UNLOCK(&vmstates);
02938    
02939    if (vc) {
02940       ast_mutex_destroy(&vc->vms->lock);
02941       ast_free(vc);
02942    }
02943    else
02944       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02945 }
02946 
02947 static void set_update(MAILSTREAM * stream) 
02948 {
02949    struct vm_state *vms;
02950    char *mailbox = stream->mailbox, *user;
02951    char buf[1024] = "";
02952 
02953    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02954       if (user && option_debug > 2)
02955          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02956       return;
02957    }
02958 
02959    ast_debug(3, "User %s mailbox set for update.\n", user);
02960 
02961    vms->updated = 1; /* Set updated flag since mailbox changed */
02962 }
02963 
02964 static void init_vm_state(struct vm_state *vms) 
02965 {
02966    int x;
02967    vms->vmArrayIndex = 0;
02968    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02969       vms->msgArray[x] = 0;
02970    }
02971    ast_mutex_init(&vms->lock);
02972 }
02973 
02974 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02975 {
02976    char *body_content;
02977    char *body_decoded;
02978    char *fn = is_intro ? vms->introfn : vms->fn;
02979    unsigned long len;
02980    unsigned long newlen;
02981    char filename[256];
02982    
02983    if (!body || body == NIL)
02984       return -1;
02985 
02986    ast_mutex_lock(&vms->lock);
02987    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02988    ast_mutex_unlock(&vms->lock);
02989    if (body_content != NIL) {
02990       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02991       /* ast_debug(1,body_content); */
02992       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02993       /* If the body of the file is empty, return an error */
02994       if (!newlen) {
02995          return -1;
02996       }
02997       write_file(filename, (char *) body_decoded, newlen);
02998    } else {
02999       ast_debug(5, "Body of message is NULL.\n");
03000       return -1;
03001    }
03002    return 0;
03003 }
03004 
03005 /*! 
03006  * \brief Get delimiter via mm_list callback 
03007  * \param stream
03008  *
03009  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03010  */
03011 /* MUTEX should already be held */
03012 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03013    char tmp[50];
03014    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03015    mail_list(stream, tmp, "*");
03016 }
03017 
03018 /*! 
03019  * \brief Check Quota for user 
03020  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03021  * \param mailbox the mailbox to check the quota for.
03022  *
03023  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03024  */
03025 static void check_quota(struct vm_state *vms, char *mailbox) {
03026    ast_mutex_lock(&vms->lock);
03027    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03028    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03029    if (vms && vms->mailstream != NULL) {
03030       imap_getquotaroot(vms->mailstream, mailbox);
03031    } else {
03032       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03033    }
03034    ast_mutex_unlock(&vms->lock);
03035 }
03036 
03037 #endif /* IMAP_STORAGE */
03038 
03039 /*! \brief Lock file path
03040     only return failure if ast_lock_path returns 'timeout',
03041    not if the path does not exist or any other reason
03042 */
03043 static int vm_lock_path(const char *path)
03044 {
03045    switch (ast_lock_path(path)) {
03046    case AST_LOCK_TIMEOUT:
03047       return -1;
03048    default:
03049       return 0;
03050    }
03051 }
03052 
03053 
03054 #ifdef ODBC_STORAGE
03055 struct generic_prepare_struct {
03056    char *sql;
03057    int argc;
03058    char **argv;
03059 };
03060 
03061 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03062 {
03063    struct generic_prepare_struct *gps = data;
03064    int res, i;
03065    SQLHSTMT stmt;
03066 
03067    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03068    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03069       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03070       return NULL;
03071    }
03072    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
03073    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03074       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03075       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03076       return NULL;
03077    }
03078    for (i = 0; i < gps->argc; i++)
03079       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03080 
03081    return stmt;
03082 }
03083 
03084 /*!
03085  * \brief Retrieves a file from an ODBC data store.
03086  * \param dir the path to the file to be retreived.
03087  * \param msgnum the message number, such as within a mailbox folder.
03088  * 
03089  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03090  * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
03091  *
03092  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03093  * The output is the message information file with the name msgnum and the extension .txt
03094  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03095  * 
03096  * \return 0 on success, -1 on error.
03097  */
03098 static int retrieve_file(char *dir, int msgnum)
03099 {
03100    int x = 0;
03101    int res;
03102    int fd=-1;
03103    size_t fdlen = 0;
03104    void *fdm = MAP_FAILED;
03105    SQLSMALLINT colcount=0;
03106    SQLHSTMT stmt;
03107    char sql[PATH_MAX];
03108    char fmt[80]="";
03109    char *c;
03110    char coltitle[256];
03111    SQLSMALLINT collen;
03112    SQLSMALLINT datatype;
03113    SQLSMALLINT decimaldigits;
03114    SQLSMALLINT nullable;
03115    SQLULEN colsize;
03116    SQLLEN colsize2;
03117    FILE *f=NULL;
03118    char rowdata[80];
03119    char fn[PATH_MAX];
03120    char full_fn[PATH_MAX];
03121    char msgnums[80];
03122    char *argv[] = { dir, msgnums };
03123    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03124 
03125    struct odbc_obj *obj;
03126    obj = ast_odbc_request_obj(odbc_database, 0);
03127    if (obj) {
03128       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03129       c = strchr(fmt, '|');
03130       if (c)
03131          *c = '\0';
03132       if (!strcasecmp(fmt, "wav49"))
03133          strcpy(fmt, "WAV");
03134       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03135       if (msgnum > -1)
03136          make_file(fn, sizeof(fn), dir, msgnum);
03137       else
03138          ast_copy_string(fn, dir, sizeof(fn));
03139 
03140       /* Create the information file */
03141       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03142       
03143       if (!(f = fopen(full_fn, "w+"))) {
03144          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03145          goto yuck;
03146       }
03147       
03148       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03149       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03150       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03151       if (!stmt) {
03152          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03153          ast_odbc_release_obj(obj);
03154          goto yuck;
03155       }
03156       res = SQLFetch(stmt);
03157       if (res == SQL_NO_DATA) {
03158          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03159          ast_odbc_release_obj(obj);
03160          goto yuck;
03161       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03162          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03163          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03164          ast_odbc_release_obj(obj);
03165          goto yuck;
03166       }
03167       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03168       if (fd < 0) {
03169          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03170          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03171          ast_odbc_release_obj(obj);
03172          goto yuck;
03173       }
03174       res = SQLNumResultCols(stmt, &colcount);
03175       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03176          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03177          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03178          ast_odbc_release_obj(obj);
03179          goto yuck;
03180       }
03181       if (f) 
03182          fprintf(f, "[message]\n");
03183       for (x=0;x<colcount;x++) {
03184          rowdata[0] = '\0';
03185          collen = sizeof(coltitle);
03186          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
03187                   &datatype, &colsize, &decimaldigits, &nullable);
03188          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03189             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03190             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03191             ast_odbc_release_obj(obj);
03192             goto yuck;
03193          }
03194          if (!strcasecmp(coltitle, "recording")) {
03195             off_t offset;
03196             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03197             fdlen = colsize2;
03198             if (fd > -1) {
03199                char tmp[1]="";
03200                lseek(fd, fdlen - 1, SEEK_SET);
03201                if (write(fd, tmp, 1) != 1) {
03202                   close(fd);
03203                   fd = -1;
03204                   continue;
03205                }
03206                /* Read out in small chunks */
03207                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03208                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03209                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03210                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03211                      ast_odbc_release_obj(obj);
03212                      goto yuck;
03213                   } else {
03214                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03215                      munmap(fdm, CHUNKSIZE);
03216                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03217                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03218                         unlink(full_fn);
03219                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03220                         ast_odbc_release_obj(obj);
03221                         goto yuck;
03222                      }
03223                   }
03224                }
03225                if (truncate(full_fn, fdlen) < 0) {
03226                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03227                }
03228             }
03229          } else {
03230             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03231             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03232                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03233                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03234                ast_odbc_release_obj(obj);
03235                goto yuck;
03236             }
03237             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03238                fprintf(f, "%s=%s\n", coltitle, rowdata);
03239          }
03240       }
03241       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03242       ast_odbc_release_obj(obj);
03243    } else
03244       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03245 yuck: 
03246    if (f)
03247       fclose(f);
03248    if (fd > -1)
03249       close(fd);
03250    return x - 1;
03251 }
03252 
03253 /*!
03254  * \brief Determines the highest message number in use for a given user and mailbox folder.
03255  * \param vmu 
03256  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03257  *
03258  * This method is used when mailboxes are stored in an ODBC back end.
03259  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03260  *
03261  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03262  */
03263 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03264 {
03265    int x = 0;
03266    int res;
03267    SQLHSTMT stmt;
03268    char sql[PATH_MAX];
03269    char rowdata[20];
03270    char *argv[] = { dir };
03271    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03272 
03273    struct odbc_obj *obj;
03274    obj = ast_odbc_request_obj(odbc_database, 0);
03275    if (obj) {
03276       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03277       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03278       if (!stmt) {
03279          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03280          ast_odbc_release_obj(obj);
03281          goto yuck;
03282       }
03283       res = SQLFetch(stmt);
03284       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03285          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03286          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03287          ast_odbc_release_obj(obj);
03288          goto yuck;
03289       }
03290       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03291       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03292          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03293          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03294          ast_odbc_release_obj(obj);
03295          goto yuck;
03296       }
03297       if (sscanf(rowdata, "%30d", &x) != 1)
03298          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03299       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03300       ast_odbc_release_obj(obj);
03301    } else
03302       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03303 yuck: 
03304    return x - 1;
03305 }
03306 
03307 /*!
03308  * \brief Determines if the specified message exists.
03309  * \param dir the folder the mailbox folder to look for messages. 
03310  * \param msgnum the message index to query for.
03311  *
03312  * This method is used when mailboxes are stored in an ODBC back end.
03313  *
03314  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03315  */
03316 static int message_exists(char *dir, int msgnum)
03317 {
03318    int x = 0;
03319    int res;
03320    SQLHSTMT stmt;
03321    char sql[PATH_MAX];
03322    char rowdata[20];
03323    char msgnums[20];
03324    char *argv[] = { dir, msgnums };
03325    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03326 
03327    struct odbc_obj *obj;
03328    obj = ast_odbc_request_obj(odbc_database, 0);
03329    if (obj) {
03330       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03331       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03332       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03333       if (!stmt) {
03334          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03335          ast_odbc_release_obj(obj);
03336          goto yuck;
03337       }
03338       res = SQLFetch(stmt);
03339       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03340          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03341          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03342          ast_odbc_release_obj(obj);
03343          goto yuck;
03344       }
03345       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03346       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03347          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03348          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03349          ast_odbc_release_obj(obj);
03350          goto yuck;
03351       }
03352       if (sscanf(rowdata, "%30d", &x) != 1)
03353          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03354       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03355       ast_odbc_release_obj(obj);
03356    } else
03357       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03358 yuck: 
03359    return x;
03360 }
03361 
03362 /*!
03363  * \brief returns the one-based count for messages.
03364  * \param vmu
03365  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03366  *
03367  * This method is used when mailboxes are stored in an ODBC back end.
03368  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03369  * one-based messages.
03370  * This method just calls last_message_index and returns +1 of its value.
03371  *
03372  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03373  */
03374 static int count_messages(struct ast_vm_user *vmu, char *dir)
03375 {
03376    return last_message_index(vmu, dir) + 1;
03377 }
03378 
03379 /*!
03380  * \brief Deletes a message from the mailbox folder.
03381  * \param sdir The mailbox folder to work in.
03382  * \param smsg The message index to be deleted.
03383  *
03384  * This method is used when mailboxes are stored in an ODBC back end.
03385  * The specified message is directly deleted from the database 'voicemessages' table.
03386  * 
03387  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03388  */
03389 static void delete_file(const char *sdir, int smsg)
03390 {
03391    SQLHSTMT stmt;
03392    char sql[PATH_MAX];
03393    char msgnums[20];
03394    char *argv[] = { NULL, msgnums };
03395    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03396    struct odbc_obj *obj;
03397 
03398    argv[0] = ast_strdupa(sdir);
03399 
03400    obj = ast_odbc_request_obj(odbc_database, 0);
03401    if (obj) {
03402       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03403       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03404       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03405       if (!stmt)
03406          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03407       else
03408          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03409       ast_odbc_release_obj(obj);
03410    } else
03411       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03412    return;  
03413 }
03414 
03415 /*!
03416  * \brief Copies a voicemail from one mailbox to another.
03417  * \param sdir the folder for which to look for the message to be copied.
03418  * \param smsg the index of the message to be copied.
03419  * \param ddir the destination folder to copy the message into.
03420  * \param dmsg the index to be used for the copied message.
03421  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03422  * \param dmailboxcontext The context for the destination user.
03423  *
03424  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03425  */
03426 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03427 {
03428    SQLHSTMT stmt;
03429    char sql[512];
03430    char msgnums[20];
03431    char msgnumd[20];
03432    struct odbc_obj *obj;
03433    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03434    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03435 
03436    delete_file(ddir, dmsg);
03437    obj = ast_odbc_request_obj(odbc_database, 0);
03438    if (obj) {
03439       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03440       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03441       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);
03442       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03443       if (!stmt)
03444          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03445       else
03446          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03447       ast_odbc_release_obj(obj);
03448    } else
03449       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03450    return;  
03451 }
03452 
03453 struct insert_data {
03454    char *sql;
03455    const char *dir;
03456    const char *msgnums;
03457    void *data;
03458    SQLLEN datalen;
03459    SQLLEN indlen;
03460    const char *context;
03461    const char *macrocontext;
03462    const char *callerid;
03463    const char *origtime;
03464    const char *duration;
03465    const char *mailboxuser;
03466    const char *mailboxcontext;
03467    const char *category;
03468    const char *flag;
03469 };
03470 
03471 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03472 {
03473    struct insert_data *data = vdata;
03474    int res;
03475    SQLHSTMT stmt;
03476 
03477    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03478    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03479       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03480       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03481       return NULL;
03482    }
03483 
03484    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03485    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03486    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03487    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03488    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03489    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03490    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03491    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03492    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03493    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03494    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03495    if (!ast_strlen_zero(data->category)) {
03496       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03497    }
03498    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03499    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03500       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03501       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03502       return NULL;
03503    }
03504 
03505    return stmt;
03506 }
03507 
03508 /*!
03509  * \brief Stores a voicemail into the database.
03510  * \param dir the folder the mailbox folder to store the message.
03511  * \param mailboxuser the user owning the mailbox folder.
03512  * \param mailboxcontext
03513  * \param msgnum the message index for the message to be stored.
03514  *
03515  * This method is used when mailboxes are stored in an ODBC back end.
03516  * The message sound file and information file is looked up on the file system. 
03517  * A SQL query is invoked to store the message into the (MySQL) database.
03518  *
03519  * \return the zero on success -1 on error.
03520  */
03521 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03522 {
03523    int res = 0;
03524    int fd = -1;
03525    void *fdm = MAP_FAILED;
03526    size_t fdlen = -1;
03527    SQLHSTMT stmt;
03528    char sql[PATH_MAX];
03529    char msgnums[20];
03530    char fn[PATH_MAX];
03531    char full_fn[PATH_MAX];
03532    char fmt[80]="";
03533    char *c;
03534    struct ast_config *cfg=NULL;
03535    struct odbc_obj *obj;
03536    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03537       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03538    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03539 
03540    delete_file(dir, msgnum);
03541    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03542       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03543       return -1;
03544    }
03545 
03546    do {
03547       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03548       c = strchr(fmt, '|');
03549       if (c)
03550          *c = '\0';
03551       if (!strcasecmp(fmt, "wav49"))
03552          strcpy(fmt, "WAV");
03553       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03554       if (msgnum > -1)
03555          make_file(fn, sizeof(fn), dir, msgnum);
03556       else
03557          ast_copy_string(fn, dir, sizeof(fn));
03558       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03559       cfg = ast_config_load(full_fn, config_flags);
03560       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03561       fd = open(full_fn, O_RDWR);
03562       if (fd < 0) {
03563          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03564          res = -1;
03565          break;
03566       }
03567       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03568          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03569             idata.context = "";
03570          }
03571          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03572             idata.macrocontext = "";
03573          }
03574          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03575             idata.callerid = "";
03576          }
03577          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03578             idata.origtime = "";
03579          }
03580          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03581             idata.duration = "";
03582          }
03583          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03584             idata.category = "";
03585          }
03586          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03587             idata.flag = "";
03588          }
03589       }
03590       fdlen = lseek(fd, 0, SEEK_END);
03591       lseek(fd, 0, SEEK_SET);
03592       printf("Length is %zd\n", fdlen);
03593       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03594       if (fdm == MAP_FAILED) {
03595          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03596          res = -1;
03597          break;
03598       } 
03599       idata.data = fdm;
03600       idata.datalen = idata.indlen = fdlen;
03601 
03602       if (!ast_strlen_zero(idata.category)) 
03603          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03604       else
03605          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03606 
03607       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03608          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03609       } else {
03610          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03611          res = -1;
03612       }
03613    } while (0);
03614    if (obj) {
03615       ast_odbc_release_obj(obj);
03616    }
03617    if (cfg)
03618       ast_config_destroy(cfg);
03619    if (fdm != MAP_FAILED)
03620       munmap(fdm, fdlen);
03621    if (fd > -1)
03622       close(fd);
03623    return res;
03624 }
03625 
03626 /*!
03627  * \brief Renames a message in a mailbox folder.
03628  * \param sdir The folder of the message to be renamed.
03629  * \param smsg The index of the message to be renamed.
03630  * \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.
03631  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03632  * \param ddir The destination folder for the message to be renamed into
03633  * \param dmsg The destination message for the message to be renamed.
03634  *
03635  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03636  * 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.
03637  * 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.
03638  */
03639 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03640 {
03641    SQLHSTMT stmt;
03642    char sql[PATH_MAX];
03643    char msgnums[20];
03644    char msgnumd[20];
03645    struct odbc_obj *obj;
03646    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03647    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03648 
03649    delete_file(ddir, dmsg);
03650    obj = ast_odbc_request_obj(odbc_database, 0);
03651    if (obj) {
03652       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03653       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03654       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03655       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03656       if (!stmt)
03657          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03658       else
03659          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03660       ast_odbc_release_obj(obj);
03661    } else
03662       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03663    return;  
03664 }
03665 
03666 /*!
03667  * \brief Removes a voicemail message file.
03668  * \param dir the path to the message file.
03669  * \param msgnum the unique number for the message within the mailbox.
03670  *
03671  * Removes the message content file and the information file.
03672  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03673  * Typical use is to clean up after a RETRIEVE operation. 
03674  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03675  * \return zero on success, -1 on error.
03676  */
03677 static int remove_file(char *dir, int msgnum)
03678 {
03679    char fn[PATH_MAX];
03680    char full_fn[PATH_MAX];
03681    char msgnums[80];
03682    
03683    if (msgnum > -1) {
03684       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03685       make_file(fn, sizeof(fn), dir, msgnum);
03686    } else
03687       ast_copy_string(fn, dir, sizeof(fn));
03688    ast_filedelete(fn, NULL);  
03689    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03690    unlink(full_fn);
03691    return 0;
03692 }
03693 #else
03694 #ifndef IMAP_STORAGE
03695 /*!
03696  * \brief Find all .txt files - even if they are not in sequence from 0000.
03697  * \param vmu
03698  * \param dir
03699  *
03700  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03701  *
03702  * \return the count of messages, zero or more.
03703  */
03704 static int count_messages(struct ast_vm_user *vmu, char *dir)
03705 {
03706 
03707    int vmcount = 0;
03708    DIR *vmdir = NULL;
03709    struct dirent *vment = NULL;
03710 
03711    if (vm_lock_path(dir))
03712       return ERROR_LOCK_PATH;
03713 
03714    if ((vmdir = opendir(dir))) {
03715       while ((vment = readdir(vmdir))) {
03716          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03717             vmcount++;
03718          }
03719       }
03720       closedir(vmdir);
03721    }
03722    ast_unlock_path(dir);
03723    
03724    return vmcount;
03725 }
03726 
03727 /*!
03728  * \brief Renames a message in a mailbox folder.
03729  * \param sfn The path to the mailbox information and data file to be renamed.
03730  * \param dfn The path for where the message data and information files will be renamed to.
03731  *
03732  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03733  */
03734 static void rename_file(char *sfn, char *dfn)
03735 {
03736    char stxt[PATH_MAX];
03737    char dtxt[PATH_MAX];
03738    ast_filerename(sfn,dfn,NULL);
03739    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03740    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03741    if (ast_check_realtime("voicemail_data")) {
03742       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03743    }
03744    rename(stxt, dtxt);
03745 }
03746 
03747 /*! 
03748  * \brief Determines the highest message number in use for a given user and mailbox folder.
03749  * \param vmu 
03750  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03751  *
03752  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03753  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03754  *
03755  * \note Should always be called with a lock already set on dir.
03756  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03757  */
03758 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03759 {
03760    int x;
03761    unsigned char map[MAXMSGLIMIT] = "";
03762    DIR *msgdir;
03763    struct dirent *msgdirent;
03764    int msgdirint;
03765    char extension[4];
03766    int stopcount = 0;
03767 
03768    /* Reading the entire directory into a file map scales better than
03769     * doing a stat repeatedly on a predicted sequence.  I suspect this
03770     * is partially due to stat(2) internally doing a readdir(2) itself to
03771     * find each file. */
03772    if (!(msgdir = opendir(dir))) {
03773       return -1;
03774    }
03775 
03776    while ((msgdirent = readdir(msgdir))) {
03777       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
03778          map[msgdirint] = 1;
03779          stopcount++;
03780          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
03781       }
03782    }
03783    closedir(msgdir);
03784 
03785    for (x = 0; x < vmu->maxmsg; x++) {
03786       if (map[x] == 1) {
03787          stopcount--;
03788       } else if (map[x] == 0 && !stopcount) {
03789          break;
03790       }
03791    }
03792 
03793    return x - 1;
03794 }
03795 
03796 #endif /* #ifndef IMAP_STORAGE */
03797 #endif /* #else of #ifdef ODBC_STORAGE */
03798 #ifndef IMAP_STORAGE
03799 /*!
03800  * \brief Utility function to copy a file.
03801  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03802  * \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.
03803  *
03804  * 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.
03805  * The copy operation copies up to 4096 bytes at once.
03806  *
03807  * \return zero on success, -1 on error.
03808  */
03809 static int copy(char *infile, char *outfile)
03810 {
03811    int ifd;
03812    int ofd;
03813    int res;
03814    int len;
03815    char buf[4096];
03816 
03817 #ifdef HARDLINK_WHEN_POSSIBLE
03818    /* Hard link if possible; saves disk space & is faster */
03819    if (link(infile, outfile)) {
03820 #endif
03821       if ((ifd = open(infile, O_RDONLY)) < 0) {
03822          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03823          return -1;
03824       }
03825       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03826          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03827          close(ifd);
03828          return -1;
03829       }
03830       do {
03831          len = read(ifd, buf, sizeof(buf));
03832          if (len < 0) {
03833             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03834             close(ifd);
03835             close(ofd);
03836             unlink(outfile);
03837          }
03838          if (len) {
03839             res = write(ofd, buf, len);
03840             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03841                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03842                close(ifd);
03843                close(ofd);
03844                unlink(outfile);
03845             }
03846          }
03847       } while (len);
03848       close(ifd);
03849       close(ofd);
03850       return 0;
03851 #ifdef HARDLINK_WHEN_POSSIBLE
03852    } else {
03853       /* Hard link succeeded */
03854       return 0;
03855    }
03856 #endif
03857 }
03858 
03859 /*!
03860  * \brief Copies a voicemail information (envelope) file.
03861  * \param frompath
03862  * \param topath 
03863  *
03864  * Every voicemail has the data (.wav) file, and the information file.
03865  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03866  * This is used by the COPY macro when not using IMAP storage.
03867  */
03868 static void copy_plain_file(char *frompath, char *topath)
03869 {
03870    char frompath2[PATH_MAX], topath2[PATH_MAX];
03871    struct ast_variable *tmp,*var = NULL;
03872    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03873    ast_filecopy(frompath, topath, NULL);
03874    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03875    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03876    if (ast_check_realtime("voicemail_data")) {
03877       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03878       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03879       for (tmp = var; tmp; tmp = tmp->next) {
03880          if (!strcasecmp(tmp->name, "origmailbox")) {
03881             origmailbox = tmp->value;
03882          } else if (!strcasecmp(tmp->name, "context")) {
03883             context = tmp->value;
03884          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03885             macrocontext = tmp->value;
03886          } else if (!strcasecmp(tmp->name, "exten")) {
03887             exten = tmp->value;
03888          } else if (!strcasecmp(tmp->name, "priority")) {
03889             priority = tmp->value;
03890          } else if (!strcasecmp(tmp->name, "callerchan")) {
03891             callerchan = tmp->value;
03892          } else if (!strcasecmp(tmp->name, "callerid")) {
03893             callerid = tmp->value;
03894          } else if (!strcasecmp(tmp->name, "origdate")) {
03895             origdate = tmp->value;
03896          } else if (!strcasecmp(tmp->name, "origtime")) {
03897             origtime = tmp->value;
03898          } else if (!strcasecmp(tmp->name, "category")) {
03899             category = tmp->value;
03900          } else if (!strcasecmp(tmp->name, "duration")) {
03901             duration = tmp->value;
03902          }
03903       }
03904       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);
03905    }
03906    copy(frompath2, topath2);
03907    ast_variables_destroy(var);
03908 }
03909 #endif
03910 
03911 /*! 
03912  * \brief Removes the voicemail sound and information file.
03913  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03914  *
03915  * This is used by the DELETE macro when voicemails are stored on the file system.
03916  *
03917  * \return zero on success, -1 on error.
03918  */
03919 static int vm_delete(char *file)
03920 {
03921    char *txt;
03922    int txtsize = 0;
03923 
03924    txtsize = (strlen(file) + 5)*sizeof(char);
03925    txt = alloca(txtsize);
03926    /* Sprintf here would safe because we alloca'd exactly the right length,
03927     * but trying to eliminate all sprintf's anyhow
03928     */
03929    if (ast_check_realtime("voicemail_data")) {
03930       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03931    }
03932    snprintf(txt, txtsize, "%s.txt", file);
03933    unlink(txt);
03934    return ast_filedelete(file, NULL);
03935 }
03936 
03937 /*!
03938  * \brief utility used by inchar(), for base_encode()
03939  */
03940 static int inbuf(struct baseio *bio, FILE *fi)
03941 {
03942    int l;
03943 
03944    if (bio->ateof)
03945       return 0;
03946 
03947    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03948       if (ferror(fi))
03949          return -1;
03950 
03951       bio->ateof = 1;
03952       return 0;
03953    }
03954 
03955    bio->iolen= l;
03956    bio->iocp= 0;
03957 
03958    return 1;
03959 }
03960 
03961 /*!
03962  * \brief utility used by base_encode()
03963  */
03964 static int inchar(struct baseio *bio, FILE *fi)
03965 {
03966    if (bio->iocp>=bio->iolen) {
03967       if (!inbuf(bio, fi))
03968          return EOF;
03969    }
03970 
03971    return bio->iobuf[bio->iocp++];
03972 }
03973 
03974 /*!
03975  * \brief utility used by base_encode()
03976  */
03977 static int ochar(struct baseio *bio, int c, FILE *so)
03978 {
03979    if (bio->linelength >= BASELINELEN) {
03980       if (fputs(ENDL, so) == EOF) {
03981          return -1;
03982       }
03983 
03984       bio->linelength= 0;
03985    }
03986 
03987    if (putc(((unsigned char) c), so) == EOF) {
03988       return -1;
03989    }
03990 
03991    bio->linelength++;
03992 
03993    return 1;
03994 }
03995 
03996 /*!
03997  * \brief Performs a base 64 encode algorithm on the contents of a File
03998  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03999  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04000  *
04001  * 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 ?
04002  *
04003  * \return zero on success, -1 on error.
04004  */
04005 static int base_encode(char *filename, FILE *so)
04006 {
04007    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04008       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04009       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04010       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04011    int i,hiteof= 0;
04012    FILE *fi;
04013    struct baseio bio;
04014 
04015    memset(&bio, 0, sizeof(bio));
04016    bio.iocp = BASEMAXINLINE;
04017 
04018    if (!(fi = fopen(filename, "rb"))) {
04019       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04020       return -1;
04021    }
04022 
04023    while (!hiteof){
04024       unsigned char igroup[3], ogroup[4];
04025       int c,n;
04026 
04027       igroup[0]= igroup[1]= igroup[2]= 0;
04028 
04029       for (n= 0;n<3;n++) {
04030          if ((c = inchar(&bio, fi)) == EOF) {
04031             hiteof= 1;
04032             break;
04033          }
04034 
04035          igroup[n]= (unsigned char)c;
04036       }
04037 
04038       if (n> 0) {
04039          ogroup[0]= dtable[igroup[0]>>2];
04040          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
04041          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
04042          ogroup[3]= dtable[igroup[2]&0x3F];
04043 
04044          if (n<3) {
04045             ogroup[3]= '=';
04046 
04047             if (n<2)
04048                ogroup[2]= '=';
04049          }
04050 
04051          for (i= 0;i<4;i++)
04052             ochar(&bio, ogroup[i], so);
04053       }
04054    }
04055 
04056    fclose(fi);
04057 
04058    if (fputs(ENDL, so) == EOF) {
04059       return 0;
04060    }
04061 
04062    return 1;
04063 }
04064 
04065 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)
04066 {
04067    char callerid[256];
04068    char fromdir[256], fromfile[256];
04069    struct ast_config *msg_cfg;
04070    const char *origcallerid, *origtime;
04071    char origcidname[80], origcidnum[80], origdate[80];
04072    int inttime;
04073    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04074 
04075    /* Prepare variables for substitution in email body and subject */
04076    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04077    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04078    snprintf(passdata, passdatasize, "%d", msgnum);
04079    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
04080    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04081    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04082    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04083       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04084    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04085    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04086    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04087    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04088    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04089 
04090    /* Retrieve info from VM attribute file */
04091    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04092    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04093    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04094       strcat(fromfile, ".txt");
04095    }
04096    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04097       if (option_debug > 0) {
04098          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04099       }
04100       return;
04101    }
04102 
04103    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04104       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04105       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04106       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04107       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04108    }
04109 
04110    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04111       struct timeval tv = { inttime, };
04112       struct ast_tm tm;
04113       ast_localtime(&tv, &tm, NULL);
04114       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04115       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04116    }
04117    ast_config_destroy(msg_cfg);
04118 }
04119 
04120 /*!
04121  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04122  * \param from The string to work with.
04123  * \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.
04124  * 
04125  * \return The destination string with quotes wrapped on it (the to field).
04126  */
04127 static char *quote(const char *from, char *to, size_t len)
04128 {
04129    char *ptr = to;
04130    *ptr++ = '"';
04131    for (; ptr < to + len - 1; from++) {
04132       if (*from == '"')
04133          *ptr++ = '\\';
04134       else if (*from == '\0')
04135          break;
04136       *ptr++ = *from;
04137    }
04138    if (ptr < to + len - 1)
04139       *ptr++ = '"';
04140    *ptr = '\0';
04141    return to;
04142 }
04143 
04144 /*! \brief
04145  * fill in *tm for current time according to the proper timezone, if any.
04146  * Return tm so it can be used as a function argument.
04147  */
04148 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04149 {
04150    const struct vm_zone *z = NULL;
04151    struct timeval t = ast_tvnow();
04152 
04153    /* Does this user have a timezone specified? */
04154    if (!ast_strlen_zero(vmu->zonetag)) {
04155       /* Find the zone in the list */
04156       AST_LIST_LOCK(&zones);
04157       AST_LIST_TRAVERSE(&zones, z, list) {
04158          if (!strcmp(z->name, vmu->zonetag))
04159             break;
04160       }
04161       AST_LIST_UNLOCK(&zones);
04162    }
04163    ast_localtime(&t, tm, z ? z->timezone : NULL);
04164    return tm;
04165 }
04166 
04167 /*!\brief Check if the string would need encoding within the MIME standard, to
04168  * avoid confusing certain mail software that expects messages to be 7-bit
04169  * clean.
04170  */
04171 static int check_mime(const char *str)
04172 {
04173    for (; *str; str++) {
04174       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04175          return 1;
04176       }
04177    }
04178    return 0;
04179 }
04180 
04181 /*!\brief Encode a string according to the MIME rules for encoding strings
04182  * that are not 7-bit clean or contain control characters.
04183  *
04184  * Additionally, if the encoded string would exceed the MIME limit of 76
04185  * characters per line, then the encoding will be broken up into multiple
04186  * sections, separated by a space character, in order to facilitate
04187  * breaking up the associated header across multiple lines.
04188  *
04189  * \param start A string to be encoded
04190  * \param end An expandable buffer for holding the result
04191  * \param preamble The length of the first line already used for this string,
04192  * to ensure that each line maintains a maximum length of 76 chars.
04193  * \param postamble the length of any additional characters appended to the
04194  * line, used to ensure proper field wrapping.
04195  * \retval The encoded string.
04196  */
04197 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
04198 {
04199    char tmp[80];
04200    int first_section = 1;
04201    size_t endlen = 0, tmplen = 0;
04202    *end = '\0';
04203 
04204    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04205    for (; *start; start++) {
04206       int need_encoding = 0;
04207       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04208          need_encoding = 1;
04209       }
04210       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04211          (first_section && !need_encoding && preamble + tmplen > 72) ||
04212          (!first_section && need_encoding && tmplen > 70) ||
04213          (!first_section && !need_encoding && tmplen > 72)) {
04214          /* Start new line */
04215          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04216          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04217          first_section = 0;
04218       }
04219       if (need_encoding && *start == ' ') {
04220          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04221       } else if (need_encoding) {
04222          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04223       } else {
04224          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04225       }
04226    }
04227    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04228    return end;
04229 }
04230 
04231 /*!
04232  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04233  * \param p The output file to generate the email contents into.
04234  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04235  * \param vmu The voicemail user who is sending the voicemail.
04236  * \param msgnum The message index in the mailbox folder.
04237  * \param context 
04238  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04239  * \param cidnum The caller ID number.
04240  * \param cidname The caller ID name.
04241  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04242  * \param format The message sound file format. i.e. .wav
04243  * \param duration The time of the message content, in seconds.
04244  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04245  * \param chan
04246  * \param category
04247  * \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.
04248  *
04249  * 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.
04250  */
04251 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)
04252 {
04253    char date[256];
04254    char host[MAXHOSTNAMELEN] = "";
04255    char who[256];
04256    char bound[256];
04257    char dur[256];
04258    struct ast_tm tm;
04259    char enc_cidnum[256] = "", enc_cidname[256] = "";
04260    char *passdata = NULL, *passdata2;
04261    size_t len_passdata = 0, len_passdata2, tmplen;
04262    char *greeting_attachment; 
04263    char filename[256];
04264 
04265 
04266    /* One alloca for multiple fields */
04267    len_passdata2 = strlen(vmu->fullname);
04268    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04269       len_passdata2 = tmplen;
04270    }
04271    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04272       len_passdata2 = tmplen;
04273    }
04274    len_passdata2 = len_passdata2 * 3 + 200;
04275    passdata2 = alloca(len_passdata2);
04276 
04277    if (cidnum) {
04278       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04279    }
04280    if (cidname) {
04281       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04282    }
04283    gethostname(host, sizeof(host) - 1);
04284 
04285    if (strchr(srcemail, '@'))
04286       ast_copy_string(who, srcemail, sizeof(who));
04287    else 
04288       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04289    
04290    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04291    if (greeting_attachment)
04292       *greeting_attachment++ = '\0';
04293 
04294    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04295    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04296    fprintf(p, "Date: %s" ENDL, date);
04297 
04298    /* Set date format for voicemail mail */
04299    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04300 
04301    if (!ast_strlen_zero(fromstring)) {
04302       struct ast_channel *ast;
04303       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04304          char *ptr;
04305          memset(passdata2, 0, len_passdata2);
04306          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04307          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04308          len_passdata = strlen(passdata2) * 3 + 300;
04309          passdata = alloca(len_passdata);
04310          if (check_mime(passdata2)) {
04311             int first_line = 1;
04312             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04313             while ((ptr = strchr(passdata, ' '))) {
04314                *ptr = '\0';
04315                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04316                first_line = 0;
04317                passdata = ptr + 1;
04318             }
04319             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04320          } else {
04321             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04322          }
04323          ast_channel_free(ast);
04324       } else {
04325          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04326       }
04327    } else {
04328       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04329    }
04330 
04331    if (check_mime(vmu->fullname)) {
04332       int first_line = 1;
04333       char *ptr;
04334       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04335       while ((ptr = strchr(passdata2, ' '))) {
04336          *ptr = '\0';
04337          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04338          first_line = 0;
04339          passdata2 = ptr + 1;
04340       }
04341       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04342    } else {
04343       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04344    }
04345    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04346       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04347       struct ast_channel *ast;
04348       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04349          int vmlen = strlen(e_subj) * 3 + 200;
04350          /* Only allocate more space if the previous was not large enough */
04351          if (vmlen > len_passdata) {
04352             passdata = alloca(vmlen);
04353             len_passdata = vmlen;
04354          }
04355 
04356          memset(passdata, 0, len_passdata);
04357          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04358          pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
04359          if (check_mime(passdata)) {
04360             int first_line = 1;
04361             char *ptr;
04362             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04363             while ((ptr = strchr(passdata2, ' '))) {
04364                *ptr = '\0';
04365                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04366                first_line = 0;
04367                passdata2 = ptr + 1;
04368             }
04369             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04370          } else {
04371             fprintf(p, "Subject: %s" ENDL, passdata);
04372          }
04373          ast_channel_free(ast);
04374       } else {
04375          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04376       }
04377    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04378       if (ast_strlen_zero(flag)) {
04379          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04380       } else {
04381          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04382       }
04383    } else {
04384       if (ast_strlen_zero(flag)) {
04385          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04386       } else {
04387          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04388       }
04389    }
04390 
04391    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04392    if (imap) {
04393       /* additional information needed for IMAP searching */
04394       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04395       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04396       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04397       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04398 #ifdef IMAP_STORAGE
04399       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04400 #else
04401       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04402 #endif
04403       /* flag added for Urgent */
04404       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04405       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04406       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04407       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04408       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04409       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04410       if (!ast_strlen_zero(category)) {
04411          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04412       } else {
04413          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04414       }
04415       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04416       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04417       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04418    }
04419    if (!ast_strlen_zero(cidnum)) {
04420       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04421    }
04422    if (!ast_strlen_zero(cidname)) {
04423       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04424    }
04425    fprintf(p, "MIME-Version: 1.0" ENDL);
04426    if (attach_user_voicemail) {
04427       /* Something unique. */
04428       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04429 
04430       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04431       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04432       fprintf(p, "--%s" ENDL, bound);
04433    }
04434    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04435    if (emailbody || vmu->emailbody) {
04436       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04437       struct ast_channel *ast;
04438       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04439          char *passdata;
04440          int vmlen = strlen(e_body) * 3 + 200;
04441          passdata = alloca(vmlen);
04442          memset(passdata, 0, vmlen);
04443          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04444          pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
04445 #ifdef IMAP_STORAGE
04446          {
04447             /* Convert body to native line terminators for IMAP backend */
04448             char *line = passdata, *next;
04449             do {
04450                /* Terminate line before outputting it to the file */
04451                if ((next = strchr(line, '\n'))) {
04452                   *next++ = '\0';
04453                }
04454                fprintf(p, "%s" ENDL, line);
04455                line = next;
04456             } while (!ast_strlen_zero(line));
04457          }
04458 #else
04459          fprintf(p, "%s" ENDL, passdata);
04460 #endif
04461          ast_channel_free(ast);
04462       } else
04463          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04464    } else if (msgnum > -1) {
04465       if (strcmp(vmu->mailbox, mailbox)) {
04466          /* Forwarded type */
04467          struct ast_config *msg_cfg;
04468          const char *v;
04469          int inttime;
04470          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04471          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04472          /* Retrieve info from VM attribute file */
04473          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04474          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04475          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04476             strcat(fromfile, ".txt");
04477          }
04478          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04479             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04480                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04481             }
04482 
04483             /* You might be tempted to do origdate, except that a) it's in the wrong
04484              * format, and b) it's missing for IMAP recordings. */
04485             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04486                struct timeval tv = { inttime, };
04487                struct ast_tm tm;
04488                ast_localtime(&tv, &tm, NULL);
04489                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04490             }
04491             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04492                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04493                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04494                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04495                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04496                date, origcallerid, origdate);
04497             ast_config_destroy(msg_cfg);
04498          } else {
04499             goto plain_message;
04500          }
04501       } else {
04502 plain_message:
04503          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04504             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04505             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04506             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04507             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04508       }
04509    } else {
04510       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04511             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04512    }
04513 
04514    if (imap || attach_user_voicemail) {
04515       if (!ast_strlen_zero(attach2)) {
04516          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04517          ast_debug(5, "creating second attachment filename %s\n", filename);
04518          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04519          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04520          ast_debug(5, "creating attachment filename %s\n", filename);
04521          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04522       } else {
04523          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04524          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04525          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04526       }
04527    }
04528 }
04529 
04530 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)
04531 {
04532    char tmpdir[256], newtmp[256];
04533    char fname[256];
04534    char tmpcmd[256];
04535    int tmpfd = -1;
04536 
04537    /* Eww. We want formats to tell us their own MIME type */
04538    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04539 
04540    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04541       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04542       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04543       tmpfd = mkstemp(newtmp);
04544       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04545       ast_debug(3, "newtmp: %s\n", newtmp);
04546       if (tmpfd > -1) {
04547          int soxstatus;
04548          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04549          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04550             attach = newtmp;
04551             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04552          } else {
04553             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04554                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04555             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04556          }
04557       }
04558    }
04559    fprintf(p, "--%s" ENDL, bound);
04560    if (msgnum > -1)
04561       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04562    else
04563       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04564    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04565    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04566    if (msgnum > -1)
04567       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04568    else
04569       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04570    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04571    base_encode(fname, p);
04572    if (last)
04573       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04574    if (tmpfd > -1) {
04575       unlink(fname);
04576       close(tmpfd);
04577       unlink(newtmp);
04578    }
04579    return 0;
04580 }
04581 #undef ENDL
04582 
04583 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)
04584 {
04585    FILE *p=NULL;
04586    char tmp[80] = "/tmp/astmail-XXXXXX";
04587    char tmp2[256];
04588    char *stringp;
04589 
04590    if (vmu && ast_strlen_zero(vmu->email)) {
04591       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04592       return(0);
04593    }
04594 
04595    /* Mail only the first format */
04596    format = ast_strdupa(format);
04597    stringp = format;
04598    strsep(&stringp, "|");
04599 
04600    if (!strcmp(format, "wav49"))
04601       format = "WAV";
04602    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));
04603    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04604       command hangs */
04605    if ((p = vm_mkftemp(tmp)) == NULL) {
04606       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04607       return -1;
04608    } else {
04609       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04610       fclose(p);
04611       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04612       ast_safe_system(tmp2);
04613       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04614    }
04615    return 0;
04616 }
04617 
04618 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)
04619 {
04620    char date[256];
04621    char host[MAXHOSTNAMELEN] = "";
04622    char who[256];
04623    char dur[PATH_MAX];
04624    char tmp[80] = "/tmp/astmail-XXXXXX";
04625    char tmp2[PATH_MAX];
04626    struct ast_tm tm;
04627    FILE *p;
04628 
04629    if ((p = vm_mkftemp(tmp)) == NULL) {
04630       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04631       return -1;
04632    }
04633    gethostname(host, sizeof(host)-1);
04634    if (strchr(srcemail, '@'))
04635       ast_copy_string(who, srcemail, sizeof(who));
04636    else 
04637       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04638    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04639    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04640    fprintf(p, "Date: %s\n", date);
04641 
04642    if (*pagerfromstring) {
04643       struct ast_channel *ast;
04644       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04645          char *passdata;
04646          int vmlen = strlen(fromstring)*3 + 200;
04647          passdata = alloca(vmlen);
04648          memset(passdata, 0, vmlen);
04649          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04650          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04651          fprintf(p, "From: %s <%s>\n", passdata, who);
04652          ast_channel_free(ast);
04653       } else 
04654          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04655    } else
04656       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04657    fprintf(p, "To: %s\n", pager);
04658    if (pagersubject) {
04659       struct ast_channel *ast;
04660       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04661          char *passdata;
04662          int vmlen = strlen(pagersubject) * 3 + 200;
04663          passdata = alloca(vmlen);
04664          memset(passdata, 0, vmlen);
04665          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04666          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04667          fprintf(p, "Subject: %s\n\n", passdata);
04668          ast_channel_free(ast);
04669       } else
04670          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04671    } else {
04672       if (ast_strlen_zero(flag)) {
04673          fprintf(p, "Subject: New VM\n\n");
04674       } else {
04675          fprintf(p, "Subject: New %s VM\n\n", flag);
04676       }
04677    }
04678 
04679    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04680    if (pagerbody) {
04681       struct ast_channel *ast;
04682       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04683          char *passdata;
04684          int vmlen = strlen(pagerbody) * 3 + 200;
04685          passdata = alloca(vmlen);
04686          memset(passdata, 0, vmlen);
04687          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04688          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04689          fprintf(p, "%s\n", passdata);
04690          ast_channel_free(ast);
04691       } else
04692          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04693    } else {
04694       fprintf(p, "New %s long %s msg in box %s\n"
04695             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04696    }
04697    fclose(p);
04698    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04699    ast_safe_system(tmp2);
04700    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04701    return 0;
04702 }
04703 
04704 /*!
04705  * \brief Gets the current date and time, as formatted string.
04706  * \param s The buffer to hold the output formatted date.
04707  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04708  * 
04709  * The date format string used is "%a %b %e %r UTC %Y".
04710  * 
04711  * \return zero on success, -1 on error.
04712  */
04713 static int get_date(char *s, int len)
04714 {
04715    struct ast_tm tm;
04716    struct timeval t = ast_tvnow();
04717    
04718    ast_localtime(&t, &tm, "UTC");
04719 
04720    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04721 }
04722 
04723 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04724 {
04725    int res;
04726    char fn[PATH_MAX];
04727    char dest[PATH_MAX];
04728 
04729    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04730 
04731    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04732       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04733       return -1;
04734    }
04735 
04736    RETRIEVE(fn, -1, ext, context);
04737    if (ast_fileexists(fn, NULL, NULL) > 0) {
04738       res = ast_stream_and_wait(chan, fn, ecodes);
04739       if (res) {
04740          DISPOSE(fn, -1);
04741          return res;
04742       }
04743    } else {
04744       /* Dispose just in case */
04745       DISPOSE(fn, -1);
04746       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04747       if (res)
04748          return res;
04749       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04750       if (res)
04751          return res;
04752    }
04753    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04754    return res;
04755 }
04756 
04757 static void free_zone(struct vm_zone *z)
04758 {
04759    ast_free(z);
04760 }
04761 
04762 #ifdef ODBC_STORAGE
04763 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04764 {
04765    int x = -1;
04766    int res;
04767    SQLHSTMT stmt = NULL;
04768    char sql[PATH_MAX];
04769    char rowdata[20];
04770    char tmp[PATH_MAX] = "";
04771    struct odbc_obj *obj = NULL;
04772    char *context;
04773    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04774 
04775    if (newmsgs)
04776       *newmsgs = 0;
04777    if (oldmsgs)
04778       *oldmsgs = 0;
04779    if (urgentmsgs)
04780       *urgentmsgs = 0;
04781 
04782    /* If no mailbox, return immediately */
04783    if (ast_strlen_zero(mailbox))
04784       return 0;
04785 
04786    ast_copy_string(tmp, mailbox, sizeof(tmp));
04787 
04788    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04789       int u, n, o;
04790       char *next, *remaining = tmp;
04791       while ((next = strsep(&remaining, " ,"))) {
04792          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04793             return -1;
04794          }
04795          if (urgentmsgs) {
04796             *urgentmsgs += u;
04797          }
04798          if (newmsgs) {
04799             *newmsgs += n;
04800          }
04801          if (oldmsgs) {
04802             *oldmsgs += o;
04803          }
04804       }
04805       return 0;
04806    }
04807 
04808    context = strchr(tmp, '@');
04809    if (context) {
04810       *context = '\0';
04811       context++;
04812    } else
04813       context = "default";
04814 
04815    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04816       do {
04817          if (newmsgs) {
04818             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04819             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04820                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04821                break;
04822             }
04823             res = SQLFetch(stmt);
04824             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04825                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04826                break;
04827             }
04828             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04829             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04830                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04831                break;
04832             }
04833             *newmsgs = atoi(rowdata);
04834             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04835          }
04836 
04837          if (oldmsgs) {
04838             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04839             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04840                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04841                break;
04842             }
04843             res = SQLFetch(stmt);
04844             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04845                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04846                break;
04847             }
04848             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04849             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04850                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04851                break;
04852             }
04853             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04854             *oldmsgs = atoi(rowdata);
04855          }
04856 
04857          if (urgentmsgs) {
04858             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04859             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04860                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04861                break;
04862             }
04863             res = SQLFetch(stmt);
04864             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04865                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04866                break;
04867             }
04868             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04869             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04870                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04871                break;
04872             }
04873             *urgentmsgs = atoi(rowdata);
04874          }
04875 
04876          x = 0;
04877       } while (0);
04878    } else {
04879       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04880    }
04881 
04882    if (stmt) {
04883       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04884    }
04885    if (obj) {
04886       ast_odbc_release_obj(obj);
04887    }
04888 
04889    return x;
04890 }
04891 
04892 /*!
04893  * \brief Gets the number of messages that exist in a mailbox folder.
04894  * \param context
04895  * \param mailbox
04896  * \param folder
04897  * 
04898  * This method is used when ODBC backend is used.
04899  * \return The number of messages in this mailbox folder (zero or more).
04900  */
04901 static int messagecount(const char *context, const char *mailbox, const char *folder)
04902 {
04903    struct odbc_obj *obj = NULL;
04904    int nummsgs = 0;
04905    int res;
04906    SQLHSTMT stmt = NULL;
04907    char sql[PATH_MAX];
04908    char rowdata[20];
04909    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04910    if (!folder)
04911       folder = "INBOX";
04912    /* If no mailbox, return immediately */
04913    if (ast_strlen_zero(mailbox))
04914       return 0;
04915 
04916    obj = ast_odbc_request_obj(odbc_database, 0);
04917    if (obj) {
04918       if (!strcmp(folder, "INBOX")) {
04919          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);
04920       } else {
04921          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04922       }
04923       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04924       if (!stmt) {
04925          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04926          goto yuck;
04927       }
04928       res = SQLFetch(stmt);
04929       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04930          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04931          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04932          goto yuck;
04933       }
04934       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04935       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04936          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04937          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04938          goto yuck;
04939       }
04940       nummsgs = atoi(rowdata);
04941       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04942    } else
04943       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04944 
04945 yuck:
04946    if (obj)
04947       ast_odbc_release_obj(obj);
04948    return nummsgs;
04949 }
04950 
04951 /** 
04952  * \brief Determines if the given folder has messages.
04953  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04954  * 
04955  * This function is used when the mailbox is stored in an ODBC back end.
04956  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04957  * \return 1 if the folder has one or more messages. zero otherwise.
04958  */
04959 static int has_voicemail(const char *mailbox, const char *folder)
04960 {
04961    char tmp[256], *tmp2 = tmp, *box, *context;
04962    ast_copy_string(tmp, mailbox, sizeof(tmp));
04963    while ((context = box = strsep(&tmp2, ",&"))) {
04964       strsep(&context, "@");
04965       if (ast_strlen_zero(context))
04966          context = "default";
04967       if (messagecount(context, box, folder))
04968          return 1;
04969    }
04970    return 0;
04971 }
04972 #endif
04973 #ifndef IMAP_STORAGE
04974 /*! 
04975  * \brief Copies a message from one mailbox to another.
04976  * \param chan
04977  * \param vmu
04978  * \param imbox
04979  * \param msgnum
04980  * \param duration
04981  * \param recip
04982  * \param fmt
04983  * \param dir
04984  *
04985  * This is only used by file storage based mailboxes.
04986  *
04987  * \return zero on success, -1 on error.
04988  */
04989 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)
04990 {
04991    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04992    const char *frombox = mbox(imbox);
04993    int recipmsgnum;
04994    int res = 0;
04995 
04996    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04997 
04998    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04999       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05000    } else {
05001       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05002    }
05003    
05004    if (!dir)
05005       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05006    else
05007       ast_copy_string(fromdir, dir, sizeof(fromdir));
05008 
05009    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05010    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05011 
05012    if (vm_lock_path(todir))
05013       return ERROR_LOCK_PATH;
05014 
05015    recipmsgnum = last_message_index(recip, todir) + 1;
05016    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05017       make_file(topath, sizeof(topath), todir, recipmsgnum);
05018 #ifndef ODBC_STORAGE
05019       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05020          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05021       } else {
05022 #endif
05023          /* If we are prepending a message for ODBC, then the message already
05024           * exists in the database, but we want to force copying from the
05025           * filesystem (since only the FS contains the prepend). */
05026          copy_plain_file(frompath, topath);
05027          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05028          vm_delete(topath);
05029 #ifndef ODBC_STORAGE
05030       }
05031 #endif
05032    } else {
05033       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05034       res = -1;
05035    }
05036    ast_unlock_path(todir);
05037    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05038    
05039    return res;
05040 }
05041 #endif
05042 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05043 
05044 static int messagecount(const char *context, const char *mailbox, const char *folder)
05045 {
05046    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05047 }
05048 
05049 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05050 {
05051    DIR *dir;
05052    struct dirent *de;
05053    char fn[256];
05054    int ret = 0;
05055 
05056    /* If no mailbox, return immediately */
05057    if (ast_strlen_zero(mailbox))
05058       return 0;
05059 
05060    if (ast_strlen_zero(folder))
05061       folder = "INBOX";
05062    if (ast_strlen_zero(context))
05063       context = "default";
05064 
05065    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05066 
05067    if (!(dir = opendir(fn)))
05068       return 0;
05069 
05070    while ((de = readdir(dir))) {
05071       if (!strncasecmp(de->d_name, "msg", 3)) {
05072          if (shortcircuit) {
05073             ret = 1;
05074             break;
05075          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05076             ret++;
05077          }
05078       }
05079    }
05080 
05081    closedir(dir);
05082 
05083    return ret;
05084 }
05085 
05086 /** 
05087  * \brief Determines if the given folder has messages.
05088  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05089  * \param folder the folder to look in
05090  *
05091  * This function is used when the mailbox is stored in a filesystem back end.
05092  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05093  * \return 1 if the folder has one or more messages. zero otherwise.
05094  */
05095 static int has_voicemail(const char *mailbox, const char *folder)
05096 {
05097    char tmp[256], *tmp2 = tmp, *box, *context;
05098    ast_copy_string(tmp, mailbox, sizeof(tmp));
05099    if (ast_strlen_zero(folder)) {
05100       folder = "INBOX";
05101    }
05102    while ((box = strsep(&tmp2, ",&"))) {
05103       if ((context = strchr(box, '@')))
05104          *context++ = '\0';
05105       else
05106          context = "default";
05107       if (__has_voicemail(context, box, folder, 1))
05108          return 1;
05109       /* If we are checking INBOX, we should check Urgent as well */
05110       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05111          return 1;
05112       }
05113    }
05114    return 0;
05115 }
05116 
05117 
05118 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05119 {
05120    char tmp[256];
05121    char *context;
05122 
05123    /* If no mailbox, return immediately */
05124    if (ast_strlen_zero(mailbox))
05125       return 0;
05126 
05127    if (newmsgs)
05128       *newmsgs = 0;
05129    if (oldmsgs)
05130       *oldmsgs = 0;
05131    if (urgentmsgs)
05132       *urgentmsgs = 0;
05133 
05134    if (strchr(mailbox, ',')) {
05135       int tmpnew, tmpold, tmpurgent;
05136       char *mb, *cur;
05137 
05138       ast_copy_string(tmp, mailbox, sizeof(tmp));
05139       mb = tmp;
05140       while ((cur = strsep(&mb, ", "))) {
05141          if (!ast_strlen_zero(cur)) {
05142             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05143                return -1;
05144             else {
05145                if (newmsgs)
05146                   *newmsgs += tmpnew; 
05147                if (oldmsgs)
05148                   *oldmsgs += tmpold;
05149                if (urgentmsgs)
05150                   *urgentmsgs += tmpurgent;
05151             }
05152          }
05153       }
05154       return 0;
05155    }
05156 
05157    ast_copy_string(tmp, mailbox, sizeof(tmp));
05158    
05159    if ((context = strchr(tmp, '@')))
05160       *context++ = '\0';
05161    else
05162       context = "default";
05163 
05164    if (newmsgs)
05165       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05166    if (oldmsgs)
05167       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05168    if (urgentmsgs)
05169       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05170 
05171    return 0;
05172 }
05173 
05174 #endif
05175 
05176 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05177 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05178 {
05179    int urgentmsgs = 0;
05180    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05181    if (newmsgs) {
05182       *newmsgs += urgentmsgs;
05183    }
05184    return res;
05185 }
05186 
05187 static void run_externnotify(char *context, char *extension, const char *flag)
05188 {
05189    char arguments[255];
05190    char ext_context[256] = "";
05191    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05192    struct ast_smdi_mwi_message *mwi_msg;
05193 
05194    if (!ast_strlen_zero(context))
05195       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05196    else
05197       ast_copy_string(ext_context, extension, sizeof(ext_context));
05198 
05199    if (smdi_iface) {
05200       if (ast_app_has_voicemail(ext_context, NULL)) 
05201          ast_smdi_mwi_set(smdi_iface, extension);
05202       else
05203          ast_smdi_mwi_unset(smdi_iface, extension);
05204 
05205       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05206          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05207          if (!strncmp(mwi_msg->cause, "INV", 3))
05208             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05209          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05210             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05211          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05212          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05213       } else {
05214          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05215       }
05216    }
05217 
05218    if (!ast_strlen_zero(externnotify)) {
05219       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05220          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05221       } else {
05222          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05223          ast_debug(1, "Executing %s\n", arguments);
05224          ast_safe_system(arguments);
05225       }
05226    }
05227 }
05228 
05229 /*!
05230  * \brief Variables used for saving a voicemail.
05231  *
05232  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05233  */
05234 struct leave_vm_options {
05235    unsigned int flags;
05236    signed char record_gain;
05237    char *exitcontext;
05238 };
05239 
05240 /*!
05241  * \brief Prompts the user and records a voicemail to a mailbox.
05242  * \param chan
05243  * \param ext
05244  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05245  * 
05246  * 
05247  * 
05248  * \return zero on success, -1 on error.
05249  */
05250 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05251 {
05252 #ifdef IMAP_STORAGE
05253    int newmsgs, oldmsgs;
05254 #else
05255    char urgdir[PATH_MAX];
05256 #endif
05257    char txtfile[PATH_MAX];
05258    char tmptxtfile[PATH_MAX];
05259    struct vm_state *vms = NULL;
05260    char callerid[256];
05261    FILE *txt;
05262    char date[256];
05263    int txtdes;
05264    int res = 0;
05265    int msgnum;
05266    int duration = 0;
05267    int ausemacro = 0;
05268    int ousemacro = 0;
05269    int ouseexten = 0;
05270    char tmpdur[16];
05271    char priority[16];
05272    char origtime[16];
05273    char dir[PATH_MAX];
05274    char tmpdir[PATH_MAX];
05275    char fn[PATH_MAX];
05276    char prefile[PATH_MAX] = "";
05277    char tempfile[PATH_MAX] = "";
05278    char ext_context[256] = "";
05279    char fmt[80];
05280    char *context;
05281    char ecodes[17] = "#";
05282    struct ast_str *tmp = ast_str_create(16);
05283    char *tmpptr;
05284    struct ast_vm_user *vmu;
05285    struct ast_vm_user svm;
05286    const char *category = NULL;
05287    const char *code;
05288    const char *alldtmf = "0123456789ABCD*#";
05289    char flag[80];
05290 
05291    ast_str_set(&tmp, 0, "%s", ext);
05292    ext = ast_str_buffer(tmp);
05293    if ((context = strchr(ext, '@'))) {
05294       *context++ = '\0';
05295       tmpptr = strchr(context, '&');
05296    } else {
05297       tmpptr = strchr(ext, '&');
05298    }
05299 
05300    if (tmpptr)
05301       *tmpptr++ = '\0';
05302 
05303    ast_channel_lock(chan);
05304    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05305       category = ast_strdupa(category);
05306    }
05307    ast_channel_unlock(chan);
05308 
05309    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05310       ast_copy_string(flag, "Urgent", sizeof(flag));
05311    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05312       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05313    } else {
05314       flag[0] = '\0';
05315    }
05316 
05317    ast_debug(3, "Before find_user\n");
05318    if (!(vmu = find_user(&svm, context, ext))) {
05319       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05320       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05321       ast_free(tmp);
05322       return res;
05323    }
05324    /* Setup pre-file if appropriate */
05325    if (strcmp(vmu->context, "default"))
05326       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05327    else
05328       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05329 
05330    /* Set the path to the prefile. Will be one of 
05331       VM_SPOOL_DIRcontext/ext/busy
05332       VM_SPOOL_DIRcontext/ext/unavail
05333       Depending on the flag set in options.
05334    */
05335    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05336       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05337    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05338       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05339    }
05340    /* Set the path to the tmpfile as
05341       VM_SPOOL_DIR/context/ext/temp
05342       and attempt to create the folder structure.
05343    */
05344    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05345    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05346       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05347       ast_free(tmp);
05348       return -1;
05349    }
05350    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05351    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05352       ast_copy_string(prefile, tempfile, sizeof(prefile));
05353 
05354    DISPOSE(tempfile, -1);
05355    /* It's easier just to try to make it than to check for its existence */
05356 #ifndef IMAP_STORAGE
05357    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05358 #else
05359    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05360    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05361       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05362    }
05363 #endif
05364 
05365    /* Check current or macro-calling context for special extensions */
05366    if (ast_test_flag(vmu, VM_OPERATOR)) {
05367       if (!ast_strlen_zero(vmu->exit)) {
05368          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05369             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05370             ouseexten = 1;
05371          }
05372       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05373          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05374          ouseexten = 1;
05375       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05376          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05377          ousemacro = 1;
05378       }
05379    }
05380 
05381    if (!ast_strlen_zero(vmu->exit)) {
05382       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05383          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05384    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05385       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05386    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05387       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05388       ausemacro = 1;
05389    }
05390 
05391    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05392       for (code = alldtmf; *code; code++) {
05393          char e[2] = "";
05394          e[0] = *code;
05395          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05396             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05397       }
05398    }
05399 
05400    /* Play the beginning intro if desired */
05401    if (!ast_strlen_zero(prefile)) {
05402 #ifdef ODBC_STORAGE
05403       int success = 
05404 #endif
05405          RETRIEVE(prefile, -1, ext, context);
05406       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05407          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05408             res = ast_waitstream(chan, ecodes);
05409 #ifdef ODBC_STORAGE
05410          if (success == -1) {
05411             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05412             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05413             store_file(prefile, vmu->mailbox, vmu->context, -1);
05414          }
05415 #endif
05416       } else {
05417          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05418          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05419       }
05420       DISPOSE(prefile, -1);
05421       if (res < 0) {
05422          ast_debug(1, "Hang up during prefile playback\n");
05423          free_user(vmu);
05424          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05425          ast_free(tmp);
05426          return -1;
05427       }
05428    }
05429    if (res == '#') {
05430       /* On a '#' we skip the instructions */
05431       ast_set_flag(options, OPT_SILENT);
05432       res = 0;
05433    }
05434    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05435       res = ast_stream_and_wait(chan, INTRO, ecodes);
05436       if (res == '#') {
05437          ast_set_flag(options, OPT_SILENT);
05438          res = 0;
05439       }
05440    }
05441    if (res > 0)
05442       ast_stopstream(chan);
05443    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05444     other than the operator -- an automated attendant or mailbox login for example */
05445    if (res == '*') {
05446       chan->exten[0] = 'a';
05447       chan->exten[1] = '\0';
05448       if (!ast_strlen_zero(vmu->exit)) {
05449          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05450       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05451          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05452       }
05453       chan->priority = 0;
05454       free_user(vmu);
05455       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05456       ast_free(tmp);
05457       return 0;
05458    }
05459 
05460    /* Check for a '0' here */
05461    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05462    transfer:
05463       if (ouseexten || ousemacro) {
05464          chan->exten[0] = 'o';
05465          chan->exten[1] = '\0';
05466          if (!ast_strlen_zero(vmu->exit)) {
05467             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05468          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05469             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05470          }
05471          ast_play_and_wait(chan, "transfer");
05472          chan->priority = 0;
05473          free_user(vmu);
05474          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05475       }
05476       ast_free(tmp);
05477       return OPERATOR_EXIT;
05478    }
05479 
05480    /* Allow all other digits to exit Voicemail and return to the dialplan */
05481    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05482       if (!ast_strlen_zero(options->exitcontext))
05483          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05484       free_user(vmu);
05485       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05486       ast_free(tmp);
05487       return res;
05488    }
05489 
05490    if (res < 0) {
05491       free_user(vmu);
05492       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05493       ast_free(tmp);
05494       return -1;
05495    }
05496    /* The meat of recording the message...  All the announcements and beeps have been played*/
05497    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05498    if (!ast_strlen_zero(fmt)) {
05499       msgnum = 0;
05500 
05501 #ifdef IMAP_STORAGE
05502       /* Is ext a mailbox? */
05503       /* must open stream for this user to get info! */
05504       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05505       if (res < 0) {
05506          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05507          ast_free(tmp);
05508          return -1;
05509       }
05510       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05511       /* It is possible under certain circumstances that inboxcount did not
05512        * create a vm_state when it was needed. This is a catchall which will
05513        * rarely be used.
05514        */
05515          if (!(vms = create_vm_state_from_user(vmu))) {
05516             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05517             ast_free(tmp);
05518             return -1;
05519          }
05520       }
05521       vms->newmessages++;
05522       
05523       /* here is a big difference! We add one to it later */
05524       msgnum = newmsgs + oldmsgs;
05525       ast_debug(3, "Messagecount set to %d\n",msgnum);
05526       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05527       /* set variable for compatibility */
05528       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05529 
05530       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05531          goto leave_vm_out;
05532       }
05533 #else
05534       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05535          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05536          if (!res)
05537             res = ast_waitstream(chan, "");
05538          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05539          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05540          inprocess_count(vmu->mailbox, vmu->context, -1);
05541          goto leave_vm_out;
05542       }
05543 
05544 #endif
05545       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05546       txtdes = mkstemp(tmptxtfile);
05547       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05548       if (txtdes < 0) {
05549          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05550          if (!res)
05551             res = ast_waitstream(chan, "");
05552          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05553          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05554          inprocess_count(vmu->mailbox, vmu->context, -1);
05555          goto leave_vm_out;
05556       }
05557 
05558       /* Now play the beep once we have the message number for our next message. */
05559       if (res >= 0) {
05560          /* Unless we're *really* silent, try to send the beep */
05561          res = ast_stream_and_wait(chan, "beep", "");
05562       }
05563             
05564       /* Store information in real-time storage */
05565       if (ast_check_realtime("voicemail_data")) {
05566          snprintf(priority, sizeof(priority), "%d", chan->priority);
05567          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05568          get_date(date, sizeof(date));
05569          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);
05570       }
05571 
05572       /* Store information */
05573       txt = fdopen(txtdes, "w+");
05574       if (txt) {
05575          get_date(date, sizeof(date));
05576          fprintf(txt, 
05577             ";\n"
05578             "; Message Information file\n"
05579             ";\n"
05580             "[message]\n"
05581             "origmailbox=%s\n"
05582             "context=%s\n"
05583             "macrocontext=%s\n"
05584             "exten=%s\n"
05585             "priority=%d\n"
05586             "callerchan=%s\n"
05587             "callerid=%s\n"
05588             "origdate=%s\n"
05589             "origtime=%ld\n"
05590             "category=%s\n",
05591             ext,
05592             chan->context,
05593             chan->macrocontext, 
05594             chan->exten,
05595             chan->priority,
05596             chan->name,
05597             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05598             date, (long)time(NULL),
05599             category ? category : "");
05600       } else {
05601          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05602          inprocess_count(vmu->mailbox, vmu->context, -1);
05603          if (ast_check_realtime("voicemail_data")) {
05604             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05605          }
05606          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05607          goto leave_vm_out;
05608       }
05609       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05610 
05611       if (txt) {
05612          fprintf(txt, "flag=%s\n", flag);
05613          if (duration < vmminsecs) {
05614             fclose(txt);
05615             if (option_verbose > 2) 
05616                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05617             ast_filedelete(tmptxtfile, NULL);
05618             unlink(tmptxtfile);
05619             if (ast_check_realtime("voicemail_data")) {
05620                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05621             }
05622             inprocess_count(vmu->mailbox, vmu->context, -1);
05623          } else {
05624             fprintf(txt, "duration=%d\n", duration);
05625             fclose(txt);
05626             if (vm_lock_path(dir)) {
05627                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05628                /* Delete files */
05629                ast_filedelete(tmptxtfile, NULL);
05630                unlink(tmptxtfile);
05631                inprocess_count(vmu->mailbox, vmu->context, -1);
05632             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05633                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05634                unlink(tmptxtfile);
05635                ast_unlock_path(dir);
05636                inprocess_count(vmu->mailbox, vmu->context, -1);
05637                if (ast_check_realtime("voicemail_data")) {
05638                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05639                }
05640             } else {
05641 #ifndef IMAP_STORAGE
05642                msgnum = last_message_index(vmu, dir) + 1;
05643 #endif
05644                make_file(fn, sizeof(fn), dir, msgnum);
05645 
05646                /* assign a variable with the name of the voicemail file */ 
05647 #ifndef IMAP_STORAGE
05648                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05649 #else
05650                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05651 #endif
05652 
05653                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05654                ast_filerename(tmptxtfile, fn, NULL);
05655                rename(tmptxtfile, txtfile);
05656                inprocess_count(vmu->mailbox, vmu->context, -1);
05657 
05658                /* Properly set permissions on voicemail text descriptor file.
05659                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05660                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05661                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05662 
05663                ast_unlock_path(dir);
05664                if (ast_check_realtime("voicemail_data")) {
05665                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05666                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05667                }
05668                /* We must store the file first, before copying the message, because
05669                 * ODBC storage does the entire copy with SQL.
05670                 */
05671                if (ast_fileexists(fn, NULL, NULL) > 0) {
05672                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05673                }
05674 
05675                /* Are there to be more recipients of this message? */
05676                while (tmpptr) {
05677                   struct ast_vm_user recipu, *recip;
05678                   char *exten, *cntx;
05679                
05680                   exten = strsep(&tmpptr, "&");
05681                   cntx = strchr(exten, '@');
05682                   if (cntx) {
05683                      *cntx = '\0';
05684                      cntx++;
05685                   }
05686                   if ((recip = find_user(&recipu, cntx, exten))) {
05687                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05688                      free_user(recip);
05689                   }
05690                }
05691 #ifndef IMAP_STORAGE
05692                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05693                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05694                   char sfn[PATH_MAX];
05695                   char dfn[PATH_MAX];
05696                   int x;
05697                   /* It's easier just to try to make it than to check for its existence */
05698                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05699                   x = last_message_index(vmu, urgdir) + 1;
05700                   make_file(sfn, sizeof(sfn), dir, msgnum);
05701                   make_file(dfn, sizeof(dfn), urgdir, x);
05702                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05703                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05704                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05705                   ast_copy_string(fn, dfn, sizeof(fn));
05706                   msgnum = x;
05707                }
05708 #endif
05709                /* Notification needs to happen after the copy, though. */
05710                if (ast_fileexists(fn, NULL, NULL)) {
05711 #ifdef IMAP_STORAGE
05712                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05713 #else
05714                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05715 #endif
05716                }
05717 
05718                /* Disposal needs to happen after the optional move and copy */
05719                if (ast_fileexists(fn, NULL, NULL)) {
05720                   DISPOSE(dir, msgnum);
05721                }
05722             }
05723          }
05724       } else {
05725          inprocess_count(vmu->mailbox, vmu->context, -1);
05726       }
05727       if (res == '0') {
05728          goto transfer;
05729       } else if (res > 0 && res != 't')
05730          res = 0;
05731 
05732       if (duration < vmminsecs)
05733          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05734          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05735       else
05736          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05737    } else
05738       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05739 leave_vm_out:
05740    free_user(vmu);
05741 
05742 #ifdef IMAP_STORAGE
05743    /* expunge message - use UID Expunge if supported on IMAP server*/
05744    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05745    if (expungeonhangup == 1) {
05746       ast_mutex_lock(&vms->lock);
05747 #ifdef HAVE_IMAP_TK2006
05748       if (LEVELUIDPLUS (vms->mailstream)) {
05749          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05750       } else 
05751 #endif
05752          mail_expunge(vms->mailstream);
05753       ast_mutex_unlock(&vms->lock);
05754    }
05755 #endif
05756 
05757    ast_free(tmp);
05758    return res;
05759 }
05760 
05761 #if !defined(IMAP_STORAGE) && !defined(ODBC_STORAGE)
05762 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
05763 {
05764     /* we know the actual number of messages, so stop process when number is hit */
05765 
05766     int x, dest;
05767     char sfn[PATH_MAX];
05768     char dfn[PATH_MAX];
05769 
05770     if (vm_lock_path(dir))
05771         return ERROR_LOCK_PATH;
05772 
05773     for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
05774         make_file(sfn, sizeof(sfn), dir, x);
05775         if (EXISTS(dir, x, sfn, NULL)) {
05776 
05777             if (x != dest) {
05778                 make_file(dfn, sizeof(dfn), dir, dest);
05779                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
05780             }
05781 
05782             dest++;
05783         }
05784     }
05785     ast_unlock_path(dir);
05786 
05787     return dest;
05788 }
05789 #endif
05790 
05791 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05792 {
05793    int d;
05794    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05795    return d;
05796 }
05797 
05798 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05799 {
05800 #ifdef IMAP_STORAGE
05801    /* we must use mbox(x) folder names, and copy the message there */
05802    /* simple. huh? */
05803    char sequence[10];
05804    char mailbox[256];
05805    int res;
05806 
05807    /* get the real IMAP message number for this message */
05808    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05809    
05810    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05811    ast_mutex_lock(&vms->lock);
05812    /* if save to Old folder, put in INBOX as read */
05813    if (box == OLD_FOLDER) {
05814       mail_setflag(vms->mailstream, sequence, "\\Seen");
05815       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05816    } else if (box == NEW_FOLDER) {
05817       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05818       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05819    }
05820    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05821       ast_mutex_unlock(&vms->lock);
05822       return 0;
05823    }
05824    /* Create the folder if it don't exist */
05825    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05826    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05827    if (mail_create(vms->mailstream, mailbox) == NIL) 
05828       ast_debug(5, "Folder exists.\n");
05829    else
05830       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05831    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05832    ast_mutex_unlock(&vms->lock);
05833    return res;
05834 #else
05835    char *dir = vms->curdir;
05836    char *username = vms->username;
05837    char *context = vmu->context;
05838    char sfn[PATH_MAX];
05839    char dfn[PATH_MAX];
05840    char ddir[PATH_MAX];
05841    const char *dbox = mbox(box);
05842    int x, i;
05843    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05844 
05845    if (vm_lock_path(ddir))
05846       return ERROR_LOCK_PATH;
05847 
05848    x = last_message_index(vmu, ddir) + 1;
05849 
05850    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05851       x--;
05852       for (i = 1; i <= x; i++) {
05853          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05854          make_file(sfn, sizeof(sfn), ddir, i);
05855          make_file(dfn, sizeof(dfn), ddir, i - 1);
05856          if (EXISTS(ddir, i, sfn, NULL)) {
05857             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05858          } else
05859             break;
05860       }
05861    } else {
05862       if (x >= vmu->maxmsg) {
05863          ast_unlock_path(ddir);
05864          return -1;
05865       }
05866    }
05867    make_file(sfn, sizeof(sfn), dir, msg);
05868    make_file(dfn, sizeof(dfn), ddir, x);
05869    if (strcmp(sfn, dfn)) {
05870       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05871    }
05872    ast_unlock_path(ddir);
05873 #endif
05874    return 0;
05875 }
05876 
05877 static int adsi_logo(unsigned char *buf)
05878 {
05879    int bytes = 0;
05880    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05881    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05882    return bytes;
05883 }
05884 
05885 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05886 {
05887    unsigned char buf[256];
05888    int bytes=0;
05889    int x;
05890    char num[5];
05891 
05892    *useadsi = 0;
05893    bytes += ast_adsi_data_mode(buf + bytes);
05894    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05895 
05896    bytes = 0;
05897    bytes += adsi_logo(buf);
05898    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05899 #ifdef DISPLAY
05900    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05901 #endif
05902    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05903    bytes += ast_adsi_data_mode(buf + bytes);
05904    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05905 
05906    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05907       bytes = 0;
05908       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05909       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05910       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05911       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05912       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05913       return 0;
05914    }
05915 
05916 #ifdef DISPLAY
05917    /* Add a dot */
05918    bytes = 0;
05919    bytes += ast_adsi_logo(buf);
05920    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05921    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05922    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05923    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05924 #endif
05925    bytes = 0;
05926    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05927    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05928    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05929    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05930    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05931    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05932    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05933 
05934 #ifdef DISPLAY
05935    /* Add another dot */
05936    bytes = 0;
05937    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05938    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05939 
05940    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05941    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05942 #endif
05943 
05944    bytes = 0;
05945    /* These buttons we load but don't use yet */
05946    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05947    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05948    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05949    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05950    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05951    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05952    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05953 
05954 #ifdef DISPLAY
05955    /* Add another dot */
05956    bytes = 0;
05957    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05958    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05959    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05960 #endif
05961 
05962    bytes = 0;
05963    for (x=0;x<5;x++) {
05964       snprintf(num, sizeof(num), "%d", x);
05965       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05966    }
05967    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05968    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05969 
05970 #ifdef DISPLAY
05971    /* Add another dot */
05972    bytes = 0;
05973    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05974    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05975    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05976 #endif
05977 
05978    if (ast_adsi_end_download(chan)) {
05979       bytes = 0;
05980       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05981       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05982       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05983       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05984       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05985       return 0;
05986    }
05987    bytes = 0;
05988    bytes += ast_adsi_download_disconnect(buf + bytes);
05989    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05990    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05991 
05992    ast_debug(1, "Done downloading scripts...\n");
05993 
05994 #ifdef DISPLAY
05995    /* Add last dot */
05996    bytes = 0;
05997    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05998    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05999 #endif
06000    ast_debug(1, "Restarting session...\n");
06001 
06002    bytes = 0;
06003    /* Load the session now */
06004    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06005       *useadsi = 1;
06006       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06007    } else
06008       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06009 
06010    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06011    return 0;
06012 }
06013 
06014 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06015 {
06016    int x;
06017    if (!ast_adsi_available(chan))
06018       return;
06019    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06020    if (x < 0)
06021       return;
06022    if (!x) {
06023       if (adsi_load_vmail(chan, useadsi)) {
06024          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06025          return;
06026       }
06027    } else
06028       *useadsi = 1;
06029 }
06030 
06031 static void adsi_login(struct ast_channel *chan)
06032 {
06033    unsigned char buf[256];
06034    int bytes=0;
06035    unsigned char keys[8];
06036    int x;
06037    if (!ast_adsi_available(chan))
06038       return;
06039 
06040    for (x=0;x<8;x++)
06041       keys[x] = 0;
06042    /* Set one key for next */
06043    keys[3] = ADSI_KEY_APPS + 3;
06044 
06045    bytes += adsi_logo(buf + bytes);
06046    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06047    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06048    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06049    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06050    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06051    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06052    bytes += ast_adsi_set_keys(buf + bytes, keys);
06053    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06054    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06055 }
06056 
06057 static void adsi_password(struct ast_channel *chan)
06058 {
06059    unsigned char buf[256];
06060    int bytes=0;
06061    unsigned char keys[8];
06062    int x;
06063    if (!ast_adsi_available(chan))
06064       return;
06065 
06066    for (x=0;x<8;x++)
06067       keys[x] = 0;
06068    /* Set one key for next */
06069    keys[3] = ADSI_KEY_APPS + 3;
06070 
06071    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06072    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06073    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06074    bytes += ast_adsi_set_keys(buf + bytes, keys);
06075    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06076    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06077 }
06078 
06079 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06080 {
06081    unsigned char buf[256];
06082    int bytes=0;
06083    unsigned char keys[8];
06084    int x,y;
06085 
06086    if (!ast_adsi_available(chan))
06087       return;
06088 
06089    for (x=0;x<5;x++) {
06090       y = ADSI_KEY_APPS + 12 + start + x;
06091       if (y > ADSI_KEY_APPS + 12 + 4)
06092          y = 0;
06093       keys[x] = ADSI_KEY_SKT | y;
06094    }
06095    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06096    keys[6] = 0;
06097    keys[7] = 0;
06098 
06099    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06100    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06101    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06102    bytes += ast_adsi_set_keys(buf + bytes, keys);
06103    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06104 
06105    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06106 }
06107 
06108 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06109 {
06110    int bytes=0;
06111    unsigned char buf[256]; 
06112    char buf1[256], buf2[256];
06113    char fn2[PATH_MAX];
06114 
06115    char cid[256]="";
06116    char *val;
06117    char *name, *num;
06118    char datetime[21]="";
06119    FILE *f;
06120 
06121    unsigned char keys[8];
06122 
06123    int x;
06124 
06125    if (!ast_adsi_available(chan))
06126       return;
06127 
06128    /* Retrieve important info */
06129    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06130    f = fopen(fn2, "r");
06131    if (f) {
06132       while (!feof(f)) {   
06133          if (!fgets((char *)buf, sizeof(buf), f)) {
06134             continue;
06135          }
06136          if (!feof(f)) {
06137             char *stringp=NULL;
06138             stringp = (char *)buf;
06139             strsep(&stringp, "=");
06140             val = strsep(&stringp, "=");
06141             if (!ast_strlen_zero(val)) {
06142                if (!strcmp((char *)buf, "callerid"))
06143                   ast_copy_string(cid, val, sizeof(cid));
06144                if (!strcmp((char *)buf, "origdate"))
06145                   ast_copy_string(datetime, val, sizeof(datetime));
06146             }
06147          }
06148       }
06149       fclose(f);
06150    }
06151    /* New meaning for keys */
06152    for (x=0;x<5;x++)
06153       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06154    keys[6] = 0x0;
06155    keys[7] = 0x0;
06156 
06157    if (!vms->curmsg) {
06158       /* No prev key, provide "Folder" instead */
06159       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06160    }
06161    if (vms->curmsg >= vms->lastmsg) {
06162       /* If last message ... */
06163       if (vms->curmsg) {
06164          /* but not only message, provide "Folder" instead */
06165          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06166          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06167 
06168       } else {
06169          /* Otherwise if only message, leave blank */
06170          keys[3] = 1;
06171       }
06172    }
06173 
06174    if (!ast_strlen_zero(cid)) {
06175       ast_callerid_parse(cid, &name, &num);
06176       if (!name)
06177          name = num;
06178    } else
06179       name = "Unknown Caller";
06180 
06181    /* If deleted, show "undeleted" */
06182 
06183    if (vms->deleted[vms->curmsg])
06184       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06185 
06186    /* Except "Exit" */
06187    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06188    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06189       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06190    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06191 
06192    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06193    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06194    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06195    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06196    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06197    bytes += ast_adsi_set_keys(buf + bytes, keys);
06198    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06199 
06200    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06201 }
06202 
06203 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06204 {
06205    int bytes=0;
06206    unsigned char buf[256];
06207    unsigned char keys[8];
06208 
06209    int x;
06210 
06211    if (!ast_adsi_available(chan))
06212       return;
06213 
06214    /* New meaning for keys */
06215    for (x=0;x<5;x++)
06216       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06217 
06218    keys[6] = 0x0;
06219    keys[7] = 0x0;
06220 
06221    if (!vms->curmsg) {
06222       /* No prev key, provide "Folder" instead */
06223       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06224    }
06225    if (vms->curmsg >= vms->lastmsg) {
06226       /* If last message ... */
06227       if (vms->curmsg) {
06228          /* but not only message, provide "Folder" instead */
06229          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06230       } else {
06231          /* Otherwise if only message, leave blank */
06232          keys[3] = 1;
06233       }
06234    }
06235 
06236    /* If deleted, show "undeleted" */
06237    if (vms->deleted[vms->curmsg]) 
06238       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06239 
06240    /* Except "Exit" */
06241    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06242    bytes += ast_adsi_set_keys(buf + bytes, keys);
06243    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06244 
06245    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06246 }
06247 
06248 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06249 {
06250    unsigned char buf[256] = "";
06251    char buf1[256] = "", buf2[256] = "";
06252    int bytes=0;
06253    unsigned char keys[8];
06254    int x;
06255 
06256    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06257    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06258    if (!ast_adsi_available(chan))
06259       return;
06260    if (vms->newmessages) {
06261       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06262       if (vms->oldmessages) {
06263          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06264          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06265       } else {
06266          snprintf(buf2, sizeof(buf2), "%s.", newm);
06267       }
06268    } else if (vms->oldmessages) {
06269       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06270       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06271    } else {
06272       strcpy(buf1, "You have no messages.");
06273       buf2[0] = ' ';
06274       buf2[1] = '\0';
06275    }
06276    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06277    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06278    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06279 
06280    for (x=0;x<6;x++)
06281       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06282    keys[6] = 0;
06283    keys[7] = 0;
06284 
06285    /* Don't let them listen if there are none */
06286    if (vms->lastmsg < 0)
06287       keys[0] = 1;
06288    bytes += ast_adsi_set_keys(buf + bytes, keys);
06289 
06290    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06291 
06292    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06293 }
06294 
06295 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06296 {
06297    unsigned char buf[256] = "";
06298    char buf1[256] = "", buf2[256] = "";
06299    int bytes=0;
06300    unsigned char keys[8];
06301    int x;
06302 
06303    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06304 
06305    if (!ast_adsi_available(chan))
06306       return;
06307 
06308    /* Original command keys */
06309    for (x=0;x<6;x++)
06310       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06311 
06312    keys[6] = 0;
06313    keys[7] = 0;
06314 
06315    if ((vms->lastmsg + 1) < 1)
06316       keys[0] = 0;
06317 
06318    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06319       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06320 
06321    if (vms->lastmsg + 1)
06322       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06323    else
06324       strcpy(buf2, "no messages.");
06325    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06326    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06327    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06328    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06329    bytes += ast_adsi_set_keys(buf + bytes, keys);
06330 
06331    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06332 
06333    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06334    
06335 }
06336 
06337 /*
06338 static void adsi_clear(struct ast_channel *chan)
06339 {
06340    char buf[256];
06341    int bytes=0;
06342    if (!ast_adsi_available(chan))
06343       return;
06344    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06345    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06346 
06347    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06348 }
06349 */
06350 
06351 static void adsi_goodbye(struct ast_channel *chan)
06352 {
06353    unsigned char buf[256];
06354    int bytes=0;
06355 
06356    if (!ast_adsi_available(chan))
06357       return;
06358    bytes += adsi_logo(buf + bytes);
06359    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06360    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06361    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06362    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06363 
06364    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06365 }
06366 
06367 /*!\brief get_folder: Folder menu
06368  * Plays "press 1 for INBOX messages" etc.
06369  * Should possibly be internationalized
06370  */
06371 static int get_folder(struct ast_channel *chan, int start)
06372 {
06373    int x;
06374    int d;
06375    char fn[PATH_MAX];
06376    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06377    if (d)
06378       return d;
06379    for (x = start; x< 5; x++) {  /* For all folders */
06380       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06381          return d;
06382       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06383       if (d)
06384          return d;
06385       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06386       d = vm_play_folder_name(chan, fn);
06387       if (d)
06388          return d;
06389       d = ast_waitfordigit(chan, 500);
06390       if (d)
06391          return d;
06392    }
06393    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06394    if (d)
06395       return d;
06396    d = ast_waitfordigit(chan, 4000);
06397    return d;
06398 }
06399 
06400 /*!
06401  * \brief plays a prompt and waits for a keypress.
06402  * \param chan
06403  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06404  * \param start Does not appear to be used at this time.
06405  *
06406  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06407  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06408  * prompting for the number inputs that correspond to the available folders.
06409  * 
06410  * \return zero on success, or -1 on error.
06411  */
06412 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06413 {
06414    int res = 0;
06415    int loops = 0;
06416    res = ast_play_and_wait(chan, fn);  /* Folder name */
06417    while (((res < '0') || (res > '9')) &&
06418          (res != '#') && (res >= 0) &&
06419          loops < 4) {
06420       res = get_folder(chan, 0);
06421       loops++;
06422    }
06423    if (loops == 4) { /* give up */
06424       return '#';
06425    }
06426    return res;
06427 }
06428 
06429 /*!
06430  * \brief presents the option to prepend to an existing message when forwarding it.
06431  * \param chan
06432  * \param vmu
06433  * \param curdir
06434  * \param curmsg
06435  * \param vmfmts
06436  * \param context
06437  * \param record_gain
06438  * \param duration
06439  * \param vms
06440  *
06441  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06442  *
06443  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06444  * \return zero on success, -1 on error.
06445  */
06446 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06447          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06448 {
06449 #ifdef IMAP_STORAGE
06450    int res;
06451 #endif
06452    int cmd = 0;
06453    int retries = 0, prepend_duration = 0, already_recorded = 0;
06454    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06455    char textfile[PATH_MAX];
06456    struct ast_config *msg_cfg;
06457    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06458 #ifndef IMAP_STORAGE
06459    signed char zero_gain = 0;
06460 #endif
06461    const char *duration_str;
06462 
06463    /* Must always populate duration correctly */
06464    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06465    strcpy(textfile, msgfile);
06466    strcpy(backup, msgfile);
06467    strcpy(backup_textfile, msgfile);
06468    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06469    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06470    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06471 
06472    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06473       *duration = atoi(duration_str);
06474    } else {
06475       *duration = 0;
06476    }
06477 
06478    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06479       if (cmd)
06480          retries = 0;
06481       switch (cmd) {
06482       case '1': 
06483 
06484 #ifdef IMAP_STORAGE
06485          /* Record new intro file */
06486          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06487          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06488          res = ast_play_and_wait(chan, INTRO);
06489          res = ast_play_and_wait(chan, "beep");
06490          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06491          cmd = 't';
06492 #else
06493 
06494          /* prepend a message to the current message, update the metadata and return */
06495 
06496          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06497          strcpy(textfile, msgfile);
06498          strncat(textfile, ".txt", sizeof(textfile) - 1);
06499          *duration = 0;
06500 
06501          /* if we can't read the message metadata, stop now */
06502          if (!msg_cfg) {
06503             cmd = 0;
06504             break;
06505          }
06506          
06507          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06508 #ifndef IMAP_STORAGE
06509          if (already_recorded) {
06510             ast_filecopy(backup, msgfile, NULL);
06511             copy(backup_textfile, textfile);
06512          }
06513          else {
06514             ast_filecopy(msgfile, backup, NULL);
06515             copy(textfile, backup_textfile);
06516          }
06517 #endif
06518          already_recorded = 1;
06519 
06520          if (record_gain)
06521             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06522 
06523          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06524          if (cmd == 'S') {
06525             ast_filerename(backup, msgfile, NULL);
06526          }
06527 
06528          if (record_gain)
06529             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06530 
06531          
06532          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06533             *duration = atoi(duration_str);
06534 
06535          if (prepend_duration) {
06536             struct ast_category *msg_cat;
06537             /* need enough space for a maximum-length message duration */
06538             char duration_buf[12];
06539 
06540             *duration += prepend_duration;
06541             msg_cat = ast_category_get(msg_cfg, "message");
06542             snprintf(duration_buf, 11, "%ld", *duration);
06543             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06544                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06545             }
06546          }
06547 
06548 #endif
06549          break;
06550       case '2': 
06551          /* NULL out introfile so we know there is no intro! */
06552 #ifdef IMAP_STORAGE
06553          *vms->introfn = '\0';
06554 #endif
06555          cmd = 't';
06556          break;
06557       case '*':
06558          cmd = '*';
06559          break;
06560       default: 
06561          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06562             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06563          if (!cmd)
06564             cmd = ast_play_and_wait(chan,"vm-starmain");
06565             /* "press star to return to the main menu" */
06566          if (!cmd)
06567             cmd = ast_waitfordigit(chan,6000);
06568          if (!cmd)
06569             retries++;
06570          if (retries > 3)
06571             cmd = 't';
06572       }
06573    }
06574 
06575    if (msg_cfg)
06576       ast_config_destroy(msg_cfg);
06577    if (prepend_duration)
06578       *duration = prepend_duration;
06579 
06580    if (already_recorded && cmd == -1) {
06581       /* restore original message if prepention cancelled */
06582       ast_filerename(backup, msgfile, NULL);
06583       rename(backup_textfile, textfile);
06584    }
06585 
06586    if (cmd == 't' || cmd == 'S')
06587       cmd = 0;
06588    return cmd;
06589 }
06590 
06591 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06592 {
06593    struct ast_event *event;
06594    char *mailbox, *context;
06595 
06596    /* Strip off @default */
06597    context = mailbox = ast_strdupa(box);
06598    strsep(&context, "@");
06599    if (ast_strlen_zero(context))
06600       context = "default";
06601 
06602    if (!(event = ast_event_new(AST_EVENT_MWI,
06603          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06604          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06605          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06606          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06607          AST_EVENT_IE_END))) {
06608       return;
06609    }
06610 
06611    ast_event_queue_and_cache(event);
06612 }
06613 
06614 /*!
06615  * \brief Sends email notification that a user has a new voicemail waiting for them.
06616  * \param chan
06617  * \param vmu
06618  * \param vms
06619  * \param msgnum
06620  * \param duration
06621  * \param fmt
06622  * \param cidnum The Caller ID phone number value.
06623  * \param cidname The Caller ID name value.
06624  *
06625  * \return zero on success, -1 on error.
06626  */
06627 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)
06628 {
06629    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06630    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06631    const char *category;
06632    char *myserveremail = serveremail;
06633 
06634    ast_channel_lock(chan);
06635    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06636       category = ast_strdupa(category);
06637    }
06638    ast_channel_unlock(chan);
06639 
06640 #ifndef IMAP_STORAGE
06641    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06642 #else
06643    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06644 #endif
06645    make_file(fn, sizeof(fn), todir, msgnum);
06646    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06647 
06648    if (!ast_strlen_zero(vmu->attachfmt)) {
06649       if (strstr(fmt, vmu->attachfmt))
06650          fmt = vmu->attachfmt;
06651       else
06652          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);
06653    }
06654 
06655    /* Attach only the first format */
06656    fmt = ast_strdupa(fmt);
06657    stringp = fmt;
06658    strsep(&stringp, "|");
06659 
06660    if (!ast_strlen_zero(vmu->serveremail))
06661       myserveremail = vmu->serveremail;
06662 
06663    if (!ast_strlen_zero(vmu->email)) {
06664       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06665 
06666       if (attach_user_voicemail)
06667          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06668 
06669       /* XXX possible imap issue, should category be NULL XXX */
06670       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06671 
06672       if (attach_user_voicemail)
06673          DISPOSE(todir, msgnum);
06674    }
06675 
06676    if (!ast_strlen_zero(vmu->pager)) {
06677       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06678    }
06679 
06680    if (ast_test_flag(vmu, VM_DELETE))
06681       DELETE(todir, msgnum, fn, vmu);
06682 
06683    /* Leave voicemail for someone */
06684    if (ast_app_has_voicemail(ext_context, NULL)) 
06685       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06686 
06687    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06688 
06689    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);
06690    run_externnotify(vmu->context, vmu->mailbox, flag);
06691 
06692 #ifdef IMAP_STORAGE
06693    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06694    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06695       vm_imap_delete(NULL, vms->curmsg, vmu);
06696       vms->newmessages--;  /* Fix new message count */
06697    }
06698 #endif
06699 
06700    return 0;
06701 }
06702 
06703 /*!
06704  * \brief Sends a voicemail message to a mailbox recipient.
06705  * \param ast_channel
06706  * \param context
06707  * \param vms
06708  * \param sender
06709  * \param fmt
06710  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06711  *             Will be 0 when called to forward an existing message (option 8)
06712  *             Will be 1 when called to leave a message (option 3->5)
06713  * \param record_gain 
06714  *
06715  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06716  * 
06717  * When in the leave message mode (is_new_message == 1):
06718  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06719  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06720  *
06721  * When in the forward message mode (is_new_message == 0):
06722  *   - retreives the current message to be forwarded
06723  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06724  *   - determines the target mailbox and folders
06725  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06726  *
06727  * \return zero on success, -1 on error.
06728  */
06729 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)
06730 {
06731 #ifdef IMAP_STORAGE
06732    int todircount=0;
06733    struct vm_state *dstvms;
06734 #endif
06735    char username[70]="";
06736    char fn[PATH_MAX]; /* for playback of name greeting */
06737    char ecodes[16] = "#";
06738    int res = 0, cmd = 0;
06739    struct ast_vm_user *receiver = NULL, *vmtmp;
06740    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06741    char *stringp;
06742    const char *s;
06743    int saved_messages = 0;
06744    int valid_extensions = 0;
06745    char *dir;
06746    int curmsg;
06747    char urgent_str[7] = "";
06748    char tmptxtfile[PATH_MAX];
06749    int prompt_played = 0;
06750 #ifndef IMAP_STORAGE
06751    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06752 #endif
06753    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06754       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06755    }
06756 
06757    if (vms == NULL) return -1;
06758    dir = vms->curdir;
06759    curmsg = vms->curmsg;
06760 
06761    tmptxtfile[0] = '\0';
06762    while (!res && !valid_extensions) {
06763       int use_directory = 0;
06764       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06765          int done = 0;
06766          int retries = 0;
06767          cmd=0;
06768          while ((cmd >= 0) && !done ){
06769             if (cmd)
06770                retries = 0;
06771             switch (cmd) {
06772             case '1': 
06773                use_directory = 0;
06774                done = 1;
06775                break;
06776             case '2': 
06777                use_directory = 1;
06778                done=1;
06779                break;
06780             case '*': 
06781                cmd = 't';
06782                done = 1;
06783                break;
06784             default: 
06785                /* Press 1 to enter an extension press 2 to use the directory */
06786                cmd = ast_play_and_wait(chan,"vm-forward");
06787                if (!cmd)
06788                   cmd = ast_waitfordigit(chan,3000);
06789                if (!cmd)
06790                   retries++;
06791                if (retries > 3) {
06792                   cmd = 't';
06793                   done = 1;
06794                }
06795                
06796             }
06797          }
06798          if (cmd < 0 || cmd == 't')
06799             break;
06800       }
06801       
06802       if (use_directory) {
06803          /* use app_directory */
06804          
06805          char old_context[sizeof(chan->context)];
06806          char old_exten[sizeof(chan->exten)];
06807          int old_priority;
06808          struct ast_app* directory_app;
06809 
06810          directory_app = pbx_findapp("Directory");
06811          if (directory_app) {
06812             char vmcontext[256];
06813             /* make backup copies */
06814             memcpy(old_context, chan->context, sizeof(chan->context));
06815             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06816             old_priority = chan->priority;
06817             
06818             /* call the the Directory, changes the channel */
06819             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06820             res = pbx_exec(chan, directory_app, vmcontext);
06821             
06822             ast_copy_string(username, chan->exten, sizeof(username));
06823             
06824             /* restore the old context, exten, and priority */
06825             memcpy(chan->context, old_context, sizeof(chan->context));
06826             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06827             chan->priority = old_priority;
06828          } else {
06829             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06830             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06831          }
06832       } else {
06833          /* Ask for an extension */
06834          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06835          prompt_played++;
06836          if (res || prompt_played > 4)
06837             break;
06838          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06839             break;
06840       }
06841       
06842       /* start all over if no username */
06843       if (ast_strlen_zero(username))
06844          continue;
06845       stringp = username;
06846       s = strsep(&stringp, "*");
06847       /* start optimistic */
06848       valid_extensions = 1;
06849       while (s) {
06850          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06851             int oldmsgs;
06852             int newmsgs;
06853             int capacity;
06854             if (inboxcount(s, &newmsgs, &oldmsgs)) {
06855                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
06856                /* Shouldn't happen, but allow trying another extension if it does */
06857                res = ast_play_and_wait(chan, "pbx-invalid");
06858                valid_extensions = 0;
06859                break;
06860             }
06861             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
06862             if ((newmsgs + oldmsgs) >= capacity) {
06863                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
06864                res = ast_play_and_wait(chan, "vm-mailboxfull");
06865                valid_extensions = 0;
06866                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06867                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
06868                   free_user(vmtmp);
06869                }
06870                inprocess_count(receiver->mailbox, receiver->context, -1);
06871                break;
06872             }
06873             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06874          } else {
06875             /* XXX Optimization for the future.  When we encounter a single bad extension,
06876              * bailing out on all of the extensions may not be the way to go.  We should
06877              * probably just bail on that single extension, then allow the user to enter
06878              * several more. XXX
06879              */
06880             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06881                free_user(receiver);
06882             }
06883             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06884             /* "I am sorry, that's not a valid extension.  Please try again." */
06885             res = ast_play_and_wait(chan, "pbx-invalid");
06886             valid_extensions = 0;
06887             break;
06888          }
06889 
06890          /* play name if available, else play extension number */
06891          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06892          RETRIEVE(fn, -1, s, receiver->context);
06893          if (ast_fileexists(fn, NULL, NULL) > 0) {
06894             res = ast_stream_and_wait(chan, fn, ecodes);
06895             if (res) {
06896                DISPOSE(fn, -1);
06897                return res;
06898             }
06899          } else {
06900             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06901          }
06902          DISPOSE(fn, -1);
06903 
06904          s = strsep(&stringp, "*");
06905       }
06906       /* break from the loop of reading the extensions */
06907       if (valid_extensions)
06908          break;
06909    }
06910    /* check if we're clear to proceed */
06911    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06912       return res;
06913    if (is_new_message == 1) {
06914       struct leave_vm_options leave_options;
06915       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06916       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06917 
06918       /* Send VoiceMail */
06919       memset(&leave_options, 0, sizeof(leave_options));
06920       leave_options.record_gain = record_gain;
06921       cmd = leave_voicemail(chan, mailbox, &leave_options);
06922    } else {
06923       /* Forward VoiceMail */
06924       long duration = 0;
06925       struct vm_state vmstmp;
06926       int copy_msg_result = 0;
06927       memcpy(&vmstmp, vms, sizeof(vmstmp));
06928 
06929       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06930 
06931       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06932       if (!cmd) {
06933          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06934 #ifdef IMAP_STORAGE
06935             int attach_user_voicemail;
06936             char *myserveremail = serveremail;
06937             
06938             /* get destination mailbox */
06939             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06940             if (!dstvms) {
06941                dstvms = create_vm_state_from_user(vmtmp);
06942             }
06943             if (dstvms) {
06944                init_mailstream(dstvms, 0);
06945                if (!dstvms->mailstream) {
06946                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06947                } else {
06948                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06949                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06950                }
06951             } else {
06952                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06953             }
06954             if (!ast_strlen_zero(vmtmp->serveremail))
06955                myserveremail = vmtmp->serveremail;
06956             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06957             /* NULL category for IMAP storage */
06958             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);
06959 #else
06960             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06961 #endif
06962             saved_messages++;
06963             AST_LIST_REMOVE_CURRENT(list);
06964             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
06965             free_user(vmtmp);
06966             if (res)
06967                break;
06968          }
06969          AST_LIST_TRAVERSE_SAFE_END;
06970          if (saved_messages > 0 && !copy_msg_result) {
06971             /* give confirmation that the message was saved */
06972             /* commented out since we can't forward batches yet
06973             if (saved_messages == 1)
06974                res = ast_play_and_wait(chan, "vm-message");
06975             else
06976                res = ast_play_and_wait(chan, "vm-messages");
06977             if (!res)
06978                res = ast_play_and_wait(chan, "vm-saved"); */
06979 #ifdef IMAP_STORAGE
06980             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06981             if (ast_strlen_zero(vmstmp.introfn))
06982 #endif
06983             res = ast_play_and_wait(chan, "vm-msgsaved");
06984          }
06985 #ifndef IMAP_STORAGE
06986          else {
06987             /* with IMAP, mailbox full warning played by imap_check_limits */
06988             res = ast_play_and_wait(chan, "vm-mailboxfull");
06989          }
06990          /* Restore original message without prepended message if backup exists */
06991          make_file(msgfile, sizeof(msgfile), dir, curmsg);
06992          strcpy(textfile, msgfile);
06993          strcpy(backup, msgfile);
06994          strcpy(backup_textfile, msgfile);
06995          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06996          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06997          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06998          if (ast_fileexists(backup, NULL, NULL) > 0) {
06999             ast_filerename(backup, msgfile, NULL);
07000             rename(backup_textfile, textfile);
07001          }
07002 #endif
07003       }
07004       DISPOSE(dir, curmsg);
07005 #ifndef IMAP_STORAGE
07006       if (cmd) { /* assuming hangup, cleanup backup file */
07007          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07008          strcpy(textfile, msgfile);
07009          strcpy(backup_textfile, msgfile);
07010          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07011          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07012          rename(backup_textfile, textfile);
07013       }
07014 #endif
07015    }
07016 
07017    /* If anything failed above, we still have this list to free */
07018    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07019       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07020       free_user(vmtmp);
07021    }
07022    return res ? res : cmd;
07023 }
07024 
07025 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07026 {
07027    int res;
07028    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07029       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07030    return res;
07031 }
07032 
07033 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07034 {
07035    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);
07036 }
07037 
07038 static int play_message_category(struct ast_channel *chan, const char *category)
07039 {
07040    int res = 0;
07041 
07042    if (!ast_strlen_zero(category))
07043       res = ast_play_and_wait(chan, category);
07044 
07045    if (res) {
07046       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07047       res = 0;
07048    }
07049 
07050    return res;
07051 }
07052 
07053 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07054 {
07055    int res = 0;
07056    struct vm_zone *the_zone = NULL;
07057    time_t t;
07058 
07059    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07060       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07061       return 0;
07062    }
07063 
07064    /* Does this user have a timezone specified? */
07065    if (!ast_strlen_zero(vmu->zonetag)) {
07066       /* Find the zone in the list */
07067       struct vm_zone *z;
07068       AST_LIST_LOCK(&zones);
07069       AST_LIST_TRAVERSE(&zones, z, list) {
07070          if (!strcmp(z->name, vmu->zonetag)) {
07071             the_zone = z;
07072             break;
07073          }
07074       }
07075       AST_LIST_UNLOCK(&zones);
07076    }
07077 
07078 /* No internal variable parsing for now, so we'll comment it out for the time being */
07079 #if 0
07080    /* Set the DIFF_* variables */
07081    ast_localtime(&t, &time_now, NULL);
07082    tv_now = ast_tvnow();
07083    ast_localtime(&tv_now, &time_then, NULL);
07084 
07085    /* Day difference */
07086    if (time_now.tm_year == time_then.tm_year)
07087       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
07088    else
07089       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07090    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07091 
07092    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07093 #endif
07094    if (the_zone) {
07095       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07096    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07097       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07098    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07099       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07100    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07101       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);
07102    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07103       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07104    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07105       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07106    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07107       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07108    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07109       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);
07110    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07111       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07112    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07113       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07114    } else {
07115       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07116    }
07117 #if 0
07118    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07119 #endif
07120    return res;
07121 }
07122 
07123 
07124 
07125 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07126 {
07127    int res = 0;
07128    int i;
07129    char *callerid, *name;
07130    char prefile[PATH_MAX] = "";
07131    
07132 
07133    /* If voicemail cid is not enabled, or we didn't get cid or context from
07134     * the attribute file, leave now.
07135     *
07136     * TODO Still need to change this so that if this function is called by the
07137     * message envelope (and someone is explicitly requesting to hear the CID),
07138     * it does not check to see if CID is enabled in the config file.
07139     */
07140    if ((cid == NULL)||(context == NULL))
07141       return res;
07142 
07143    /* Strip off caller ID number from name */
07144    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07145    ast_callerid_parse(cid, &name, &callerid);
07146    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07147       /* Check for internal contexts and only */
07148       /* say extension when the call didn't come from an internal context in the list */
07149       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07150          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07151          if ((strcmp(cidinternalcontexts[i], context) == 0))
07152             break;
07153       }
07154       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07155          if (!res) {
07156             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07157             if (!ast_strlen_zero(prefile)) {
07158             /* See if we can find a recorded name for this person instead of their extension number */
07159                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07160                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07161                   if (!callback)
07162                      res = wait_file2(chan, vms, "vm-from");
07163                   res = ast_stream_and_wait(chan, prefile, "");
07164                } else {
07165                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07166                   /* Say "from extension" as one saying to sound smoother */
07167                   if (!callback)
07168                      res = wait_file2(chan, vms, "vm-from-extension");
07169                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07170                }
07171             }
07172          }
07173       } else if (!res) {
07174          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07175          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07176          if (!callback)
07177             res = wait_file2(chan, vms, "vm-from-phonenumber");
07178          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07179       }
07180    } else {
07181       /* Number unknown */
07182       ast_debug(1, "VM-CID: From an unknown number\n");
07183       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07184       res = wait_file2(chan, vms, "vm-unknown-caller");
07185    }
07186    return res;
07187 }
07188 
07189 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07190 {
07191    int res = 0;
07192    int durationm;
07193    int durations;
07194    /* Verify that we have a duration for the message */
07195    if (duration == NULL)
07196       return res;
07197 
07198    /* Convert from seconds to minutes */
07199    durations=atoi(duration);
07200    durationm=(durations / 60);
07201 
07202    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07203 
07204    if ((!res) && (durationm >= minduration)) {
07205       res = wait_file2(chan, vms, "vm-duration");
07206 
07207       /* POLISH syntax */
07208       if (!strncasecmp(chan->language, "pl", 2)) {
07209          div_t num = div(durationm, 10);
07210 
07211          if (durationm == 1) {
07212             res = ast_play_and_wait(chan, "digits/1z");
07213             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07214          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07215             if (num.rem == 2) {
07216                if (!num.quot) {
07217                   res = ast_play_and_wait(chan, "digits/2-ie");
07218                } else {
07219                   res = say_and_wait(chan, durationm - 2 , chan->language);
07220                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07221                }
07222             } else {
07223                res = say_and_wait(chan, durationm, chan->language);
07224             }
07225             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07226          } else {
07227             res = say_and_wait(chan, durationm, chan->language);
07228             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07229          }
07230       /* DEFAULT syntax */
07231       } else {
07232          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07233          res = wait_file2(chan, vms, "vm-minutes");
07234       }
07235    }
07236    return res;
07237 }
07238 
07239 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07240 {
07241    int res = 0;
07242    char filename[256], *cid;
07243    const char *origtime, *context, *category, *duration, *flag;
07244    struct ast_config *msg_cfg;
07245    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07246 
07247    vms->starting = 0;
07248    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07249    adsi_message(chan, vms);
07250    if (!vms->curmsg)
07251       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07252    else if (vms->curmsg == vms->lastmsg)
07253       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07254 
07255    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07256    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07257    msg_cfg = ast_config_load(filename, config_flags);
07258    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07259       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07260       return 0;
07261    }
07262    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07263 
07264    /* Play the word urgent if we are listening to urgent messages */
07265    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07266       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07267    }
07268 
07269    if (!res) {
07270       /* POLISH syntax */
07271       if (!strncasecmp(chan->language, "pl", 2)) {
07272          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07273             int ten, one;
07274             char nextmsg[256];
07275             ten = (vms->curmsg + 1) / 10;
07276             one = (vms->curmsg + 1) % 10;
07277 
07278             if (vms->curmsg < 20) {
07279                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07280                res = wait_file2(chan, vms, nextmsg);
07281             } else {
07282                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07283                res = wait_file2(chan, vms, nextmsg);
07284                if (one > 0) {
07285                   if (!res) {
07286                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07287                      res = wait_file2(chan, vms, nextmsg);
07288                   }
07289                }
07290             }
07291          }
07292          if (!res)
07293             res = wait_file2(chan, vms, "vm-message");
07294       /* HEBREW syntax */
07295       } else if (!strncasecmp(chan->language, "he", 2)) {
07296          if (!vms->curmsg) {
07297             res = wait_file2(chan, vms, "vm-message");
07298             res = wait_file2(chan, vms, "vm-first");
07299          } else if (vms->curmsg == vms->lastmsg) {
07300             res = wait_file2(chan, vms, "vm-message");
07301             res = wait_file2(chan, vms, "vm-last");
07302          } else {
07303             res = wait_file2(chan, vms, "vm-message");
07304             res = wait_file2(chan, vms, "vm-number");
07305             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07306          }
07307       } else {
07308          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07309             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07310          } else { /* DEFAULT syntax */
07311             res = wait_file2(chan, vms, "vm-message");
07312          }
07313          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07314             if (!res) {
07315                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07316             }
07317          }
07318       }
07319    }
07320 
07321    if (!msg_cfg) {
07322       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07323       return 0;
07324    }
07325 
07326    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07327       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07328       DISPOSE(vms->curdir, vms->curmsg);
07329       ast_config_destroy(msg_cfg);
07330       return 0;
07331    }
07332 
07333    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07334    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07335    category = ast_variable_retrieve(msg_cfg, "message", "category");
07336 
07337    context = ast_variable_retrieve(msg_cfg, "message", "context");
07338    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07339       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07340    if (!res) {
07341       res = play_message_category(chan, category);
07342    }
07343    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07344       res = play_message_datetime(chan, vmu, origtime, filename);
07345    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07346       res = play_message_callerid(chan, vms, cid, context, 0);
07347    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07348       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07349    /* Allow pressing '1' to skip envelope / callerid */
07350    if (res == '1')
07351       res = 0;
07352    ast_config_destroy(msg_cfg);
07353 
07354    if (!res) {
07355       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07356       vms->heard[vms->curmsg] = 1;
07357 #ifdef IMAP_STORAGE
07358       /*IMAP storage stores any prepended message from a forward
07359        * as a separate file from the rest of the message
07360        */
07361       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07362          wait_file(chan, vms, vms->introfn);
07363       }
07364 #endif
07365       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07366          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07367          res = 0;
07368       }
07369    }
07370    DISPOSE(vms->curdir, vms->curmsg);
07371    return res;
07372 }
07373 
07374 #ifdef IMAP_STORAGE
07375 static int imap_remove_file(char *dir, int msgnum)
07376 {
07377    char fn[PATH_MAX];
07378    char full_fn[PATH_MAX];
07379    char intro[PATH_MAX] = {0,};
07380    
07381    if (msgnum > -1) {
07382       make_file(fn, sizeof(fn), dir, msgnum);
07383       snprintf(intro, sizeof(intro), "%sintro", fn);
07384    } else
07385       ast_copy_string(fn, dir, sizeof(fn));
07386    
07387    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07388       ast_filedelete(fn, NULL);
07389       if (!ast_strlen_zero(intro)) {
07390          ast_filedelete(intro, NULL);
07391       }
07392       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07393       unlink(full_fn);
07394    }
07395    return 0;
07396 }
07397 
07398 
07399 
07400 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07401 {
07402    char *file, *filename;
07403    char *attachment;
07404    char arg[10];
07405    int i;
07406    BODY* body;
07407 
07408    file = strrchr(ast_strdupa(dir), '/');
07409    if (file) {
07410       *file++ = '\0';
07411    } else {
07412       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07413       return -1;
07414    }
07415 
07416    ast_mutex_lock(&vms->lock);
07417    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07418       mail_fetchstructure(vms->mailstream, i + 1, &body);
07419       /* We have the body, now we extract the file name of the first attachment. */
07420       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07421          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07422       } else {
07423          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07424          ast_mutex_unlock(&vms->lock);
07425          return -1;
07426       }
07427       filename = strsep(&attachment, ".");
07428       if (!strcmp(filename, file)) {
07429          sprintf (arg,"%d", i+1);
07430          mail_setflag (vms->mailstream,arg,"\\DELETED");
07431       }
07432    }
07433    mail_expunge(vms->mailstream);
07434    ast_mutex_unlock(&vms->lock);
07435    return 0;
07436 }
07437 
07438 #elif !defined(IMAP_STORAGE)
07439 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07440 {
07441    int count_msg, last_msg;
07442 
07443    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07444 
07445    /* Rename the member vmbox HERE so that we don't try to return before
07446     * we know what's going on.
07447     */
07448    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07449 
07450    /* Faster to make the directory than to check if it exists. */
07451    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07452 
07453    /* traverses directory using readdir (or select query for ODBC) */
07454    count_msg = count_messages(vmu, vms->curdir);
07455    if (count_msg < 0) {
07456       return count_msg;
07457    } else {
07458       vms->lastmsg = count_msg - 1;
07459    }
07460 
07461    if (vm_allocate_dh(vms, vmu, count_msg)) {
07462       return -1;
07463    }
07464 
07465    /*
07466    The following test is needed in case sequencing gets messed up.
07467    There appears to be more than one way to mess up sequence, so
07468    we will not try to find all of the root causes--just fix it when
07469    detected.
07470    */
07471 
07472    if (vm_lock_path(vms->curdir)) {
07473       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07474       return ERROR_LOCK_PATH;
07475    }
07476 
07477    /* for local storage, checks directory for messages up to maxmsg limit */
07478    last_msg = last_message_index(vmu, vms->curdir);
07479    ast_unlock_path(vms->curdir);
07480 
07481    if (last_msg < -1) {
07482       return last_msg;
07483 #ifndef ODBC_STORAGE
07484    } else if (vms->lastmsg != last_msg) {
07485       ast_log(LOG_NOTICE, "Resequencing mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07486         resequence_mailbox(vmu, vms->curdir, count_msg);
07487 #endif
07488    }
07489 
07490    return 0;
07491 }
07492 #endif
07493 
07494 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07495 {
07496    int x = 0;
07497 #ifndef IMAP_STORAGE
07498    int res = 0, nummsg;
07499    char fn2[PATH_MAX];
07500 #endif
07501 
07502    if (vms->lastmsg <= -1) {
07503       goto done;
07504    }
07505 
07506    vms->curmsg = -1;
07507 #ifndef IMAP_STORAGE
07508    /* Get the deleted messages fixed */
07509    if (vm_lock_path(vms->curdir)) {
07510       return ERROR_LOCK_PATH;
07511    }
07512 
07513    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07514    for (x = 0; x < vms->lastmsg + 1; x++) {
07515       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07516          /* Save this message.  It's not in INBOX or hasn't been heard */
07517          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07518          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07519             break;
07520          }
07521          vms->curmsg++;
07522          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07523          if (strcmp(vms->fn, fn2)) {
07524             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07525          }
07526       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07527          /* Move to old folder before deleting */
07528          res = save_to_folder(vmu, vms, x, 1);
07529          if (res == ERROR_LOCK_PATH) {
07530             /* If save failed do not delete the message */
07531             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07532             vms->deleted[x] = 0;
07533             vms->heard[x] = 0;
07534             --x;
07535          }
07536       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07537          /* Move to deleted folder */
07538          res = save_to_folder(vmu, vms, x, 10);
07539          if (res == ERROR_LOCK_PATH) {
07540             /* If save failed do not delete the message */
07541             vms->deleted[x] = 0;
07542             vms->heard[x] = 0;
07543             --x;
07544          }
07545       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07546          /* If realtime storage enabled - we should explicitly delete this message,
07547          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07548          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07549          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07550             DELETE(vms->curdir, x, vms->fn, vmu);
07551          }
07552       }
07553    }
07554 
07555    /* Delete ALL remaining messages */
07556    nummsg = x - 1;
07557    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07558       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07559       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07560          DELETE(vms->curdir, x, vms->fn, vmu);
07561       }
07562    }
07563    ast_unlock_path(vms->curdir);
07564 #else /* defined(IMAP_STORAGE) */
07565    if (vms->deleted) {
07566       /* Since we now expunge after each delete, deleting in reverse order
07567        * ensures that no reordering occurs between each step. */
07568       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07569          if (vms->deleted[x]) {
07570             ast_debug(3, "IMAP delete of %d\n", x);
07571             DELETE(vms->curdir, x, vms->fn, vmu);
07572          }
07573       }
07574    }
07575 #endif
07576 
07577 done:
07578    if (vms->deleted) {
07579       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07580    }
07581    if (vms->heard) {
07582       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07583    }
07584 
07585    return 0;
07586 }
07587 
07588 /* In Greek even though we CAN use a syntax like "friends messages"
07589  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07590  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07591  * syntax for the above three categories which is more elegant.
07592  */
07593 
07594 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07595 {
07596    int cmd;
07597    char *buf;
07598 
07599    buf = alloca(strlen(box) + 2);
07600    strcpy(buf, box);
07601    strcat(buf,"s");
07602 
07603    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07604       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07605       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07606    } else {
07607       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07608       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07609    }
07610 }
07611 
07612 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07613 {
07614    int cmd;
07615 
07616    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07617       if (!strcasecmp(box, "vm-INBOX"))
07618          cmd = ast_play_and_wait(chan, "vm-new-e");
07619       else
07620          cmd = ast_play_and_wait(chan, "vm-old-e");
07621       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07622    } else {
07623       cmd = ast_play_and_wait(chan, "vm-messages");
07624       return cmd ? cmd : ast_play_and_wait(chan, box);
07625    }
07626 }
07627 
07628 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07629 {
07630    int cmd;
07631 
07632    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07633       cmd = ast_play_and_wait(chan, "vm-messages");
07634       return cmd ? cmd : ast_play_and_wait(chan, box);
07635    } else {
07636       cmd = ast_play_and_wait(chan, box);
07637       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07638    }
07639 }
07640 
07641 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07642 {
07643    int cmd;
07644 
07645    if (  !strncasecmp(chan->language, "it", 2) ||
07646         !strncasecmp(chan->language, "es", 2) ||
07647         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07648       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07649       return cmd ? cmd : ast_play_and_wait(chan, box);
07650    } else if (!strncasecmp(chan->language, "gr", 2)) {
07651       return vm_play_folder_name_gr(chan, box);
07652    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07653       return ast_play_and_wait(chan, box);
07654    } else if (!strncasecmp(chan->language, "pl", 2)) {
07655       return vm_play_folder_name_pl(chan, box);
07656    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07657       return vm_play_folder_name_ua(chan, box);
07658    } else {  /* Default English */
07659       cmd = ast_play_and_wait(chan, box);
07660       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07661    }
07662 }
07663 
07664 /* GREEK SYNTAX
07665    In greek the plural for old/new is
07666    different so we need the following files
07667    We also need vm-denExeteMynhmata because
07668    this syntax is different.
07669 
07670    -> vm-Olds.wav : "Palia"
07671    -> vm-INBOXs.wav : "Nea"
07672    -> vm-denExeteMynhmata : "den exete mynhmata"
07673 */
07674 
07675 
07676 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07677 {
07678    int res = 0;
07679 
07680    if (vms->newmessages) {
07681       res = ast_play_and_wait(chan, "vm-youhave");
07682       if (!res) 
07683          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07684       if (!res) {
07685          if ((vms->newmessages == 1)) {
07686             res = ast_play_and_wait(chan, "vm-INBOX");
07687             if (!res)
07688                res = ast_play_and_wait(chan, "vm-message");
07689          } else {
07690             res = ast_play_and_wait(chan, "vm-INBOXs");
07691             if (!res)
07692                res = ast_play_and_wait(chan, "vm-messages");
07693          }
07694       }
07695    } else if (vms->oldmessages){
07696       res = ast_play_and_wait(chan, "vm-youhave");
07697       if (!res)
07698          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07699       if ((vms->oldmessages == 1)){
07700          res = ast_play_and_wait(chan, "vm-Old");
07701          if (!res)
07702             res = ast_play_and_wait(chan, "vm-message");
07703       } else {
07704          res = ast_play_and_wait(chan, "vm-Olds");
07705          if (!res)
07706             res = ast_play_and_wait(chan, "vm-messages");
07707       }
07708    } else if (!vms->oldmessages && !vms->newmessages) 
07709       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07710    return res;
07711 }
07712 
07713 /* Version of vm_intro() designed to work for many languages.
07714  *
07715  * It is hoped that this function can prevent the proliferation of 
07716  * language-specific vm_intro() functions and in time replace the language-
07717  * specific functions which already exist.  An examination of the language-
07718  * specific functions revealed that they all corrected the same deficiencies
07719  * in vm_intro_en() (which was the default function). Namely:
07720  *
07721  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07722  *     wording of the voicemail greeting hides this problem.  For example,
07723  *     vm-INBOX contains only the word "new".  This means that both of these
07724  *     sequences produce valid utterances:
07725  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07726  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07727  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07728  *     in many languages) the first utterance becomes "you have 1 the new message".
07729  *  2) The function contains hardcoded rules for pluralizing the word "message".
07730  *     These rules are correct for English, but not for many other languages.
07731  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07732  *     required in many languages.
07733  *  4) The gender of the word for "message" is not specified. This is a problem
07734  *     because in many languages the gender of the number in phrases such
07735  *     as "you have one new message" must match the gender of the word
07736  *     meaning "message".
07737  *
07738  * Fixing these problems for each new language has meant duplication of effort.
07739  * This new function solves the problems in the following general ways:
07740  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07741  *     and vm-Old respectively for those languages where it makes sense.
07742  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07743  *     on vm-message.
07744  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07745  *     prefix on vm-new and vm-old (none for English).
07746  *  4) Pass the gender of the language's word for "message" as an agument to
07747  *     this function which is can in turn pass on to the functions which 
07748  *     say numbers and put endings on nounds and adjectives.
07749  *
07750  * All languages require these messages:
07751  *  vm-youhave    "You have..."
07752  *  vm-and     "and"
07753  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07754  *
07755  * To use it for English, you will need these additional sound files:
07756  *  vm-new     "new"
07757  *  vm-message    "message", singular
07758  *  vm-messages      "messages", plural
07759  *
07760  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07761  *
07762  *  vm-newn    "novoye" (singular, neuter)
07763  *  vm-newx    "novikh" (counting plural form, genative plural)
07764  *  vm-message    "sobsheniye" (singular form)
07765  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07766  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07767  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07768  *  digits/2n     "dva" (neuter singular)
07769  */
07770 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07771 {
07772    int res;
07773    int lastnum = 0;
07774 
07775    res = ast_play_and_wait(chan, "vm-youhave");
07776 
07777    if (!res && vms->newmessages) {
07778       lastnum = vms->newmessages;
07779 
07780       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07781          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07782       }
07783 
07784       if (!res && vms->oldmessages) {
07785          res = ast_play_and_wait(chan, "vm-and");
07786       }
07787    }
07788 
07789    if (!res && vms->oldmessages) {
07790       lastnum = vms->oldmessages;
07791 
07792       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07793          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07794       }
07795    }
07796 
07797    if (!res) {
07798       if (lastnum == 0) {
07799          res = ast_play_and_wait(chan, "vm-no");
07800       }
07801       if (!res) {
07802          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07803       }
07804    }
07805 
07806    return res;
07807 }
07808 
07809 /* Default Hebrew syntax */
07810 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07811 {
07812    int res = 0;
07813 
07814    /* Introduce messages they have */
07815    if (!res) {
07816       if ((vms->newmessages) || (vms->oldmessages)) {
07817          res = ast_play_and_wait(chan, "vm-youhave");
07818       }
07819       /*
07820        * The word "shtei" refers to the number 2 in hebrew when performing a count
07821        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07822        * an element, this is one of them.
07823        */
07824       if (vms->newmessages) {
07825          if (!res) {
07826             if (vms->newmessages == 1) {
07827                res = ast_play_and_wait(chan, "vm-INBOX1");
07828             } else {
07829                if (vms->newmessages == 2) {
07830                   res = ast_play_and_wait(chan, "vm-shtei");
07831                } else {
07832                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07833                }
07834                res = ast_play_and_wait(chan, "vm-INBOX");
07835             }
07836          }
07837          if (vms->oldmessages && !res) {
07838             res = ast_play_and_wait(chan, "vm-and");
07839             if (vms->oldmessages == 1) {
07840                res = ast_play_and_wait(chan, "vm-Old1");
07841             } else {
07842                if (vms->oldmessages == 2) {
07843                   res = ast_play_and_wait(chan, "vm-shtei");
07844                } else {
07845                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07846                }
07847                res = ast_play_and_wait(chan, "vm-Old");
07848             }
07849          }
07850       }
07851       if (!res && vms->oldmessages && !vms->newmessages) {
07852          if (!res) {
07853             if (vms->oldmessages == 1) {
07854                res = ast_play_and_wait(chan, "vm-Old1");
07855             } else {
07856                if (vms->oldmessages == 2) {
07857                   res = ast_play_and_wait(chan, "vm-shtei");
07858                } else {
07859                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07860                }
07861                res = ast_play_and_wait(chan, "vm-Old");
07862             }
07863          }
07864       }
07865       if (!res) {
07866          if (!vms->oldmessages && !vms->newmessages) {
07867             if (!res) {
07868                res = ast_play_and_wait(chan, "vm-nomessages");
07869             }
07870          }
07871       }
07872    }
07873    return res;
07874 }
07875    
07876 /* Default English syntax */
07877 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07878 {
07879    int res;
07880 
07881    /* Introduce messages they have */
07882    res = ast_play_and_wait(chan, "vm-youhave");
07883    if (!res) {
07884       if (vms->urgentmessages) {
07885          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07886          if (!res)
07887             res = ast_play_and_wait(chan, "vm-Urgent");
07888          if ((vms->oldmessages || vms->newmessages) && !res) {
07889             res = ast_play_and_wait(chan, "vm-and");
07890          } else if (!res) {
07891             if ((vms->urgentmessages == 1))
07892                res = ast_play_and_wait(chan, "vm-message");
07893             else
07894                res = ast_play_and_wait(chan, "vm-messages");
07895          }
07896       }
07897       if (vms->newmessages) {
07898          res = say_and_wait(chan, vms->newmessages, chan->language);
07899          if (!res)
07900             res = ast_play_and_wait(chan, "vm-INBOX");
07901          if (vms->oldmessages && !res)
07902             res = ast_play_and_wait(chan, "vm-and");
07903          else if (!res) {
07904             if ((vms->newmessages == 1))
07905                res = ast_play_and_wait(chan, "vm-message");
07906             else
07907                res = ast_play_and_wait(chan, "vm-messages");
07908          }
07909             
07910       }
07911       if (!res && vms->oldmessages) {
07912          res = say_and_wait(chan, vms->oldmessages, chan->language);
07913          if (!res)
07914             res = ast_play_and_wait(chan, "vm-Old");
07915          if (!res) {
07916             if (vms->oldmessages == 1)
07917                res = ast_play_and_wait(chan, "vm-message");
07918             else
07919                res = ast_play_and_wait(chan, "vm-messages");
07920          }
07921       }
07922       if (!res) {
07923          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07924             res = ast_play_and_wait(chan, "vm-no");
07925             if (!res)
07926                res = ast_play_and_wait(chan, "vm-messages");
07927          }
07928       }
07929    }
07930    return res;
07931 }
07932 
07933 /* ITALIAN syntax */
07934 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07935 {
07936    /* Introduce messages they have */
07937    int res;
07938    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07939       res = ast_play_and_wait(chan, "vm-no") ||
07940          ast_play_and_wait(chan, "vm-message");
07941    else
07942       res = ast_play_and_wait(chan, "vm-youhave");
07943    if (!res && vms->newmessages) {
07944       res = (vms->newmessages == 1) ?
07945          ast_play_and_wait(chan, "digits/un") ||
07946          ast_play_and_wait(chan, "vm-nuovo") ||
07947          ast_play_and_wait(chan, "vm-message") :
07948          /* 2 or more new messages */
07949          say_and_wait(chan, vms->newmessages, chan->language) ||
07950          ast_play_and_wait(chan, "vm-nuovi") ||
07951          ast_play_and_wait(chan, "vm-messages");
07952       if (!res && vms->oldmessages)
07953          res = ast_play_and_wait(chan, "vm-and");
07954    }
07955    if (!res && vms->oldmessages) {
07956       res = (vms->oldmessages == 1) ?
07957          ast_play_and_wait(chan, "digits/un") ||
07958          ast_play_and_wait(chan, "vm-vecchio") ||
07959          ast_play_and_wait(chan, "vm-message") :
07960          /* 2 or more old messages */
07961          say_and_wait(chan, vms->oldmessages, chan->language) ||
07962          ast_play_and_wait(chan, "vm-vecchi") ||
07963          ast_play_and_wait(chan, "vm-messages");
07964    }
07965    return res;
07966 }
07967 
07968 /* POLISH syntax */
07969 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07970 {
07971    /* Introduce messages they have */
07972    int res;
07973    div_t num;
07974 
07975    if (!vms->oldmessages && !vms->newmessages) {
07976       res = ast_play_and_wait(chan, "vm-no");
07977       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07978       return res;
07979    } else {
07980       res = ast_play_and_wait(chan, "vm-youhave");
07981    }
07982 
07983    if (vms->newmessages) {
07984       num = div(vms->newmessages, 10);
07985       if (vms->newmessages == 1) {
07986          res = ast_play_and_wait(chan, "digits/1-a");
07987          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07988          res = res ? res : ast_play_and_wait(chan, "vm-message");
07989       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07990          if (num.rem == 2) {
07991             if (!num.quot) {
07992                res = ast_play_and_wait(chan, "digits/2-ie");
07993             } else {
07994                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07995                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07996             }
07997          } else {
07998             res = say_and_wait(chan, vms->newmessages, chan->language);
07999          }
08000          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08001          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08002       } else {
08003          res = say_and_wait(chan, vms->newmessages, chan->language);
08004          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08005          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08006       }
08007       if (!res && vms->oldmessages)
08008          res = ast_play_and_wait(chan, "vm-and");
08009    }
08010    if (!res && vms->oldmessages) {
08011       num = div(vms->oldmessages, 10);
08012       if (vms->oldmessages == 1) {
08013          res = ast_play_and_wait(chan, "digits/1-a");
08014          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08015          res = res ? res : ast_play_and_wait(chan, "vm-message");
08016       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08017          if (num.rem == 2) {
08018             if (!num.quot) {
08019                res = ast_play_and_wait(chan, "digits/2-ie");
08020             } else {
08021                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08022                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08023             }
08024          } else {
08025             res = say_and_wait(chan, vms->oldmessages, chan->language);
08026          }
08027          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08028          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08029       } else {
08030          res = say_and_wait(chan, vms->oldmessages, chan->language);
08031          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08032          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08033       }
08034    }
08035 
08036    return res;
08037 }
08038 
08039 /* SWEDISH syntax */
08040 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08041 {
08042    /* Introduce messages they have */
08043    int res;
08044 
08045    res = ast_play_and_wait(chan, "vm-youhave");
08046    if (res)
08047       return res;
08048 
08049    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08050       res = ast_play_and_wait(chan, "vm-no");
08051       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08052       return res;
08053    }
08054 
08055    if (vms->newmessages) {
08056       if ((vms->newmessages == 1)) {
08057          res = ast_play_and_wait(chan, "digits/ett");
08058          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08059          res = res ? res : ast_play_and_wait(chan, "vm-message");
08060       } else {
08061          res = say_and_wait(chan, vms->newmessages, chan->language);
08062          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08063          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08064       }
08065       if (!res && vms->oldmessages)
08066          res = ast_play_and_wait(chan, "vm-and");
08067    }
08068    if (!res && vms->oldmessages) {
08069       if (vms->oldmessages == 1) {
08070          res = ast_play_and_wait(chan, "digits/ett");
08071          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08072          res = res ? res : ast_play_and_wait(chan, "vm-message");
08073       } else {
08074          res = say_and_wait(chan, vms->oldmessages, chan->language);
08075          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08076          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08077       }
08078    }
08079 
08080    return res;
08081 }
08082 
08083 /* NORWEGIAN syntax */
08084 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
08085 {
08086    /* Introduce messages they have */
08087    int res;
08088 
08089    res = ast_play_and_wait(chan, "vm-youhave");
08090    if (res)
08091       return res;
08092 
08093    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08094       res = ast_play_and_wait(chan, "vm-no");
08095       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08096       return res;
08097    }
08098 
08099    if (vms->newmessages) {
08100       if ((vms->newmessages == 1)) {
08101          res = ast_play_and_wait(chan, "digits/1");
08102          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08103          res = res ? res : ast_play_and_wait(chan, "vm-message");
08104       } else {
08105          res = say_and_wait(chan, vms->newmessages, chan->language);
08106          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08107          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08108       }
08109       if (!res && vms->oldmessages)
08110          res = ast_play_and_wait(chan, "vm-and");
08111    }
08112    if (!res && vms->oldmessages) {
08113       if (vms->oldmessages == 1) {
08114          res = ast_play_and_wait(chan, "digits/1");
08115          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08116          res = res ? res : ast_play_and_wait(chan, "vm-message");
08117       } else {
08118          res = say_and_wait(chan, vms->oldmessages, chan->language);
08119          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08120          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08121       }
08122    }
08123 
08124    return res;
08125 }
08126 
08127 /* GERMAN syntax */
08128 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
08129 {
08130    /* Introduce messages they have */
08131    int res;
08132    res = ast_play_and_wait(chan, "vm-youhave");
08133    if (!res) {
08134       if (vms->newmessages) {
08135          if ((vms->newmessages == 1))
08136             res = ast_play_and_wait(chan, "digits/1F");
08137          else
08138             res = say_and_wait(chan, vms->newmessages, chan->language);
08139          if (!res)
08140             res = ast_play_and_wait(chan, "vm-INBOX");
08141          if (vms->oldmessages && !res)
08142             res = ast_play_and_wait(chan, "vm-and");
08143          else if (!res) {
08144             if ((vms->newmessages == 1))
08145                res = ast_play_and_wait(chan, "vm-message");
08146             else
08147                res = ast_play_and_wait(chan, "vm-messages");
08148          }
08149             
08150       }
08151       if (!res && vms->oldmessages) {
08152          if (vms->oldmessages == 1)
08153             res = ast_play_and_wait(chan, "digits/1F");
08154          else
08155             res = say_and_wait(chan, vms->oldmessages, chan->language);
08156          if (!res)
08157             res = ast_play_and_wait(chan, "vm-Old");
08158          if (!res) {
08159             if (vms->oldmessages == 1)
08160                res = ast_play_and_wait(chan, "vm-message");
08161             else
08162                res = ast_play_and_wait(chan, "vm-messages");
08163          }
08164       }
08165       if (!res) {
08166          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08167             res = ast_play_and_wait(chan, "vm-no");
08168             if (!res)
08169                res = ast_play_and_wait(chan, "vm-messages");
08170          }
08171       }
08172    }
08173    return res;
08174 }
08175 
08176 /* SPANISH syntax */
08177 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
08178 {
08179    /* Introduce messages they have */
08180    int res;
08181    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08182       res = ast_play_and_wait(chan, "vm-youhaveno");
08183       if (!res)
08184          res = ast_play_and_wait(chan, "vm-messages");
08185    } else {
08186       res = ast_play_and_wait(chan, "vm-youhave");
08187    }
08188    if (!res) {
08189       if (vms->newmessages) {
08190          if (!res) {
08191             if ((vms->newmessages == 1)) {
08192                res = ast_play_and_wait(chan, "digits/1M");
08193                if (!res)
08194                   res = ast_play_and_wait(chan, "vm-message");
08195                if (!res)
08196                   res = ast_play_and_wait(chan, "vm-INBOXs");
08197             } else {
08198                res = say_and_wait(chan, vms->newmessages, chan->language);
08199                if (!res)
08200                   res = ast_play_and_wait(chan, "vm-messages");
08201                if (!res)
08202                   res = ast_play_and_wait(chan, "vm-INBOX");
08203             }
08204          }
08205          if (vms->oldmessages && !res)
08206             res = ast_play_and_wait(chan, "vm-and");
08207       }
08208       if (vms->oldmessages) {
08209          if (!res) {
08210             if (vms->oldmessages == 1) {
08211                res = ast_play_and_wait(chan, "digits/1M");
08212                if (!res)
08213                   res = ast_play_and_wait(chan, "vm-message");
08214                if (!res)
08215                   res = ast_play_and_wait(chan, "vm-Olds");
08216             } else {
08217                res = say_and_wait(chan, vms->oldmessages, chan->language);
08218                if (!res)
08219                   res = ast_play_and_wait(chan, "vm-messages");
08220                if (!res)
08221                   res = ast_play_and_wait(chan, "vm-Old");
08222             }
08223          }
08224       }
08225    }
08226 return res;
08227 }
08228 
08229 /* BRAZILIAN PORTUGUESE syntax */
08230 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
08231    /* Introduce messages they have */
08232    int res;
08233    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08234       res = ast_play_and_wait(chan, "vm-nomessages");
08235       return res;
08236    } else {
08237       res = ast_play_and_wait(chan, "vm-youhave");
08238    }
08239    if (vms->newmessages) {
08240       if (!res)
08241          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08242       if ((vms->newmessages == 1)) {
08243          if (!res)
08244             res = ast_play_and_wait(chan, "vm-message");
08245          if (!res)
08246             res = ast_play_and_wait(chan, "vm-INBOXs");
08247       } else {
08248          if (!res)
08249             res = ast_play_and_wait(chan, "vm-messages");
08250          if (!res)
08251             res = ast_play_and_wait(chan, "vm-INBOX");
08252       }
08253       if (vms->oldmessages && !res)
08254          res = ast_play_and_wait(chan, "vm-and");
08255    }
08256    if (vms->oldmessages) {
08257       if (!res)
08258          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08259       if (vms->oldmessages == 1) {
08260          if (!res)
08261             res = ast_play_and_wait(chan, "vm-message");
08262          if (!res)
08263             res = ast_play_and_wait(chan, "vm-Olds");
08264       } else {
08265          if (!res)
08266             res = ast_play_and_wait(chan, "vm-messages");
08267          if (!res)
08268             res = ast_play_and_wait(chan, "vm-Old");
08269       }
08270    }
08271    return res;
08272 }
08273 
08274 /* FRENCH syntax */
08275 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
08276 {
08277    /* Introduce messages they have */
08278    int res;
08279    res = ast_play_and_wait(chan, "vm-youhave");
08280    if (!res) {
08281       if (vms->newmessages) {
08282          res = say_and_wait(chan, vms->newmessages, chan->language);
08283          if (!res)
08284             res = ast_play_and_wait(chan, "vm-INBOX");
08285          if (vms->oldmessages && !res)
08286             res = ast_play_and_wait(chan, "vm-and");
08287          else if (!res) {
08288             if ((vms->newmessages == 1))
08289                res = ast_play_and_wait(chan, "vm-message");
08290             else
08291                res = ast_play_and_wait(chan, "vm-messages");
08292          }
08293             
08294       }
08295       if (!res && vms->oldmessages) {
08296          res = say_and_wait(chan, vms->oldmessages, chan->language);
08297          if (!res)
08298             res = ast_play_and_wait(chan, "vm-Old");
08299          if (!res) {
08300             if (vms->oldmessages == 1)
08301                res = ast_play_and_wait(chan, "vm-message");
08302             else
08303                res = ast_play_and_wait(chan, "vm-messages");
08304          }
08305       }
08306       if (!res) {
08307          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08308             res = ast_play_and_wait(chan, "vm-no");
08309             if (!res)
08310                res = ast_play_and_wait(chan, "vm-messages");
08311          }
08312       }
08313    }
08314    return res;
08315 }
08316 
08317 /* DUTCH syntax */
08318 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
08319 {
08320    /* Introduce messages they have */
08321    int res;
08322    res = ast_play_and_wait(chan, "vm-youhave");
08323    if (!res) {
08324       if (vms->newmessages) {
08325          res = say_and_wait(chan, vms->newmessages, chan->language);
08326          if (!res) {
08327             if (vms->newmessages == 1)
08328                res = ast_play_and_wait(chan, "vm-INBOXs");
08329             else
08330                res = ast_play_and_wait(chan, "vm-INBOX");
08331          }
08332          if (vms->oldmessages && !res)
08333             res = ast_play_and_wait(chan, "vm-and");
08334          else if (!res) {
08335             if ((vms->newmessages == 1))
08336                res = ast_play_and_wait(chan, "vm-message");
08337             else
08338                res = ast_play_and_wait(chan, "vm-messages");
08339          }
08340             
08341       }
08342       if (!res && vms->oldmessages) {
08343          res = say_and_wait(chan, vms->oldmessages, chan->language);
08344          if (!res) {
08345             if (vms->oldmessages == 1)
08346                res = ast_play_and_wait(chan, "vm-Olds");
08347             else
08348                res = ast_play_and_wait(chan, "vm-Old");
08349          }
08350          if (!res) {
08351             if (vms->oldmessages == 1)
08352                res = ast_play_and_wait(chan, "vm-message");
08353             else
08354                res = ast_play_and_wait(chan, "vm-messages");
08355          }
08356       }
08357       if (!res) {
08358          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08359             res = ast_play_and_wait(chan, "vm-no");
08360             if (!res)
08361                res = ast_play_and_wait(chan, "vm-messages");
08362          }
08363       }
08364    }
08365    return res;
08366 }
08367 
08368 /* PORTUGUESE syntax */
08369 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08370 {
08371    /* Introduce messages they have */
08372    int res;
08373    res = ast_play_and_wait(chan, "vm-youhave");
08374    if (!res) {
08375       if (vms->newmessages) {
08376          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08377          if (!res) {
08378             if ((vms->newmessages == 1)) {
08379                res = ast_play_and_wait(chan, "vm-message");
08380                if (!res)
08381                   res = ast_play_and_wait(chan, "vm-INBOXs");
08382             } else {
08383                res = ast_play_and_wait(chan, "vm-messages");
08384                if (!res)
08385                   res = ast_play_and_wait(chan, "vm-INBOX");
08386             }
08387          }
08388          if (vms->oldmessages && !res)
08389             res = ast_play_and_wait(chan, "vm-and");
08390       }
08391       if (!res && vms->oldmessages) {
08392          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08393          if (!res) {
08394             if (vms->oldmessages == 1) {
08395                res = ast_play_and_wait(chan, "vm-message");
08396                if (!res)
08397                   res = ast_play_and_wait(chan, "vm-Olds");
08398             } else {
08399                res = ast_play_and_wait(chan, "vm-messages");
08400                if (!res)
08401                   res = ast_play_and_wait(chan, "vm-Old");
08402             }
08403          }
08404       }
08405       if (!res) {
08406          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08407             res = ast_play_and_wait(chan, "vm-no");
08408             if (!res)
08409                res = ast_play_and_wait(chan, "vm-messages");
08410          }
08411       }
08412    }
08413    return res;
08414 }
08415 
08416 
08417 /* CZECH syntax */
08418 /* in czech there must be declension of word new and message
08419  * czech        : english        : czech      : english
08420  * --------------------------------------------------------
08421  * vm-youhave   : you have 
08422  * vm-novou     : one new        : vm-zpravu  : message
08423  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08424  * vm-novych    : 5-infinite new : vm-zprav   : messages
08425  * vm-starou   : one old
08426  * vm-stare     : 2-4 old 
08427  * vm-starych   : 5-infinite old
08428  * jednu        : one   - falling 4. 
08429  * vm-no        : no  ( no messages )
08430  */
08431 
08432 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08433 {
08434    int res;
08435    res = ast_play_and_wait(chan, "vm-youhave");
08436    if (!res) {
08437       if (vms->newmessages) {
08438          if (vms->newmessages == 1) {
08439             res = ast_play_and_wait(chan, "digits/jednu");
08440          } else {
08441             res = say_and_wait(chan, vms->newmessages, chan->language);
08442          }
08443          if (!res) {
08444             if ((vms->newmessages == 1))
08445                res = ast_play_and_wait(chan, "vm-novou");
08446             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08447                res = ast_play_and_wait(chan, "vm-nove");
08448             if (vms->newmessages > 4)
08449                res = ast_play_and_wait(chan, "vm-novych");
08450          }
08451          if (vms->oldmessages && !res)
08452             res = ast_play_and_wait(chan, "vm-and");
08453          else if (!res) {
08454             if ((vms->newmessages == 1))
08455                res = ast_play_and_wait(chan, "vm-zpravu");
08456             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08457                res = ast_play_and_wait(chan, "vm-zpravy");
08458             if (vms->newmessages > 4)
08459                res = ast_play_and_wait(chan, "vm-zprav");
08460          }
08461       }
08462       if (!res && vms->oldmessages) {
08463          res = say_and_wait(chan, vms->oldmessages, chan->language);
08464          if (!res) {
08465             if ((vms->oldmessages == 1))
08466                res = ast_play_and_wait(chan, "vm-starou");
08467             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08468                res = ast_play_and_wait(chan, "vm-stare");
08469             if (vms->oldmessages > 4)
08470                res = ast_play_and_wait(chan, "vm-starych");
08471          }
08472          if (!res) {
08473             if ((vms->oldmessages == 1))
08474                res = ast_play_and_wait(chan, "vm-zpravu");
08475             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08476                res = ast_play_and_wait(chan, "vm-zpravy");
08477             if (vms->oldmessages > 4)
08478                res = ast_play_and_wait(chan, "vm-zprav");
08479          }
08480       }
08481       if (!res) {
08482          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08483             res = ast_play_and_wait(chan, "vm-no");
08484             if (!res)
08485                res = ast_play_and_wait(chan, "vm-zpravy");
08486          }
08487       }
08488    }
08489    return res;
08490 }
08491 
08492 /* CHINESE (Taiwan) syntax */
08493 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08494 {
08495    int res;
08496    /* Introduce messages they have */
08497    res = ast_play_and_wait(chan, "vm-you");
08498 
08499    if (!res && vms->newmessages) {
08500       res = ast_play_and_wait(chan, "vm-have");
08501       if (!res)
08502          res = say_and_wait(chan, vms->newmessages, chan->language);
08503       if (!res)
08504          res = ast_play_and_wait(chan, "vm-tong");
08505       if (!res)
08506          res = ast_play_and_wait(chan, "vm-INBOX");
08507       if (vms->oldmessages && !res)
08508          res = ast_play_and_wait(chan, "vm-and");
08509       else if (!res) 
08510          res = ast_play_and_wait(chan, "vm-messages");
08511    }
08512    if (!res && vms->oldmessages) {
08513       res = ast_play_and_wait(chan, "vm-have");
08514       if (!res)
08515          res = say_and_wait(chan, vms->oldmessages, chan->language);
08516       if (!res)
08517          res = ast_play_and_wait(chan, "vm-tong");
08518       if (!res)
08519          res = ast_play_and_wait(chan, "vm-Old");
08520       if (!res)
08521          res = ast_play_and_wait(chan, "vm-messages");
08522    }
08523    if (!res && !vms->oldmessages && !vms->newmessages) {
08524       res = ast_play_and_wait(chan, "vm-haveno");
08525       if (!res)
08526          res = ast_play_and_wait(chan, "vm-messages");
08527    }
08528    return res;
08529 }
08530 
08531 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08532 {
08533    char prefile[256];
08534    
08535    /* Notify the user that the temp greeting is set and give them the option to remove it */
08536    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08537    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08538       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08539       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08540          ast_play_and_wait(chan, "vm-tempgreetactive");
08541       }
08542       DISPOSE(prefile, -1);
08543    }
08544 
08545    /* Play voicemail intro - syntax is different for different languages */
08546    if (0) {
08547       return 0;
08548    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08549       return vm_intro_cs(chan, vms);
08550    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08551       static int deprecation_warning = 0;
08552       if (deprecation_warning++ % 10 == 0) {
08553          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08554       }
08555       return vm_intro_cs(chan, vms);
08556    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08557       return vm_intro_de(chan, vms);
08558    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08559       return vm_intro_es(chan, vms);
08560    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08561       return vm_intro_fr(chan, vms);
08562    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08563       return vm_intro_gr(chan, vms);
08564    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08565       return vm_intro_he(chan, vms);
08566    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08567       return vm_intro_it(chan, vms);
08568    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08569       return vm_intro_nl(chan, vms);
08570    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08571       return vm_intro_no(chan, vms);
08572    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08573       return vm_intro_pl(chan, vms);
08574    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08575       return vm_intro_pt_BR(chan, vms);
08576    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08577       return vm_intro_pt(chan, vms);
08578    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08579       return vm_intro_multilang(chan, vms, "n");
08580    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08581       return vm_intro_se(chan, vms);
08582    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08583       return vm_intro_multilang(chan, vms, "n");
08584    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08585       return vm_intro_zh(chan, vms);
08586    } else {                                             /* Default to ENGLISH */
08587       return vm_intro_en(chan, vms);
08588    }
08589 }
08590 
08591 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08592 {
08593    int res = 0;
08594    /* Play instructions and wait for new command */
08595    while (!res) {
08596       if (vms->starting) {
08597          if (vms->lastmsg > -1) {
08598             if (skipadvanced)
08599                res = ast_play_and_wait(chan, "vm-onefor-full");
08600             else
08601                res = ast_play_and_wait(chan, "vm-onefor");
08602             if (!res)
08603                res = vm_play_folder_name(chan, vms->vmbox);
08604          }
08605          if (!res) {
08606             if (skipadvanced)
08607                res = ast_play_and_wait(chan, "vm-opts-full");
08608             else
08609                res = ast_play_and_wait(chan, "vm-opts");
08610          }
08611       } else {
08612          /* Added for additional help */
08613          if (skipadvanced) {
08614             res = ast_play_and_wait(chan, "vm-onefor-full");
08615             if (!res)
08616                res = vm_play_folder_name(chan, vms->vmbox);
08617             res = ast_play_and_wait(chan, "vm-opts-full");
08618          }
08619          /* Logic:
08620           * If the current message is not the first OR
08621           * if we're listening to the first new message and there are
08622           * also urgent messages, then prompt for navigation to the
08623           * previous message
08624           */
08625          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08626             res = ast_play_and_wait(chan, "vm-prev");
08627          }
08628          if (!res && !skipadvanced)
08629             res = ast_play_and_wait(chan, "vm-advopts");
08630          if (!res)
08631             res = ast_play_and_wait(chan, "vm-repeat");
08632          /* Logic:
08633           * If we're not listening to the last message OR
08634           * we're listening to the last urgent message and there are
08635           * also new non-urgent messages, then prompt for navigation
08636           * to the next message
08637           */
08638          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08639             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08640             res = ast_play_and_wait(chan, "vm-next");
08641          }
08642          if (!res) {
08643             if (!vms->deleted[vms->curmsg])
08644                res = ast_play_and_wait(chan, "vm-delete");
08645             else
08646                res = ast_play_and_wait(chan, "vm-undelete");
08647             if (!res)
08648                res = ast_play_and_wait(chan, "vm-toforward");
08649             if (!res)
08650                res = ast_play_and_wait(chan, "vm-savemessage");
08651          }
08652       }
08653       if (!res) {
08654          res = ast_play_and_wait(chan, "vm-helpexit");
08655       }
08656       if (!res)
08657          res = ast_waitfordigit(chan, 6000);
08658       if (!res) {
08659          vms->repeats++;
08660          if (vms->repeats > 2) {
08661             res = 't';
08662          }
08663       }
08664    }
08665    return res;
08666 }
08667 
08668 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08669 {
08670    int res = 0;
08671    /* Play instructions and wait for new command */
08672    while (!res) {
08673       if (vms->lastmsg > -1) {
08674          res = ast_play_and_wait(chan, "vm-listen");
08675          if (!res)
08676             res = vm_play_folder_name(chan, vms->vmbox);
08677          if (!res)
08678             res = ast_play_and_wait(chan, "press");
08679          if (!res)
08680             res = ast_play_and_wait(chan, "digits/1");
08681       }
08682       if (!res)
08683          res = ast_play_and_wait(chan, "vm-opts");
08684       if (!res) {
08685          vms->starting = 0;
08686          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08687       }
08688    }
08689    return res;
08690 }
08691 
08692 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08693 {
08694    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08695       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08696    } else {             /* Default to ENGLISH */
08697       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08698    }
08699 }
08700 
08701 
08702 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08703 {
08704    int cmd = 0;
08705    int duration = 0;
08706    int tries = 0;
08707    char newpassword[80] = "";
08708    char newpassword2[80] = "";
08709    char prefile[PATH_MAX] = "";
08710    unsigned char buf[256];
08711    int bytes=0;
08712 
08713    if (ast_adsi_available(chan)) {
08714       bytes += adsi_logo(buf + bytes);
08715       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08716       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08717       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08718       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08719       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08720    }
08721 
08722    /* First, have the user change their password 
08723       so they won't get here again */
08724    for (;;) {
08725       newpassword[1] = '\0';
08726       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08727       if (cmd == '#')
08728          newpassword[0] = '\0';
08729       if (cmd < 0 || cmd == 't' || cmd == '#')
08730          return cmd;
08731       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08732       if (cmd < 0 || cmd == 't' || cmd == '#')
08733          return cmd;
08734       cmd = check_password(vmu, newpassword); /* perform password validation */
08735       if (cmd != 0) {
08736          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08737          cmd = ast_play_and_wait(chan, vm_invalid_password);
08738       } else {
08739          newpassword2[1] = '\0';
08740          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08741          if (cmd == '#')
08742             newpassword2[0] = '\0';
08743          if (cmd < 0 || cmd == 't' || cmd == '#')
08744             return cmd;
08745          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08746          if (cmd < 0 || cmd == 't' || cmd == '#')
08747             return cmd;
08748          if (!strcmp(newpassword, newpassword2))
08749             break;
08750          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08751          cmd = ast_play_and_wait(chan, vm_mismatch);
08752       }
08753       if (++tries == 3)
08754          return -1;
08755       if (cmd != 0) {
08756          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08757       }
08758    }
08759    if (pwdchange & PWDCHANGE_INTERNAL)
08760       vm_change_password(vmu, newpassword);
08761    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08762       vm_change_password_shell(vmu, newpassword);
08763 
08764    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08765    cmd = ast_play_and_wait(chan, vm_passchanged);
08766 
08767    /* If forcename is set, have the user record their name */  
08768    if (ast_test_flag(vmu, VM_FORCENAME)) {
08769       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08770       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08771          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08772          if (cmd < 0 || cmd == 't' || cmd == '#')
08773             return cmd;
08774       }
08775    }
08776 
08777    /* If forcegreetings is set, have the user record their greetings */
08778    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08779       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08780       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08781          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08782          if (cmd < 0 || cmd == 't' || cmd == '#')
08783             return cmd;
08784       }
08785 
08786       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08787       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08788          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08789          if (cmd < 0 || cmd == 't' || cmd == '#')
08790             return cmd;
08791       }
08792    }
08793 
08794    return cmd;
08795 }
08796 
08797 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08798 {
08799    int cmd = 0;
08800    int retries = 0;
08801    int duration = 0;
08802    char newpassword[80] = "";
08803    char newpassword2[80] = "";
08804    char prefile[PATH_MAX] = "";
08805    unsigned char buf[256];
08806    int bytes=0;
08807 
08808    if (ast_adsi_available(chan)) {
08809       bytes += adsi_logo(buf + bytes);
08810       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08811       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08812       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08813       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08814       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08815    }
08816    while ((cmd >= 0) && (cmd != 't')) {
08817       if (cmd)
08818          retries = 0;
08819       switch (cmd) {
08820       case '1': /* Record your unavailable message */
08821          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08822          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08823          break;
08824       case '2':  /* Record your busy message */
08825          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08826          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08827          break;
08828       case '3': /* Record greeting */
08829          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08830          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08831          break;
08832       case '4':  /* manage the temporary greeting */
08833          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08834          break;
08835       case '5': /* change password */
08836          if (vmu->password[0] == '-') {
08837             cmd = ast_play_and_wait(chan, "vm-no");
08838             break;
08839          }
08840          newpassword[1] = '\0';
08841          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08842          if (cmd == '#')
08843             newpassword[0] = '\0';
08844          else {
08845             if (cmd < 0)
08846                break;
08847             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08848                break;
08849             }
08850          }
08851          cmd = check_password(vmu, newpassword); /* perform password validation */
08852          if (cmd != 0) {
08853             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08854             cmd = ast_play_and_wait(chan, vm_invalid_password);
08855             if (!cmd) {
08856                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08857             }
08858             break;
08859          }
08860          newpassword2[1] = '\0';
08861          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08862          if (cmd == '#')
08863             newpassword2[0] = '\0';
08864          else {
08865             if (cmd < 0)
08866                break;
08867 
08868             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08869                break;
08870             }
08871          }
08872          if (strcmp(newpassword, newpassword2)) {
08873             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08874             cmd = ast_play_and_wait(chan, vm_mismatch);
08875             if (!cmd) {
08876                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08877             }
08878             break;
08879          }
08880          if (pwdchange & PWDCHANGE_INTERNAL)
08881             vm_change_password(vmu, newpassword);
08882          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08883             vm_change_password_shell(vmu, newpassword);
08884 
08885          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08886          cmd = ast_play_and_wait(chan, vm_passchanged);
08887          break;
08888       case '*': 
08889          cmd = 't';
08890          break;
08891       default: 
08892          cmd = 0;
08893          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08894          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08895          if (ast_fileexists(prefile, NULL, NULL)) {
08896             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08897          }
08898          DISPOSE(prefile, -1);
08899          if (!cmd) {
08900             cmd = ast_play_and_wait(chan, "vm-options");
08901          }
08902          if (!cmd) {
08903             cmd = ast_waitfordigit(chan,6000);
08904          }
08905          if (!cmd) {
08906             retries++;
08907          }
08908          if (retries > 3) {
08909             cmd = 't';
08910          }
08911       }
08912    }
08913    if (cmd == 't')
08914       cmd = 0;
08915    return cmd;
08916 }
08917 
08918 /*!
08919  * \brief The handler for 'record a temporary greeting'. 
08920  * \param chan
08921  * \param vmu
08922  * \param vms
08923  * \param fmtc
08924  * \param record_gain
08925  *
08926  * This is option 4 from the mailbox options menu.
08927  * This function manages the following promptings:
08928  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08929  * 2: remove (delete) the temporary greeting.
08930  * *: return to the main menu.
08931  *
08932  * \return zero on success, -1 on error.
08933  */
08934 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08935 {
08936    int cmd = 0;
08937    int retries = 0;
08938    int duration = 0;
08939    char prefile[PATH_MAX] = "";
08940    unsigned char buf[256];
08941    int bytes = 0;
08942 
08943    if (ast_adsi_available(chan)) {
08944       bytes += adsi_logo(buf + bytes);
08945       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08946       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08947       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08948       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08949       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08950    }
08951 
08952    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08953    while ((cmd >= 0) && (cmd != 't')) {
08954       if (cmd)
08955          retries = 0;
08956       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08957       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08958          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08959          cmd = 't';  
08960       } else {
08961          switch (cmd) {
08962          case '1':
08963             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08964             break;
08965          case '2':
08966             DELETE(prefile, -1, prefile, vmu);
08967             ast_play_and_wait(chan, "vm-tempremoved");
08968             cmd = 't';  
08969             break;
08970          case '*': 
08971             cmd = 't';
08972             break;
08973          default:
08974             cmd = ast_play_and_wait(chan,
08975                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08976                   "vm-tempgreeting2" : "vm-tempgreeting");
08977             if (!cmd)
08978                cmd = ast_waitfordigit(chan,6000);
08979             if (!cmd)
08980                retries++;
08981             if (retries > 3)
08982                cmd = 't';
08983          }
08984       }
08985       DISPOSE(prefile, -1);
08986    }
08987    if (cmd == 't')
08988       cmd = 0;
08989    return cmd;
08990 }
08991 
08992 /*!
08993  * \brief Greek syntax for 'You have N messages' greeting.
08994  * \param chan
08995  * \param vms
08996  * \param vmu
08997  *
08998  * \return zero on success, -1 on error.
08999  */   
09000 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09001 {
09002    int cmd=0;
09003 
09004    if (vms->lastmsg > -1) {
09005       cmd = play_message(chan, vmu, vms);
09006    } else {
09007       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09008       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09009          if (!cmd) {
09010             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09011             cmd = ast_play_and_wait(chan, vms->fn);
09012          }
09013          if (!cmd)
09014             cmd = ast_play_and_wait(chan, "vm-messages");
09015       } else {
09016          if (!cmd)
09017             cmd = ast_play_and_wait(chan, "vm-messages");
09018          if (!cmd) {
09019             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09020             cmd = ast_play_and_wait(chan, vms->fn);
09021          }
09022       }
09023    } 
09024    return cmd;
09025 }
09026 
09027 /* Hebrew Syntax */
09028 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09029 {
09030    int cmd = 0;
09031 
09032    if (vms->lastmsg > -1) {
09033       cmd = play_message(chan, vmu, vms);
09034    } else {
09035       if (!strcasecmp(vms->fn, "INBOX")) {
09036          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09037       } else {
09038          cmd = ast_play_and_wait(chan, "vm-nomessages");
09039       }
09040    }
09041    return cmd;
09042 }
09043 
09044 /*! 
09045  * \brief Default English syntax for 'You have N messages' greeting.
09046  * \param chan
09047  * \param vms
09048  * \param vmu
09049  *
09050  * \return zero on success, -1 on error.
09051  */
09052 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09053 {
09054    int cmd=0;
09055 
09056    if (vms->lastmsg > -1) {
09057       cmd = play_message(chan, vmu, vms);
09058    } else {
09059       cmd = ast_play_and_wait(chan, "vm-youhave");
09060       if (!cmd) 
09061          cmd = ast_play_and_wait(chan, "vm-no");
09062       if (!cmd) {
09063          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09064          cmd = ast_play_and_wait(chan, vms->fn);
09065       }
09066       if (!cmd)
09067          cmd = ast_play_and_wait(chan, "vm-messages");
09068    }
09069    return cmd;
09070 }
09071 
09072 /*! 
09073  *\brief Italian syntax for 'You have N messages' greeting.
09074  * \param chan
09075  * \param vms
09076  * \param vmu
09077  *
09078  * \return zero on success, -1 on error.
09079  */
09080 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09081 {
09082    int cmd=0;
09083 
09084    if (vms->lastmsg > -1) {
09085       cmd = play_message(chan, vmu, vms);
09086    } else {
09087       cmd = ast_play_and_wait(chan, "vm-no");
09088       if (!cmd)
09089          cmd = ast_play_and_wait(chan, "vm-message");
09090       if (!cmd) {
09091          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09092          cmd = ast_play_and_wait(chan, vms->fn);
09093       }
09094    }
09095    return cmd;
09096 }
09097 
09098 /*! 
09099  * \brief Spanish syntax for 'You have N messages' greeting.
09100  * \param chan
09101  * \param vms
09102  * \param vmu
09103  *
09104  * \return zero on success, -1 on error.
09105  */
09106 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09107 {
09108    int cmd=0;
09109 
09110    if (vms->lastmsg > -1) {
09111       cmd = play_message(chan, vmu, vms);
09112    } else {
09113       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09114       if (!cmd)
09115          cmd = ast_play_and_wait(chan, "vm-messages");
09116       if (!cmd) {
09117          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09118          cmd = ast_play_and_wait(chan, vms->fn);
09119       }
09120    }
09121    return cmd;
09122 }
09123 
09124 /*! 
09125  * \brief Portuguese syntax for 'You have N messages' greeting.
09126  * \param chan
09127  * \param vms
09128  * \param vmu
09129  *
09130  * \return zero on success, -1 on error.
09131  */
09132 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09133 {
09134    int cmd=0;
09135 
09136    if (vms->lastmsg > -1) {
09137       cmd = play_message(chan, vmu, vms);
09138    } else {
09139       cmd = ast_play_and_wait(chan, "vm-no");
09140       if (!cmd) {
09141          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09142          cmd = ast_play_and_wait(chan, vms->fn);
09143       }
09144       if (!cmd)
09145          cmd = ast_play_and_wait(chan, "vm-messages");
09146    }
09147    return cmd;
09148 }
09149 
09150 /*! 
09151  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09152  * \param chan
09153  * \param vms
09154  * \param vmu
09155  *
09156  * \return zero on success, -1 on error.
09157  */
09158 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09159 {
09160    int cmd=0;
09161 
09162    if (vms->lastmsg > -1) {
09163       cmd = play_message(chan, vmu, vms);
09164    } else {
09165       cmd = ast_play_and_wait(chan, "vm-you");
09166       if (!cmd) 
09167          cmd = ast_play_and_wait(chan, "vm-haveno");
09168       if (!cmd)
09169          cmd = ast_play_and_wait(chan, "vm-messages");
09170       if (!cmd) {
09171          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09172          cmd = ast_play_and_wait(chan, vms->fn);
09173       }
09174    }
09175    return cmd;
09176 }
09177 
09178 /*!
09179  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09180  * \param chan The channel for the current user. We read the language property from this.
09181  * \param vms passed into the language-specific vm_browse_messages function.
09182  * \param vmu passed into the language-specific vm_browse_messages function.
09183  * 
09184  * The method to be invoked is determined by the value of language code property in the user's channel.
09185  * The default (when unable to match) is to use english.
09186  *
09187  * \return zero on success, -1 on error.
09188  */
09189 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09190 {
09191    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09192       return vm_browse_messages_es(chan, vms, vmu);
09193    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09194       return vm_browse_messages_gr(chan, vms, vmu);
09195    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09196       return vm_browse_messages_he(chan, vms, vmu);
09197    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09198       return vm_browse_messages_it(chan, vms, vmu);
09199    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09200       return vm_browse_messages_pt(chan, vms, vmu);
09201    } else if (!strncasecmp(chan->language, "zh", 2)) {
09202       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
09203    } else {                                             /* Default to English syntax */
09204       return vm_browse_messages_en(chan, vms, vmu);
09205    }
09206 }
09207 
09208 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09209          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09210          int skipuser, int max_logins, int silent)
09211 {
09212    int useadsi=0, valid=0, logretries=0;
09213    char password[AST_MAX_EXTENSION]="", *passptr;
09214    struct ast_vm_user vmus, *vmu = NULL;
09215 
09216    /* If ADSI is supported, setup login screen */
09217    adsi_begin(chan, &useadsi);
09218    if (!skipuser && useadsi)
09219       adsi_login(chan);
09220    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09221       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09222       return -1;
09223    }
09224    
09225    /* Authenticate them and get their mailbox/password */
09226    
09227    while (!valid && (logretries < max_logins)) {
09228       /* Prompt for, and read in the username */
09229       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09230          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09231          return -1;
09232       }
09233       if (ast_strlen_zero(mailbox)) {
09234          if (chan->cid.cid_num) {
09235             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
09236          } else {
09237             ast_verb(3,"Username not entered\n");  
09238             return -1;
09239          }
09240       }
09241       if (useadsi)
09242          adsi_password(chan);
09243 
09244       if (!ast_strlen_zero(prefix)) {
09245          char fullusername[80] = "";
09246          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09247          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09248          ast_copy_string(mailbox, fullusername, mailbox_size);
09249       }
09250 
09251       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
09252       vmu = find_user(&vmus, context, mailbox);
09253       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09254          /* saved password is blank, so don't bother asking */
09255          password[0] = '\0';
09256       } else {
09257          if (ast_streamfile(chan, vm_password, chan->language)) {
09258             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09259             return -1;
09260          }
09261          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09262             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09263             return -1;
09264          }
09265       }
09266 
09267       if (vmu) {
09268          passptr = vmu->password;
09269          if (passptr[0] == '-') passptr++;
09270       }
09271       if (vmu && !strcmp(passptr, password))
09272          valid++;
09273       else {
09274          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09275          if (!ast_strlen_zero(prefix))
09276             mailbox[0] = '\0';
09277       }
09278       logretries++;
09279       if (!valid) {
09280          if (skipuser || logretries >= max_logins) {
09281             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09282                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09283                return -1;
09284             }
09285          } else {
09286             if (useadsi)
09287                adsi_login(chan);
09288             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09289                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09290                return -1;
09291             }
09292          }
09293          if (ast_waitstream(chan, "")) /* Channel is hung up */
09294             return -1;
09295       }
09296    }
09297    if (!valid && (logretries >= max_logins)) {
09298       ast_stopstream(chan);
09299       ast_play_and_wait(chan, "vm-goodbye");
09300       return -1;
09301    }
09302    if (vmu && !skipuser) {
09303       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09304    }
09305    return 0;
09306 }
09307 
09308 static int vm_execmain(struct ast_channel *chan, void *data)
09309 {
09310    /* XXX This is, admittedly, some pretty horrendous code.  For some
09311       reason it just seemed a lot easier to do with GOTO's.  I feel
09312       like I'm back in my GWBASIC days. XXX */
09313    int res=-1;
09314    int cmd=0;
09315    int valid = 0;
09316    char prefixstr[80] ="";
09317    char ext_context[256]="";
09318    int box;
09319    int useadsi = 0;
09320    int skipuser = 0;
09321    struct vm_state vms;
09322    struct ast_vm_user *vmu = NULL, vmus;
09323    char *context=NULL;
09324    int silentexit = 0;
09325    struct ast_flags flags = { 0 };
09326    signed char record_gain = 0;
09327    int play_auto = 0;
09328    int play_folder = 0;
09329    int in_urgent = 0;
09330 #ifdef IMAP_STORAGE
09331    int deleted = 0;
09332 #endif
09333 
09334    /* Add the vm_state to the active list and keep it active */
09335    memset(&vms, 0, sizeof(vms));
09336 
09337    vms.lastmsg = -1;
09338 
09339    memset(&vmus, 0, sizeof(vmus));
09340 
09341    if (chan->_state != AST_STATE_UP) {
09342       ast_debug(1, "Before ast_answer\n");
09343       ast_answer(chan);
09344    }
09345 
09346    if (!ast_strlen_zero(data)) {
09347       char *opts[OPT_ARG_ARRAY_SIZE];
09348       char *parse;
09349       AST_DECLARE_APP_ARGS(args,
09350          AST_APP_ARG(argv0);
09351          AST_APP_ARG(argv1);
09352       );
09353 
09354       parse = ast_strdupa(data);
09355 
09356       AST_STANDARD_APP_ARGS(args, parse);
09357 
09358       if (args.argc == 2) {
09359          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09360             return -1;
09361          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09362             int gain;
09363             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09364                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09365                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09366                   return -1;
09367                } else {
09368                   record_gain = (signed char) gain;
09369                }
09370             } else {
09371                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09372             }
09373          }
09374          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09375             play_auto = 1;
09376             if (opts[OPT_ARG_PLAYFOLDER]) {
09377                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09378                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09379                }
09380             } else {
09381                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09382             }  
09383             if ( play_folder > 9 || play_folder < 0) {
09384                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09385                play_folder = 0;
09386             }
09387          }
09388       } else {
09389          /* old style options parsing */
09390          while (*(args.argv0)) {
09391             if (*(args.argv0) == 's')
09392                ast_set_flag(&flags, OPT_SILENT);
09393             else if (*(args.argv0) == 'p')
09394                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09395             else 
09396                break;
09397             (args.argv0)++;
09398          }
09399 
09400       }
09401 
09402       valid = ast_test_flag(&flags, OPT_SILENT);
09403 
09404       if ((context = strchr(args.argv0, '@')))
09405          *context++ = '\0';
09406 
09407       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09408          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09409       else
09410          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09411 
09412       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09413          skipuser++;
09414       else
09415          valid = 0;
09416    }
09417 
09418    if (!valid)
09419       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09420 
09421    ast_debug(1, "After vm_authenticate\n");
09422    if (!res) {
09423       valid = 1;
09424       if (!skipuser)
09425          vmu = &vmus;
09426    } else {
09427       res = 0;
09428    }
09429 
09430    /* If ADSI is supported, setup login screen */
09431    adsi_begin(chan, &useadsi);
09432 
09433    if (!valid) {
09434       goto out;
09435    }
09436 
09437 #ifdef IMAP_STORAGE
09438    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09439    pthread_setspecific(ts_vmstate.key, &vms);
09440 
09441    vms.interactive = 1;
09442    vms.updated = 1;
09443    if (vmu)
09444       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09445    vmstate_insert(&vms);
09446    init_vm_state(&vms);
09447 #endif
09448    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09449       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09450       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09451       return -1;
09452    }
09453    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09454       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09455       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09456       return -1;
09457    }
09458    
09459    /* Set language from config to override channel language */
09460    if (!ast_strlen_zero(vmu->language))
09461       ast_string_field_set(chan, language, vmu->language);
09462 
09463    /* Retrieve urgent, old and new message counts */
09464    ast_debug(1, "Before open_mailbox\n");
09465    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09466    if (res < 0)
09467       goto out;
09468    vms.oldmessages = vms.lastmsg + 1;
09469    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09470    /* check INBOX */
09471    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09472    if (res < 0)
09473       goto out;
09474    vms.newmessages = vms.lastmsg + 1;
09475    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09476    /* Start in Urgent */
09477    in_urgent = 1;
09478    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09479    if (res < 0)
09480       goto out;
09481    vms.urgentmessages = vms.lastmsg + 1;
09482    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09483 
09484    /* Select proper mailbox FIRST!! */
09485    if (play_auto) {
09486       if (vms.urgentmessages) {
09487          in_urgent = 1;
09488          res = open_mailbox(&vms, vmu, 11);
09489       } else {
09490          in_urgent = 0;
09491          res = open_mailbox(&vms, vmu, play_folder);
09492       }
09493       if (res < 0)
09494          goto out;
09495 
09496       /* If there are no new messages, inform the user and hangup */
09497       if (vms.lastmsg == -1) {
09498          in_urgent = 0;
09499          cmd = vm_browse_messages(chan, &vms, vmu);
09500          res = 0;
09501          goto out;
09502       }
09503    } else {
09504       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09505          /* If we only have old messages start here */
09506          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09507          in_urgent = 0;
09508          play_folder = 1;
09509          if (res < 0)
09510             goto out;
09511       } else if (!vms.urgentmessages && vms.newmessages) {
09512          /* If we have new messages but none are urgent */
09513          in_urgent = 0;
09514          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09515          if (res < 0)
09516             goto out;
09517       }
09518    }
09519 
09520    if (useadsi)
09521       adsi_status(chan, &vms);
09522    res = 0;
09523 
09524    /* Check to see if this is a new user */
09525    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09526       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09527       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09528          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09529       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09530       if ((cmd == 't') || (cmd == '#')) {
09531          /* Timeout */
09532          res = 0;
09533          goto out;
09534       } else if (cmd < 0) {
09535          /* Hangup */
09536          res = -1;
09537          goto out;
09538       }
09539    }
09540 #ifdef IMAP_STORAGE
09541       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09542       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09543          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09544          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09545       }
09546       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09547       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09548          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09549          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09550       }
09551 #endif
09552    if (play_auto) {
09553       cmd = '1';
09554    } else {
09555       cmd = vm_intro(chan, vmu, &vms);
09556    }
09557 
09558    vms.repeats = 0;
09559    vms.starting = 1;
09560    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09561       /* Run main menu */
09562       switch (cmd) {
09563       case '1': /* First message */
09564          vms.curmsg = 0;
09565          /* Fall through */
09566       case '5': /* Play current message */
09567          cmd = vm_browse_messages(chan, &vms, vmu);
09568          break;
09569       case '2': /* Change folders */
09570          if (useadsi)
09571             adsi_folders(chan, 0, "Change to folder...");
09572          cmd = get_folder2(chan, "vm-changeto", 0);
09573          if (cmd == '#') {
09574             cmd = 0;
09575          } else if (cmd > 0) {
09576             cmd = cmd - '0';
09577             res = close_mailbox(&vms, vmu);
09578             if (res == ERROR_LOCK_PATH)
09579                goto out;
09580             /* If folder is not urgent, set in_urgent to zero! */
09581             if (cmd != 11) in_urgent = 0;
09582             res = open_mailbox(&vms, vmu, cmd);
09583             if (res < 0)
09584                goto out;
09585             play_folder = cmd;
09586             cmd = 0;
09587          }
09588          if (useadsi)
09589             adsi_status2(chan, &vms);
09590             
09591          if (!cmd)
09592             cmd = vm_play_folder_name(chan, vms.vmbox);
09593 
09594          vms.starting = 1;
09595          break;
09596       case '3': /* Advanced options */
09597          cmd = 0;
09598          vms.repeats = 0;
09599          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09600             switch (cmd) {
09601             case '1': /* Reply */
09602                if (vms.lastmsg > -1 && !vms.starting) {
09603                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09604                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09605                      res = cmd;
09606                      goto out;
09607                   }
09608                } else
09609                   cmd = ast_play_and_wait(chan, "vm-sorry");
09610                cmd = 't';
09611                break;
09612             case '2': /* Callback */
09613                if (!vms.starting)
09614                   ast_verb(3, "Callback Requested\n");
09615                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09616                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09617                   if (cmd == 9) {
09618                      silentexit = 1;
09619                      goto out;
09620                   } else if (cmd == ERROR_LOCK_PATH) {
09621                      res = cmd;
09622                      goto out;
09623                   }
09624                } else 
09625                   cmd = ast_play_and_wait(chan, "vm-sorry");
09626                cmd = 't';
09627                break;
09628             case '3': /* Envelope */
09629                if (vms.lastmsg > -1 && !vms.starting) {
09630                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09631                   if (cmd == ERROR_LOCK_PATH) {
09632                      res = cmd;
09633                      goto out;
09634                   }
09635                } else
09636                   cmd = ast_play_and_wait(chan, "vm-sorry");
09637                cmd = 't';
09638                break;
09639             case '4': /* Dialout */
09640                if (!ast_strlen_zero(vmu->dialout)) {
09641                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09642                   if (cmd == 9) {
09643                      silentexit = 1;
09644                      goto out;
09645                   }
09646                } else 
09647                   cmd = ast_play_and_wait(chan, "vm-sorry");
09648                cmd = 't';
09649                break;
09650 
09651             case '5': /* Leave VoiceMail */
09652                if (ast_test_flag(vmu, VM_SVMAIL)) {
09653                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09654                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09655                      res = cmd;
09656                      goto out;
09657                   }
09658                } else
09659                   cmd = ast_play_and_wait(chan,"vm-sorry");
09660                cmd='t';
09661                break;
09662                
09663             case '*': /* Return to main menu */
09664                cmd = 't';
09665                break;
09666 
09667             default:
09668                cmd = 0;
09669                if (!vms.starting) {
09670                   cmd = ast_play_and_wait(chan, "vm-toreply");
09671                }
09672                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09673                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09674                }
09675                if (!cmd && !vms.starting) {
09676                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09677                }
09678                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09679                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09680                }
09681                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09682                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09683                if (!cmd)
09684                   cmd = ast_play_and_wait(chan, "vm-starmain");
09685                if (!cmd)
09686                   cmd = ast_waitfordigit(chan,6000);
09687                if (!cmd)
09688                   vms.repeats++;
09689                if (vms.repeats > 3)
09690                   cmd = 't';
09691             }
09692          }
09693          if (cmd == 't') {
09694             cmd = 0;
09695             vms.repeats = 0;
09696          }
09697          break;
09698       case '4': /* Go to the previous message */
09699          if (vms.curmsg > 0) {
09700             vms.curmsg--;
09701             cmd = play_message(chan, vmu, &vms);
09702          } else {
09703             /* Check if we were listening to new
09704                messages.  If so, go to Urgent messages
09705                instead of saying "no more messages"
09706             */
09707             if (in_urgent == 0 && vms.urgentmessages > 0) {
09708                /* Check for Urgent messages */
09709                in_urgent = 1;
09710                res = close_mailbox(&vms, vmu);
09711                if (res == ERROR_LOCK_PATH)
09712                   goto out;
09713                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09714                if (res < 0)
09715                   goto out;
09716                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09717                vms.curmsg = vms.lastmsg;
09718                if (vms.lastmsg < 0)
09719                   cmd = ast_play_and_wait(chan, "vm-nomore");
09720             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09721                vms.curmsg = vms.lastmsg;
09722                cmd = play_message(chan, vmu, &vms);
09723             } else {
09724                cmd = ast_play_and_wait(chan, "vm-nomore");
09725             }
09726          }
09727          break;
09728       case '6': /* Go to the next message */
09729          if (vms.curmsg < vms.lastmsg) {
09730             vms.curmsg++;
09731             cmd = play_message(chan, vmu, &vms);
09732          } else {
09733             if (in_urgent && vms.newmessages > 0) {
09734                /* Check if we were listening to urgent
09735                 * messages.  If so, go to regular new messages
09736                 * instead of saying "no more messages"
09737                 */
09738                in_urgent = 0;
09739                res = close_mailbox(&vms, vmu);
09740                if (res == ERROR_LOCK_PATH)
09741                   goto out;
09742                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09743                if (res < 0)
09744                   goto out;
09745                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09746                vms.curmsg = -1;
09747                if (vms.lastmsg < 0) {
09748                   cmd = ast_play_and_wait(chan, "vm-nomore");
09749                }
09750             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09751                vms.curmsg = 0;
09752                cmd = play_message(chan, vmu, &vms);
09753             } else {
09754                cmd = ast_play_and_wait(chan, "vm-nomore");
09755             }
09756          }
09757          break;
09758       case '7': /* Delete the current message */
09759          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09760             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09761             if (useadsi)
09762                adsi_delete(chan, &vms);
09763             if (vms.deleted[vms.curmsg]) {
09764                if (play_folder == 0) {
09765                   if (in_urgent) {
09766                      vms.urgentmessages--;
09767                   } else {
09768                      vms.newmessages--;
09769                   }
09770                }
09771                else if (play_folder == 1)
09772                   vms.oldmessages--;
09773                cmd = ast_play_and_wait(chan, "vm-deleted");
09774             } else {
09775                if (play_folder == 0) {
09776                   if (in_urgent) {
09777                      vms.urgentmessages++;
09778                   } else {
09779                      vms.newmessages++;
09780                   }
09781                }
09782                else if (play_folder == 1)
09783                   vms.oldmessages++;
09784                cmd = ast_play_and_wait(chan, "vm-undeleted");
09785             }
09786             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09787                if (vms.curmsg < vms.lastmsg) {
09788                   vms.curmsg++;
09789                   cmd = play_message(chan, vmu, &vms);
09790                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09791                   vms.curmsg = 0;
09792                   cmd = play_message(chan, vmu, &vms);
09793                } else {
09794                   /* Check if we were listening to urgent
09795                      messages.  If so, go to regular new messages
09796                      instead of saying "no more messages"
09797                   */
09798                   if (in_urgent == 1) {
09799                      /* Check for new messages */
09800                      in_urgent = 0;
09801                      res = close_mailbox(&vms, vmu);
09802                      if (res == ERROR_LOCK_PATH)
09803                         goto out;
09804                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09805                      if (res < 0)
09806                         goto out;
09807                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09808                      vms.curmsg = -1;
09809                      if (vms.lastmsg < 0)
09810                         cmd = ast_play_and_wait(chan, "vm-nomore");
09811                   } else {
09812                      cmd = ast_play_and_wait(chan, "vm-nomore");
09813                   }
09814                }
09815             }
09816          } else /* Delete not valid if we haven't selected a message */
09817             cmd = 0;
09818 #ifdef IMAP_STORAGE
09819          deleted = 1;
09820 #endif
09821          break;
09822    
09823       case '8': /* Forward the current messgae */
09824          if (vms.lastmsg > -1) {
09825             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09826             if (cmd == ERROR_LOCK_PATH) {
09827                res = cmd;
09828                goto out;
09829             }
09830          } else {
09831             /* Check if we were listening to urgent
09832                messages.  If so, go to regular new messages
09833                instead of saying "no more messages"
09834             */
09835             if (in_urgent == 1 && vms.newmessages > 0) {
09836                /* Check for new messages */
09837                in_urgent = 0;
09838                res = close_mailbox(&vms, vmu);
09839                if (res == ERROR_LOCK_PATH)
09840                   goto out;
09841                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09842                if (res < 0)
09843                   goto out;
09844                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09845                vms.curmsg = -1;
09846                if (vms.lastmsg < 0)
09847                   cmd = ast_play_and_wait(chan, "vm-nomore");
09848             } else {
09849                cmd = ast_play_and_wait(chan, "vm-nomore");
09850             }
09851          }
09852          break;
09853       case '9': /* Save message to folder */
09854          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09855             /* No message selected */
09856             cmd = 0;
09857             break;
09858          }
09859          if (useadsi)
09860             adsi_folders(chan, 1, "Save to folder...");
09861          cmd = get_folder2(chan, "vm-savefolder", 1);
09862          box = 0; /* Shut up compiler */
09863          if (cmd == '#') {
09864             cmd = 0;
09865             break;
09866          } else if (cmd > 0) {
09867             box = cmd = cmd - '0';
09868             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09869             if (cmd == ERROR_LOCK_PATH) {
09870                res = cmd;
09871                goto out;
09872 #ifndef IMAP_STORAGE
09873             } else if (!cmd) {
09874                vms.deleted[vms.curmsg] = 1;
09875 #endif
09876             } else {
09877                vms.deleted[vms.curmsg] = 0;
09878                vms.heard[vms.curmsg] = 0;
09879             }
09880          }
09881          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09882          if (useadsi)
09883             adsi_message(chan, &vms);
09884          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09885          if (!cmd) {
09886             cmd = ast_play_and_wait(chan, "vm-message");
09887             if (!cmd) 
09888                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09889             if (!cmd)
09890                cmd = ast_play_and_wait(chan, "vm-savedto");
09891             if (!cmd)
09892                cmd = vm_play_folder_name(chan, vms.fn);
09893          } else {
09894             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09895          }
09896          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09897             if (vms.curmsg < vms.lastmsg) {
09898                vms.curmsg++;
09899                cmd = play_message(chan, vmu, &vms);
09900             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09901                vms.curmsg = 0;
09902                cmd = play_message(chan, vmu, &vms);
09903             } else {
09904                /* Check if we were listening to urgent
09905                   messages.  If so, go to regular new messages
09906                   instead of saying "no more messages"
09907                */
09908                if (in_urgent == 1 && vms.newmessages > 0) {
09909                   /* Check for new messages */
09910                   in_urgent = 0;
09911                   res = close_mailbox(&vms, vmu);
09912                   if (res == ERROR_LOCK_PATH)
09913                      goto out;
09914                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09915                   if (res < 0)
09916                      goto out;
09917                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09918                   vms.curmsg = -1;
09919                   if (vms.lastmsg < 0)
09920                      cmd = ast_play_and_wait(chan, "vm-nomore");
09921                } else {
09922                   cmd = ast_play_and_wait(chan, "vm-nomore");
09923                }
09924             }
09925          }
09926          break;
09927       case '*': /* Help */
09928          if (!vms.starting) {
09929             cmd = ast_play_and_wait(chan, "vm-onefor");
09930             if (!strncasecmp(chan->language, "he", 2)) {
09931                cmd = ast_play_and_wait(chan, "vm-for");
09932             }
09933             if (!cmd)
09934                cmd = vm_play_folder_name(chan, vms.vmbox);
09935             if (!cmd)
09936                cmd = ast_play_and_wait(chan, "vm-opts");
09937             if (!cmd)
09938                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09939          } else
09940             cmd = 0;
09941          break;
09942       case '0': /* Mailbox options */
09943          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09944          if (useadsi)
09945             adsi_status(chan, &vms);
09946          break;
09947       default: /* Nothing */
09948          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09949          break;
09950       }
09951    }
09952    if ((cmd == 't') || (cmd == '#')) {
09953       /* Timeout */
09954       res = 0;
09955    } else {
09956       /* Hangup */
09957       res = -1;
09958    }
09959 
09960 out:
09961    if (res > -1) {
09962       ast_stopstream(chan);
09963       adsi_goodbye(chan);
09964       if (valid && res != OPERATOR_EXIT) {
09965          if (silentexit)
09966             res = ast_play_and_wait(chan, "vm-dialout");
09967          else 
09968             res = ast_play_and_wait(chan, "vm-goodbye");
09969       }
09970       if ((valid && res > 0) || res == OPERATOR_EXIT) {
09971          res = 0;
09972       }
09973       if (useadsi)
09974          ast_adsi_unload_session(chan);
09975    }
09976    if (vmu)
09977       close_mailbox(&vms, vmu);
09978    if (valid) {
09979       int new = 0, old = 0, urgent = 0;
09980       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09981       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09982       /* Urgent flag not passwd to externnotify here */
09983       run_externnotify(vmu->context, vmu->mailbox, NULL);
09984       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09985       queue_mwi_event(ext_context, urgent, new, old);
09986    }
09987 #ifdef IMAP_STORAGE
09988    /* expunge message - use UID Expunge if supported on IMAP server*/
09989    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09990    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09991       ast_mutex_lock(&vms.lock);
09992 #ifdef HAVE_IMAP_TK2006
09993       if (LEVELUIDPLUS (vms.mailstream)) {
09994          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09995       } else 
09996 #endif
09997          mail_expunge(vms.mailstream);
09998       ast_mutex_unlock(&vms.lock);
09999    }
10000    /*  before we delete the state, we should copy pertinent info
10001     *  back to the persistent model */
10002    if (vmu) {
10003       vmstate_delete(&vms);
10004    }
10005 #endif
10006    if (vmu)
10007       free_user(vmu);
10008    if (vms.deleted)
10009       ast_free(vms.deleted);
10010    if (vms.heard)
10011       ast_free(vms.heard);
10012 
10013 #ifdef IMAP_STORAGE
10014    pthread_setspecific(ts_vmstate.key, NULL);
10015 #endif
10016    return res;
10017 }
10018 
10019 static int vm_exec(struct ast_channel *chan, void *data)
10020 {
10021    int res = 0;
10022    char *tmp;
10023    struct leave_vm_options leave_options;
10024    struct ast_flags flags = { 0 };
10025    char *opts[OPT_ARG_ARRAY_SIZE];
10026    AST_DECLARE_APP_ARGS(args,
10027       AST_APP_ARG(argv0);
10028       AST_APP_ARG(argv1);
10029    );
10030    
10031    memset(&leave_options, 0, sizeof(leave_options));
10032 
10033    if (chan->_state != AST_STATE_UP)
10034       ast_answer(chan);
10035 
10036    if (!ast_strlen_zero(data)) {
10037       tmp = ast_strdupa(data);
10038       AST_STANDARD_APP_ARGS(args, tmp);
10039       if (args.argc == 2) {
10040          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10041             return -1;
10042          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10043          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10044             int gain;
10045 
10046             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10047                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10048                return -1;
10049             } else {
10050                leave_options.record_gain = (signed char) gain;
10051             }
10052          }
10053          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10054             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10055                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10056          }
10057       }
10058    } else {
10059       char temp[256];
10060       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10061       if (res < 0)
10062          return res;
10063       if (ast_strlen_zero(temp))
10064          return 0;
10065       args.argv0 = ast_strdupa(temp);
10066    }
10067 
10068    res = leave_voicemail(chan, args.argv0, &leave_options);
10069    if (res == 't') {
10070       ast_play_and_wait(chan, "vm-goodbye");
10071       res = 0;
10072    }
10073 
10074    if (res == OPERATOR_EXIT) {
10075       res = 0;
10076    }
10077 
10078    if (res == ERROR_LOCK_PATH) {
10079       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10080       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10081       res = 0;
10082    }
10083 
10084    return res;
10085 }
10086 
10087 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10088 {
10089    struct ast_vm_user *vmu;
10090 
10091    AST_LIST_TRAVERSE(&users, vmu, list) {
10092       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10093          if (strcasecmp(vmu->context, context)) {
10094             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10095                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10096                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10097                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10098          }
10099          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10100          return NULL;
10101       }
10102       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10103          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10104          return NULL;
10105       }
10106    }
10107    
10108    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10109       return NULL;
10110    
10111    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10112    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10113 
10114    AST_LIST_INSERT_TAIL(&users, vmu, list);
10115    
10116    return vmu;
10117 }
10118 
10119 static int append_mailbox(const char *context, const char *box, const char *data)
10120 {
10121    /* Assumes lock is already held */
10122    char *tmp;
10123    char *stringp;
10124    char *s;
10125    struct ast_vm_user *vmu;
10126    char *mailbox_full;
10127    int new = 0, old = 0, urgent = 0;
10128 
10129    tmp = ast_strdupa(data);
10130 
10131    if (!(vmu = find_or_create(context, box)))
10132       return -1;
10133    
10134    populate_defaults(vmu);
10135 
10136    stringp = tmp;
10137    if ((s = strsep(&stringp, ","))) 
10138       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10139    if (stringp && (s = strsep(&stringp, ","))) 
10140       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10141    if (stringp && (s = strsep(&stringp, ","))) 
10142       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10143    if (stringp && (s = strsep(&stringp, ","))) 
10144       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10145    if (stringp && (s = strsep(&stringp, ","))) 
10146       apply_options(vmu, s);
10147 
10148    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10149    strcpy(mailbox_full, box);
10150    strcat(mailbox_full, "@");
10151    strcat(mailbox_full, context);
10152 
10153    inboxcount2(mailbox_full, &urgent, &new, &old);
10154    queue_mwi_event(mailbox_full, urgent, new, old);
10155 
10156    return 0;
10157 }
10158 
10159 static int vm_box_exists(struct ast_channel *chan, void *data) 
10160 {
10161    struct ast_vm_user svm;
10162    char *context, *box;
10163    AST_DECLARE_APP_ARGS(args,
10164       AST_APP_ARG(mbox);
10165       AST_APP_ARG(options);
10166    );
10167    static int dep_warning = 0;
10168 
10169    if (ast_strlen_zero(data)) {
10170       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10171       return -1;
10172    }
10173 
10174    if (!dep_warning) {
10175       dep_warning = 1;
10176       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
10177    }
10178 
10179    box = ast_strdupa(data);
10180 
10181    AST_STANDARD_APP_ARGS(args, box);
10182 
10183    if (args.options) {
10184    }
10185 
10186    if ((context = strchr(args.mbox, '@'))) {
10187       *context = '\0';
10188       context++;
10189    }
10190 
10191    if (find_user(&svm, context, args.mbox)) {
10192       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10193    } else
10194       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10195 
10196    return 0;
10197 }
10198 
10199 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10200 {
10201    struct ast_vm_user svm;
10202    AST_DECLARE_APP_ARGS(arg,
10203       AST_APP_ARG(mbox);
10204       AST_APP_ARG(context);
10205    );
10206 
10207    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10208 
10209    if (ast_strlen_zero(arg.mbox)) {
10210       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10211       return -1;
10212    }
10213 
10214    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10215    return 0;
10216 }
10217 
10218 static struct ast_custom_function mailbox_exists_acf = {
10219    .name = "MAILBOX_EXISTS",
10220    .read = acf_mailbox_exists,
10221 };
10222 
10223 static int vmauthenticate(struct ast_channel *chan, void *data)
10224 {
10225    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
10226    struct ast_vm_user vmus;
10227    char *options = NULL;
10228    int silent = 0, skipuser = 0;
10229    int res = -1;
10230    
10231    if (s) {
10232       s = ast_strdupa(s);
10233       user = strsep(&s, ",");
10234       options = strsep(&s, ",");
10235       if (user) {
10236          s = user;
10237          user = strsep(&s, "@");
10238          context = strsep(&s, "");
10239          if (!ast_strlen_zero(user))
10240             skipuser++;
10241          ast_copy_string(mailbox, user, sizeof(mailbox));
10242       }
10243    }
10244 
10245    if (options) {
10246       silent = (strchr(options, 's')) != NULL;
10247    }
10248 
10249    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10250       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10251       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10252       ast_play_and_wait(chan, "auth-thankyou");
10253       res = 0;
10254    }
10255 
10256    return res;
10257 }
10258 
10259 static char *show_users_realtime(int fd, const char *context)
10260 {
10261    struct ast_config *cfg;
10262    const char *cat = NULL;
10263 
10264    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10265       "context", context, SENTINEL))) {
10266       return CLI_FAILURE;
10267    }
10268 
10269    ast_cli(fd,
10270       "\n"
10271       "=============================================================\n"
10272       "=== Configured Voicemail Users ==============================\n"
10273       "=============================================================\n"
10274       "===\n");
10275 
10276    while ((cat = ast_category_browse(cfg, cat))) {
10277       struct ast_variable *var = NULL;
10278       ast_cli(fd,
10279          "=== Mailbox ...\n"
10280          "===\n");
10281       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10282          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10283       ast_cli(fd,
10284          "===\n"
10285          "=== ---------------------------------------------------------\n"
10286          "===\n");
10287    }
10288 
10289    ast_cli(fd,
10290       "=============================================================\n"
10291       "\n");
10292 
10293    ast_config_destroy(cfg);
10294 
10295    return CLI_SUCCESS;
10296 }
10297 
10298 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10299 {
10300    int which = 0;
10301    int wordlen;
10302    struct ast_vm_user *vmu;
10303    const char *context = "";
10304 
10305    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10306    if (pos > 4)
10307       return NULL;
10308    if (pos == 3)
10309       return (state == 0) ? ast_strdup("for") : NULL;
10310    wordlen = strlen(word);
10311    AST_LIST_TRAVERSE(&users, vmu, list) {
10312       if (!strncasecmp(word, vmu->context, wordlen)) {
10313          if (context && strcmp(context, vmu->context) && ++which > state)
10314             return ast_strdup(vmu->context);
10315          /* ignore repeated contexts ? */
10316          context = vmu->context;
10317       }
10318    }
10319    return NULL;
10320 }
10321 
10322 /*! \brief Show a list of voicemail users in the CLI */
10323 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10324 {
10325    struct ast_vm_user *vmu;
10326 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10327    const char *context = NULL;
10328    int users_counter = 0;
10329 
10330    switch (cmd) {
10331    case CLI_INIT:
10332       e->command = "voicemail show users";
10333       e->usage =
10334          "Usage: voicemail show users [for <context>]\n"
10335          "       Lists all mailboxes currently set up\n";
10336       return NULL;
10337    case CLI_GENERATE:
10338       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10339    }  
10340 
10341    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10342       return CLI_SHOWUSAGE;
10343    if (a->argc == 5) {
10344       if (strcmp(a->argv[3],"for"))
10345          return CLI_SHOWUSAGE;
10346       context = a->argv[4];
10347    }
10348 
10349    if (ast_check_realtime("voicemail")) {
10350       if (!context) {
10351          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10352          return CLI_SHOWUSAGE;
10353       }
10354       return show_users_realtime(a->fd, context);
10355    }
10356 
10357    AST_LIST_LOCK(&users);
10358    if (AST_LIST_EMPTY(&users)) {
10359       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10360       AST_LIST_UNLOCK(&users);
10361       return CLI_FAILURE;
10362    }
10363    if (a->argc == 3)
10364       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10365    else {
10366       int count = 0;
10367       AST_LIST_TRAVERSE(&users, vmu, list) {
10368          if (!strcmp(context, vmu->context))
10369             count++;
10370       }
10371       if (count) {
10372          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10373       } else {
10374          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10375          AST_LIST_UNLOCK(&users);
10376          return CLI_FAILURE;
10377       }
10378    }
10379    AST_LIST_TRAVERSE(&users, vmu, list) {
10380       int newmsgs = 0, oldmsgs = 0;
10381       char count[12], tmp[256] = "";
10382 
10383       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10384          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10385          inboxcount(tmp, &newmsgs, &oldmsgs);
10386          snprintf(count, sizeof(count), "%d", newmsgs);
10387          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10388          users_counter++;
10389       }
10390    }
10391    AST_LIST_UNLOCK(&users);
10392    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10393    return CLI_SUCCESS;
10394 }
10395 
10396 /*! \brief Show a list of voicemail zones in the CLI */
10397 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10398 {
10399    struct vm_zone *zone;
10400 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10401    char *res = CLI_SUCCESS;
10402 
10403    switch (cmd) {
10404    case CLI_INIT:
10405       e->command = "voicemail show zones";
10406       e->usage =
10407          "Usage: voicemail show zones\n"
10408          "       Lists zone message formats\n";
10409       return NULL;
10410    case CLI_GENERATE:
10411       return NULL;
10412    }
10413 
10414    if (a->argc != 3)
10415       return CLI_SHOWUSAGE;
10416 
10417    AST_LIST_LOCK(&zones);
10418    if (!AST_LIST_EMPTY(&zones)) {
10419       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10420       AST_LIST_TRAVERSE(&zones, zone, list) {
10421          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10422       }
10423    } else {
10424       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10425       res = CLI_FAILURE;
10426    }
10427    AST_LIST_UNLOCK(&zones);
10428 
10429    return res;
10430 }
10431 
10432 /*! \brief Reload voicemail configuration from the CLI */
10433 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10434 {
10435    switch (cmd) {
10436    case CLI_INIT:
10437       e->command = "voicemail reload";
10438       e->usage =
10439          "Usage: voicemail reload\n"
10440          "       Reload voicemail configuration\n";
10441       return NULL;
10442    case CLI_GENERATE:
10443       return NULL;
10444    }
10445 
10446    if (a->argc != 2)
10447       return CLI_SHOWUSAGE;
10448 
10449    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10450    load_config(1);
10451    
10452    return CLI_SUCCESS;
10453 }
10454 
10455 static struct ast_cli_entry cli_voicemail[] = {
10456    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10457    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10458    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10459 };
10460 
10461 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10462 {
10463    int new = 0, old = 0, urgent = 0;
10464 
10465    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10466 
10467    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10468       mwi_sub->old_urgent = urgent;
10469       mwi_sub->old_new = new;
10470       mwi_sub->old_old = old;
10471       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10472    }
10473 }
10474 
10475 static void poll_subscribed_mailboxes(void)
10476 {
10477    struct mwi_sub *mwi_sub;
10478 
10479    AST_RWLIST_RDLOCK(&mwi_subs);
10480    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10481       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10482          poll_subscribed_mailbox(mwi_sub);
10483       }
10484    }
10485    AST_RWLIST_UNLOCK(&mwi_subs);
10486 }
10487 
10488 static void *mb_poll_thread(void *data)
10489 {
10490    while (poll_thread_run) {
10491       struct timespec ts = { 0, };
10492       struct timeval wait;
10493 
10494       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10495       ts.tv_sec = wait.tv_sec;
10496       ts.tv_nsec = wait.tv_usec * 1000;
10497 
10498       ast_mutex_lock(&poll_lock);
10499       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10500       ast_mutex_unlock(&poll_lock);
10501 
10502       if (!poll_thread_run)
10503          break;
10504 
10505       poll_subscribed_mailboxes();
10506    }
10507 
10508    return NULL;
10509 }
10510 
10511 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10512 {
10513    ast_free(mwi_sub);
10514 }
10515 
10516 static int handle_unsubscribe(void *datap)
10517 {
10518    struct mwi_sub *mwi_sub;
10519    uint32_t *uniqueid = datap;
10520    
10521    AST_RWLIST_WRLOCK(&mwi_subs);
10522    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10523       if (mwi_sub->uniqueid == *uniqueid) {
10524          AST_LIST_REMOVE_CURRENT(entry);
10525          break;
10526       }
10527    }
10528    AST_RWLIST_TRAVERSE_SAFE_END
10529    AST_RWLIST_UNLOCK(&mwi_subs);
10530 
10531    if (mwi_sub)
10532       mwi_sub_destroy(mwi_sub);
10533 
10534    ast_free(uniqueid);  
10535    return 0;
10536 }
10537 
10538 static int handle_subscribe(void *datap)
10539 {
10540    unsigned int len;
10541    struct mwi_sub *mwi_sub;
10542    struct mwi_sub_task *p = datap;
10543 
10544    len = sizeof(*mwi_sub);
10545    if (!ast_strlen_zero(p->mailbox))
10546       len += strlen(p->mailbox);
10547 
10548    if (!ast_strlen_zero(p->context))
10549       len += strlen(p->context) + 1; /* Allow for seperator */
10550 
10551    if (!(mwi_sub = ast_calloc(1, len)))
10552       return -1;
10553 
10554    mwi_sub->uniqueid = p->uniqueid;
10555    if (!ast_strlen_zero(p->mailbox))
10556       strcpy(mwi_sub->mailbox, p->mailbox);
10557 
10558    if (!ast_strlen_zero(p->context)) {
10559       strcat(mwi_sub->mailbox, "@");
10560       strcat(mwi_sub->mailbox, p->context);
10561    }
10562 
10563    AST_RWLIST_WRLOCK(&mwi_subs);
10564    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10565    AST_RWLIST_UNLOCK(&mwi_subs);
10566    ast_free((void *) p->mailbox);
10567    ast_free((void *) p->context);
10568    ast_free(p);
10569    poll_subscribed_mailbox(mwi_sub);
10570    return 0;
10571 }
10572 
10573 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10574 {
10575    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10576    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10577       return;
10578 
10579    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10580       return;
10581 
10582    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10583    *uniqueid = u;
10584    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10585       ast_free(uniqueid);
10586    }
10587 }
10588 
10589 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10590 {
10591    struct mwi_sub_task *mwist;
10592    
10593    if (ast_event_get_type(event) != AST_EVENT_SUB)
10594       return;
10595 
10596    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10597       return;
10598 
10599    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10600       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10601       return;
10602    }
10603    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10604    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10605    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10606    
10607    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10608       ast_free(mwist);
10609    }
10610 }
10611 
10612 static void start_poll_thread(void)
10613 {
10614    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10615       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10616       AST_EVENT_IE_END);
10617 
10618    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10619       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10620       AST_EVENT_IE_END);
10621 
10622    if (mwi_sub_sub)
10623       ast_event_report_subs(mwi_sub_sub);
10624 
10625    poll_thread_run = 1;
10626 
10627    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10628 }
10629 
10630 static void stop_poll_thread(void)
10631 {
10632    poll_thread_run = 0;
10633 
10634    if (mwi_sub_sub) {
10635       ast_event_unsubscribe(mwi_sub_sub);
10636       mwi_sub_sub = NULL;
10637    }
10638 
10639    if (mwi_unsub_sub) {
10640       ast_event_unsubscribe(mwi_unsub_sub);
10641       mwi_unsub_sub = NULL;
10642    }
10643 
10644    ast_mutex_lock(&poll_lock);
10645    ast_cond_signal(&poll_cond);
10646    ast_mutex_unlock(&poll_lock);
10647 
10648    pthread_join(poll_thread, NULL);
10649 
10650    poll_thread = AST_PTHREADT_NULL;
10651 }
10652 
10653 /*! \brief Manager list voicemail users command */
10654 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10655 {
10656    struct ast_vm_user *vmu = NULL;
10657    const char *id = astman_get_header(m, "ActionID");
10658    char actionid[128] = "";
10659 
10660    if (!ast_strlen_zero(id))
10661       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10662 
10663    AST_LIST_LOCK(&users);
10664 
10665    if (AST_LIST_EMPTY(&users)) {
10666       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10667       AST_LIST_UNLOCK(&users);
10668       return RESULT_SUCCESS;
10669    }
10670    
10671    astman_send_ack(s, m, "Voicemail user list will follow");
10672    
10673    AST_LIST_TRAVERSE(&users, vmu, list) {
10674       char dirname[256];
10675 
10676 #ifdef IMAP_STORAGE
10677       int new, old;
10678       inboxcount(vmu->mailbox, &new, &old);
10679 #endif
10680       
10681       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10682       astman_append(s,
10683          "%s"
10684          "Event: VoicemailUserEntry\r\n"
10685          "VMContext: %s\r\n"
10686          "VoiceMailbox: %s\r\n"
10687          "Fullname: %s\r\n"
10688          "Email: %s\r\n"
10689          "Pager: %s\r\n"
10690          "ServerEmail: %s\r\n"
10691          "MailCommand: %s\r\n"
10692          "Language: %s\r\n"
10693          "TimeZone: %s\r\n"
10694          "Callback: %s\r\n"
10695          "Dialout: %s\r\n"
10696          "UniqueID: %s\r\n"
10697          "ExitContext: %s\r\n"
10698          "SayDurationMinimum: %d\r\n"
10699          "SayEnvelope: %s\r\n"
10700          "SayCID: %s\r\n"
10701          "AttachMessage: %s\r\n"
10702          "AttachmentFormat: %s\r\n"
10703          "DeleteMessage: %s\r\n"
10704          "VolumeGain: %.2f\r\n"
10705          "CanReview: %s\r\n"
10706          "CallOperator: %s\r\n"
10707          "MaxMessageCount: %d\r\n"
10708          "MaxMessageLength: %d\r\n"
10709          "NewMessageCount: %d\r\n"
10710 #ifdef IMAP_STORAGE
10711          "OldMessageCount: %d\r\n"
10712          "IMAPUser: %s\r\n"
10713 #endif
10714          "\r\n",
10715          actionid,
10716          vmu->context,
10717          vmu->mailbox,
10718          vmu->fullname,
10719          vmu->email,
10720          vmu->pager,
10721          vmu->serveremail,
10722          vmu->mailcmd,
10723          vmu->language,
10724          vmu->zonetag,
10725          vmu->callback,
10726          vmu->dialout,
10727          vmu->uniqueid,
10728          vmu->exit,
10729          vmu->saydurationm,
10730          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10731          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10732          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10733          vmu->attachfmt,
10734          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10735          vmu->volgain,
10736          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10737          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10738          vmu->maxmsg,
10739          vmu->maxsecs,
10740 #ifdef IMAP_STORAGE
10741          new, old, vmu->imapuser
10742 #else
10743          count_messages(vmu, dirname)
10744 #endif
10745          );
10746    }     
10747    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10748 
10749    AST_LIST_UNLOCK(&users);
10750 
10751    return RESULT_SUCCESS;
10752 }
10753 
10754 /*! \brief Free the users structure. */
10755 static void free_vm_users(void) 
10756 {
10757    struct ast_vm_user *current;
10758    AST_LIST_LOCK(&users);
10759    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10760       ast_set_flag(current, VM_ALLOCED);
10761       free_user(current);
10762    }
10763    AST_LIST_UNLOCK(&users);
10764 }
10765 
10766 /*! \brief Free the zones structure. */
10767 static void free_vm_zones(void)
10768 {
10769    struct vm_zone *zcur;
10770    AST_LIST_LOCK(&zones);
10771    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10772       free_zone(zcur);
10773    AST_LIST_UNLOCK(&zones);
10774 }
10775 
10776 static const char *substitute_escapes(const char *value)
10777 {
10778    char *current;
10779 
10780    /* Add 16 for fudge factor */
10781    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10782 
10783    ast_str_reset(str);
10784    
10785    /* Substitute strings \r, \n, and \t into the appropriate characters */
10786    for (current = (char *) value; *current; current++) {
10787       if (*current == '\\') {
10788          current++;
10789          if (!*current) {
10790             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10791             break;
10792          }
10793          switch (*current) {
10794          case 'r':
10795             ast_str_append(&str, 0, "\r");
10796             break;
10797          case 'n':
10798 #ifdef IMAP_STORAGE
10799             if (!str->used || str->str[str->used - 1] != '\r') {
10800                ast_str_append(&str, 0, "\r");
10801             }
10802 #endif
10803             ast_str_append(&str, 0, "\n");
10804             break;
10805          case 't':
10806             ast_str_append(&str, 0, "\t");
10807             break;
10808          default:
10809             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10810             break;
10811          }
10812       } else {
10813          ast_str_append(&str, 0, "%c", *current);
10814       }
10815    }
10816 
10817    return ast_str_buffer(str);
10818 }
10819 
10820 static int load_config(int reload)
10821 {
10822    struct ast_vm_user *current;
10823    struct ast_config *cfg, *ucfg;
10824    char *cat;
10825    struct ast_variable *var;
10826    const char *val;
10827    char *q, *stringp, *tmp;
10828    int x;
10829    int tmpadsi[4];
10830    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10831 
10832    ast_unload_realtime("voicemail");
10833    ast_unload_realtime("voicemail_data");
10834 
10835    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10836       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10837          return 0;
10838       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10839          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10840          ucfg = NULL;
10841       }
10842       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10843       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10844          ast_config_destroy(ucfg);
10845          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10846          return 0;
10847       }
10848    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10849       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10850       return 0;
10851    } else {
10852       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10853       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10854          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10855          ucfg = NULL;
10856       }
10857    }
10858 #ifdef IMAP_STORAGE
10859    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10860 #endif
10861    /* set audio control prompts */
10862    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10863    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10864    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10865    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10866    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10867 
10868    /* Free all the users structure */  
10869    free_vm_users();
10870 
10871    /* Free all the zones structure */
10872    free_vm_zones();
10873 
10874    AST_LIST_LOCK(&users);  
10875 
10876    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10877    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10878 
10879    if (cfg) {
10880       /* General settings */
10881 
10882       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10883          val = "default";
10884       ast_copy_string(userscontext, val, sizeof(userscontext));
10885       /* Attach voice message to mail message ? */
10886       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10887          val = "yes";
10888       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10889 
10890       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10891          val = "no";
10892       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10893 
10894       volgain = 0.0;
10895       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10896          sscanf(val, "%30lf", &volgain);
10897 
10898 #ifdef ODBC_STORAGE
10899       strcpy(odbc_database, "asterisk");
10900       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10901          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10902       }
10903       strcpy(odbc_table, "voicemessages");
10904       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10905          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10906       }
10907 #endif      
10908       /* Mail command */
10909       strcpy(mailcmd, SENDMAIL);
10910       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10911          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10912 
10913       maxsilence = 0;
10914       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10915          maxsilence = atoi(val);
10916          if (maxsilence > 0)
10917             maxsilence *= 1000;
10918       }
10919       
10920       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10921          maxmsg = MAXMSG;
10922       } else {
10923          maxmsg = atoi(val);
10924          if (maxmsg <= 0) {
10925             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10926             maxmsg = MAXMSG;
10927          } else if (maxmsg > MAXMSGLIMIT) {
10928             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10929             maxmsg = MAXMSGLIMIT;
10930          }
10931       }
10932 
10933       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10934          maxdeletedmsg = 0;
10935       } else {
10936          if (sscanf(val, "%30d", &x) == 1)
10937             maxdeletedmsg = x;
10938          else if (ast_true(val))
10939             maxdeletedmsg = MAXMSG;
10940          else
10941             maxdeletedmsg = 0;
10942 
10943          if (maxdeletedmsg < 0) {
10944             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10945             maxdeletedmsg = MAXMSG;
10946          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10947             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10948             maxdeletedmsg = MAXMSGLIMIT;
10949          }
10950       }
10951 
10952       /* Load date format config for voicemail mail */
10953       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10954          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10955       }
10956 
10957       /* External password changing command */
10958       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10959          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10960          pwdchange = PWDCHANGE_EXTERNAL;
10961       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10962          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10963          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10964       }
10965  
10966       /* External password validation command */
10967       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10968          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10969          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10970       }
10971 
10972 #ifdef IMAP_STORAGE
10973       /* IMAP server address */
10974       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10975          ast_copy_string(imapserver, val, sizeof(imapserver));
10976       } else {
10977          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10978       }
10979       /* IMAP server port */
10980       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10981          ast_copy_string(imapport, val, sizeof(imapport));
10982       } else {
10983          ast_copy_string(imapport,"143", sizeof(imapport));
10984       }
10985       /* IMAP server flags */
10986       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10987          ast_copy_string(imapflags, val, sizeof(imapflags));
10988       }
10989       /* IMAP server master username */
10990       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10991          ast_copy_string(authuser, val, sizeof(authuser));
10992       }
10993       /* IMAP server master password */
10994       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10995          ast_copy_string(authpassword, val, sizeof(authpassword));
10996       }
10997       /* Expunge on exit */
10998       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10999          if (ast_false(val))
11000             expungeonhangup = 0;
11001          else
11002             expungeonhangup = 1;
11003       } else {
11004          expungeonhangup = 1;
11005       }
11006       /* IMAP voicemail folder */
11007       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11008          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11009       } else {
11010          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
11011       }
11012       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11013          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11014       }
11015       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11016          imapgreetings = ast_true(val);
11017       } else {
11018          imapgreetings = 0;
11019       }
11020       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11021          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11022       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11023          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11024          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11025       } else {
11026          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11027       }
11028 
11029       /* There is some very unorthodox casting done here. This is due
11030        * to the way c-client handles the argument passed in. It expects a 
11031        * void pointer and casts the pointer directly to a long without
11032        * first dereferencing it. */
11033       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11034          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11035       } else {
11036          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11037       }
11038 
11039       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11040          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11041       } else {
11042          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11043       }
11044 
11045       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11046          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11047       } else {
11048          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11049       }
11050 
11051       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11052          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11053       } else {
11054          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11055       }
11056 
11057       /* Increment configuration version */
11058       imapversion++;
11059 #endif
11060       /* External voicemail notify application */
11061       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11062          ast_copy_string(externnotify, val, sizeof(externnotify));
11063          ast_debug(1, "found externnotify: %s\n", externnotify);
11064       } else {
11065          externnotify[0] = '\0';
11066       }
11067 
11068       /* SMDI voicemail notification */
11069       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11070          ast_debug(1, "Enabled SMDI voicemail notification\n");
11071          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11072             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
11073          } else {
11074             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11075             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
11076          }
11077          if (!smdi_iface) {
11078             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11079          } 
11080       }
11081 
11082       /* Silence treshold */
11083       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11084       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11085          silencethreshold = atoi(val);
11086       
11087       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11088          val = ASTERISK_USERNAME;
11089       ast_copy_string(serveremail, val, sizeof(serveremail));
11090       
11091       vmmaxsecs = 0;
11092       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11093          if (sscanf(val, "%30d", &x) == 1) {
11094             vmmaxsecs = x;
11095          } else {
11096             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11097          }
11098       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11099          static int maxmessage_deprecate = 0;
11100          if (maxmessage_deprecate == 0) {
11101             maxmessage_deprecate = 1;
11102             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11103          }
11104          if (sscanf(val, "%30d", &x) == 1) {
11105             vmmaxsecs = x;
11106          } else {
11107             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11108          }
11109       }
11110 
11111       vmminsecs = 0;
11112       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11113          if (sscanf(val, "%30d", &x) == 1) {
11114             vmminsecs = x;
11115             if (maxsilence / 1000 >= vmminsecs) {
11116                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11117             }
11118          } else {
11119             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11120          }
11121       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11122          static int maxmessage_deprecate = 0;
11123          if (maxmessage_deprecate == 0) {
11124             maxmessage_deprecate = 1;
11125             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11126          }
11127          if (sscanf(val, "%30d", &x) == 1) {
11128             vmminsecs = x;
11129             if (maxsilence / 1000 >= vmminsecs) {
11130                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11131             }
11132          } else {
11133             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11134          }
11135       }
11136 
11137       val = ast_variable_retrieve(cfg, "general", "format");
11138       if (!val) {
11139          val = "wav";   
11140       } else {
11141          tmp = ast_strdupa(val);
11142          val = ast_format_str_reduce(tmp);
11143          if (!val) {
11144             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11145             val = "wav";
11146          }
11147       }
11148       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11149 
11150       skipms = 3000;
11151       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11152          if (sscanf(val, "%30d", &x) == 1) {
11153             maxgreet = x;
11154          } else {
11155             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11156          }
11157       }
11158 
11159       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11160          if (sscanf(val, "%30d", &x) == 1) {
11161             skipms = x;
11162          } else {
11163             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11164          }
11165       }
11166 
11167       maxlogins = 3;
11168       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11169          if (sscanf(val, "%30d", &x) == 1) {
11170             maxlogins = x;
11171          } else {
11172             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11173          }
11174       }
11175 
11176       minpassword = MINPASSWORD;
11177       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11178          if (sscanf(val, "%30d", &x) == 1) {
11179             minpassword = x;
11180          } else {
11181             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11182          }
11183       }
11184 
11185       /* Force new user to record name ? */
11186       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11187          val = "no";
11188       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11189 
11190       /* Force new user to record greetings ? */
11191       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11192          val = "no";
11193       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11194 
11195       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11196          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11197          stringp = ast_strdupa(val);
11198          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11199             if (!ast_strlen_zero(stringp)) {
11200                q = strsep(&stringp, ",");
11201                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11202                   q++;
11203                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11204                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11205             } else {
11206                cidinternalcontexts[x][0] = '\0';
11207             }
11208          }
11209       }
11210       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11211          ast_debug(1,"VM Review Option disabled globally\n");
11212          val = "no";
11213       }
11214       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11215 
11216       /* Temporary greeting reminder */
11217       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11218          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11219          val = "no";
11220       } else {
11221          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11222       }
11223       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11224       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11225          ast_debug(1, "VM next message wrap disabled globally\n");
11226          val = "no";
11227       }
11228       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11229 
11230       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11231          ast_debug(1,"VM Operator break disabled globally\n");
11232          val = "no";
11233       }
11234       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11235 
11236       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11237          ast_debug(1,"VM CID Info before msg disabled globally\n");
11238          val = "no";
11239       } 
11240       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11241 
11242       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
11243          ast_debug(1,"Send Voicemail msg disabled globally\n");
11244          val = "no";
11245       }
11246       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11247    
11248       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11249          ast_debug(1,"ENVELOPE before msg enabled globally\n");
11250          val = "yes";
11251       }
11252       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11253 
11254       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11255          ast_debug(1,"Move Heard enabled globally\n");
11256          val = "yes";
11257       }
11258       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11259 
11260       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11261          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11262          val = "no";
11263       }
11264       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11265 
11266       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11267          ast_debug(1,"Duration info before msg enabled globally\n");
11268          val = "yes";
11269       }
11270       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11271 
11272       saydurationminfo = 2;
11273       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11274          if (sscanf(val, "%30d", &x) == 1) {
11275             saydurationminfo = x;
11276          } else {
11277             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11278          }
11279       }
11280 
11281       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11282          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
11283          val = "no";
11284       }
11285       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11286 
11287       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11288          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11289          ast_debug(1, "found dialout context: %s\n", dialcontext);
11290       } else {
11291          dialcontext[0] = '\0';  
11292       }
11293       
11294       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11295          ast_copy_string(callcontext, val, sizeof(callcontext));
11296          ast_debug(1, "found callback context: %s\n", callcontext);
11297       } else {
11298          callcontext[0] = '\0';
11299       }
11300 
11301       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11302          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11303          ast_debug(1, "found operator context: %s\n", exitcontext);
11304       } else {
11305          exitcontext[0] = '\0';
11306       }
11307       
11308       /* load password sounds configuration */
11309       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11310          ast_copy_string(vm_password, val, sizeof(vm_password));
11311       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11312          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11313       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11314          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11315       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11316          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11317       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11318          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11319       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11320          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11321       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11322          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11323       }
11324       /* load configurable audio prompts */
11325       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11326          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11327       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11328          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11329       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11330          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11331       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11332          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11333       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11334          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11335 
11336       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11337          val = "no";
11338       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11339 
11340       poll_freq = DEFAULT_POLL_FREQ;
11341       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11342          if (sscanf(val, "%30u", &poll_freq) != 1) {
11343             poll_freq = DEFAULT_POLL_FREQ;
11344             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11345          }
11346       }
11347 
11348       poll_mailboxes = 0;
11349       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11350          poll_mailboxes = ast_true(val);
11351 
11352       if (ucfg) { 
11353          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11354             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11355                continue;
11356             if ((current = find_or_create(userscontext, cat))) {
11357                populate_defaults(current);
11358                apply_options_full(current, ast_variable_browse(ucfg, cat));
11359                ast_copy_string(current->context, userscontext, sizeof(current->context));
11360             }
11361          }
11362          ast_config_destroy(ucfg);
11363       }
11364       cat = ast_category_browse(cfg, NULL);
11365       while (cat) {
11366          if (strcasecmp(cat, "general")) {
11367             var = ast_variable_browse(cfg, cat);
11368             if (strcasecmp(cat, "zonemessages")) {
11369                /* Process mailboxes in this context */
11370                while (var) {
11371                   append_mailbox(cat, var->name, var->value);
11372                   var = var->next;
11373                }
11374             } else {
11375                /* Timezones in this context */
11376                while (var) {
11377                   struct vm_zone *z;
11378                   if ((z = ast_malloc(sizeof(*z)))) {
11379                      char *msg_format, *tzone;
11380                      msg_format = ast_strdupa(var->value);
11381                      tzone = strsep(&msg_format, "|,");
11382                      if (msg_format) {
11383                         ast_copy_string(z->name, var->name, sizeof(z->name));
11384                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11385                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11386                         AST_LIST_LOCK(&zones);
11387                         AST_LIST_INSERT_HEAD(&zones, z, list);
11388                         AST_LIST_UNLOCK(&zones);
11389                      } else {
11390                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11391                         ast_free(z);
11392                      }
11393                   } else {
11394                      AST_LIST_UNLOCK(&users);
11395                      ast_config_destroy(cfg);
11396                      return -1;
11397                   }
11398                   var = var->next;
11399                }
11400             }
11401          }
11402          cat = ast_category_browse(cfg, cat);
11403       }
11404       memset(fromstring, 0, sizeof(fromstring));
11405       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11406       strcpy(charset, "ISO-8859-1");
11407       if (emailbody) {
11408          ast_free(emailbody);
11409          emailbody = NULL;
11410       }
11411       if (emailsubject) {
11412          ast_free(emailsubject);
11413          emailsubject = NULL;
11414       }
11415       if (pagerbody) {
11416          ast_free(pagerbody);
11417          pagerbody = NULL;
11418       }
11419       if (pagersubject) {
11420          ast_free(pagersubject);
11421          pagersubject = NULL;
11422       }
11423       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11424          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11425       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11426          ast_copy_string(fromstring, val, sizeof(fromstring));
11427       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11428          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11429       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11430          ast_copy_string(charset, val, sizeof(charset));
11431       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11432          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11433          for (x = 0; x < 4; x++) {
11434             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11435          }
11436       }
11437       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11438          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11439          for (x = 0; x < 4; x++) {
11440             memcpy(&adsisec[x], &tmpadsi[x], 1);
11441          }
11442       }
11443       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11444          if (atoi(val)) {
11445             adsiver = atoi(val);
11446          }
11447       }
11448       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11449          ast_copy_string(zonetag, val, sizeof(zonetag));
11450       }
11451       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11452          emailsubject = ast_strdup(val);
11453       }
11454       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11455          emailbody = ast_strdup(substitute_escapes(val));
11456       }
11457       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11458          pagersubject = ast_strdup(val);
11459       }
11460       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11461          pagerbody = ast_strdup(substitute_escapes(val));
11462       }
11463       AST_LIST_UNLOCK(&users);
11464       ast_config_destroy(cfg);
11465 
11466       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11467          start_poll_thread();
11468       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11469          stop_poll_thread();;
11470 
11471       return 0;
11472    } else {
11473       AST_LIST_UNLOCK(&users);
11474       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11475       if (ucfg)
11476          ast_config_destroy(ucfg);
11477       return 0;
11478    }
11479 }
11480 
11481 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11482 {
11483    int res = -1;
11484    char dir[PATH_MAX];
11485    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11486    ast_debug(2, "About to try retrieving name file %s\n", dir);
11487    RETRIEVE(dir, -1, mailbox, context);
11488    if (ast_fileexists(dir, NULL, NULL)) {
11489       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11490    }
11491    DISPOSE(dir, -1);
11492    return res;
11493 }
11494 
11495 static int reload(void)
11496 {
11497    return load_config(1);
11498 }
11499 
11500 static int unload_module(void)
11501 {
11502    int res;
11503 
11504    res = ast_unregister_application(app);
11505    res |= ast_unregister_application(app2);
11506    res |= ast_unregister_application(app3);
11507    res |= ast_unregister_application(app4);
11508    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11509    res |= ast_manager_unregister("VoicemailUsersList");
11510    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11511    ast_uninstall_vm_functions();
11512    ao2_ref(inprocess_container, -1);
11513 
11514    if (poll_thread != AST_PTHREADT_NULL)
11515       stop_poll_thread();
11516 
11517    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11518    ast_unload_realtime("voicemail");
11519    ast_unload_realtime("voicemail_data");
11520 
11521    free_vm_users();
11522    free_vm_zones();
11523    return res;
11524 }
11525 
11526 static int load_module(void)
11527 {
11528    int res;
11529    my_umask = umask(0);
11530    umask(my_umask);
11531 
11532    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11533       return AST_MODULE_LOAD_DECLINE;
11534    }
11535 
11536    /* compute the location of the voicemail spool directory */
11537    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11538 
11539    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11540       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11541    }
11542 
11543    if ((res = load_config(0)))
11544       return res;
11545 
11546    res = ast_register_application_xml(app, vm_exec);
11547    res |= ast_register_application_xml(app2, vm_execmain);
11548    res |= ast_register_application_xml(app3, vm_box_exists);
11549    res |= ast_register_application_xml(app4, vmauthenticate);
11550    res |= ast_custom_function_register(&mailbox_exists_acf);
11551    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11552    if (res)
11553       return res;
11554 
11555    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11556 
11557    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11558    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11559    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11560 
11561    return res;
11562 }
11563 
11564 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11565 {
11566    int cmd = 0;
11567    char destination[80] = "";
11568    int retries = 0;
11569 
11570    if (!num) {
11571       ast_verb(3, "Destination number will be entered manually\n");
11572       while (retries < 3 && cmd != 't') {
11573          destination[1] = '\0';
11574          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11575          if (!cmd)
11576             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11577          if (!cmd)
11578             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11579          if (!cmd) {
11580             cmd = ast_waitfordigit(chan, 6000);
11581             if (cmd)
11582                destination[0] = cmd;
11583          }
11584          if (!cmd) {
11585             retries++;
11586          } else {
11587 
11588             if (cmd < 0)
11589                return 0;
11590             if (cmd == '*') {
11591                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11592                return 0;
11593             }
11594             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11595                retries++;
11596             else
11597                cmd = 't';
11598          }
11599       }
11600       if (retries >= 3) {
11601          return 0;
11602       }
11603       
11604    } else {
11605       if (option_verbose > 2)
11606          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11607       ast_copy_string(destination, num, sizeof(destination));
11608    }
11609 
11610    if (!ast_strlen_zero(destination)) {
11611       if (destination[strlen(destination) -1 ] == '*')
11612          return 0; 
11613       if (option_verbose > 2)
11614          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11615       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11616       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11617       chan->priority = 0;
11618       return 9;
11619    }
11620    return 0;
11621 }
11622 
11623 /*!
11624  * \brief The advanced options within a message.
11625  * \param chan
11626  * \param vmu 
11627  * \param vms
11628  * \param msg
11629  * \param option
11630  * \param record_gain
11631  *
11632  * Provides handling for the play message envelope, call the person back, or reply to message. 
11633  *
11634  * \return zero on success, -1 on error.
11635  */
11636 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)
11637 {
11638    int res = 0;
11639    char filename[PATH_MAX];
11640    struct ast_config *msg_cfg = NULL;
11641    const char *origtime, *context;
11642    char *name, *num;
11643    int retries = 0;
11644    char *cid;
11645    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11646 
11647    vms->starting = 0; 
11648 
11649    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11650 
11651    /* Retrieve info from VM attribute file */
11652    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11653    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11654    msg_cfg = ast_config_load(filename, config_flags);
11655    DISPOSE(vms->curdir, vms->curmsg);
11656    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11657       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11658       return 0;
11659    }
11660 
11661    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11662       ast_config_destroy(msg_cfg);
11663       return 0;
11664    }
11665 
11666    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11667 
11668    context = ast_variable_retrieve(msg_cfg, "message", "context");
11669    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11670       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11671    switch (option) {
11672    case 3: /* Play message envelope */
11673       if (!res)
11674          res = play_message_datetime(chan, vmu, origtime, filename);
11675       if (!res)
11676          res = play_message_callerid(chan, vms, cid, context, 0);
11677 
11678       res = 't';
11679       break;
11680 
11681    case 2:  /* Call back */
11682 
11683       if (ast_strlen_zero(cid))
11684          break;
11685 
11686       ast_callerid_parse(cid, &name, &num);
11687       while ((res > -1) && (res != 't')) {
11688          switch (res) {
11689          case '1':
11690             if (num) {
11691                /* Dial the CID number */
11692                res = dialout(chan, vmu, num, vmu->callback);
11693                if (res) {
11694                   ast_config_destroy(msg_cfg);
11695                   return 9;
11696                }
11697             } else {
11698                res = '2';
11699             }
11700             break;
11701 
11702          case '2':
11703             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11704             if (!ast_strlen_zero(vmu->dialout)) {
11705                res = dialout(chan, vmu, NULL, vmu->dialout);
11706                if (res) {
11707                   ast_config_destroy(msg_cfg);
11708                   return 9;
11709                }
11710             } else {
11711                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11712                res = ast_play_and_wait(chan, "vm-sorry");
11713             }
11714             ast_config_destroy(msg_cfg);
11715             return res;
11716          case '*':
11717             res = 't';
11718             break;
11719          case '3':
11720          case '4':
11721          case '5':
11722          case '6':
11723          case '7':
11724          case '8':
11725          case '9':
11726          case '0':
11727 
11728             res = ast_play_and_wait(chan, "vm-sorry");
11729             retries++;
11730             break;
11731          default:
11732             if (num) {
11733                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11734                res = ast_play_and_wait(chan, "vm-num-i-have");
11735                if (!res)
11736                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11737                if (!res)
11738                   res = ast_play_and_wait(chan, "vm-tocallnum");
11739                /* Only prompt for a caller-specified number if there is a dialout context specified */
11740                if (!ast_strlen_zero(vmu->dialout)) {
11741                   if (!res)
11742                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11743                }
11744             } else {
11745                res = ast_play_and_wait(chan, "vm-nonumber");
11746                if (!ast_strlen_zero(vmu->dialout)) {
11747                   if (!res)
11748                      res = ast_play_and_wait(chan, "vm-toenternumber");
11749                }
11750             }
11751             if (!res)
11752                res = ast_play_and_wait(chan, "vm-star-cancel");
11753             if (!res)
11754                res = ast_waitfordigit(chan, 6000);
11755             if (!res) {
11756                retries++;
11757                if (retries > 3)
11758                   res = 't';
11759             }
11760             break; 
11761             
11762          }
11763          if (res == 't')
11764             res = 0;
11765          else if (res == '*')
11766             res = -1;
11767       }
11768       break;
11769       
11770    case 1:  /* Reply */
11771       /* Send reply directly to sender */
11772       if (ast_strlen_zero(cid))
11773          break;
11774 
11775       ast_callerid_parse(cid, &name, &num);
11776       if (!num) {
11777          ast_verb(3, "No CID number available, no reply sent\n");
11778          if (!res)
11779             res = ast_play_and_wait(chan, "vm-nonumber");
11780          ast_config_destroy(msg_cfg);
11781          return res;
11782       } else {
11783          struct ast_vm_user vmu2;
11784          if (find_user(&vmu2, vmu->context, num)) {
11785             struct leave_vm_options leave_options;
11786             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11787             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11788 
11789             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11790             
11791             memset(&leave_options, 0, sizeof(leave_options));
11792             leave_options.record_gain = record_gain;
11793             res = leave_voicemail(chan, mailbox, &leave_options);
11794             if (!res)
11795                res = 't';
11796             ast_config_destroy(msg_cfg);
11797             return res;
11798          } else {
11799             /* Sender has no mailbox, can't reply */
11800             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11801             ast_play_and_wait(chan, "vm-nobox");
11802             res = 't';
11803             ast_config_destroy(msg_cfg);
11804             return res;
11805          }
11806       } 
11807       res = 0;
11808 
11809       break;
11810    }
11811 
11812 #ifndef IMAP_STORAGE
11813    ast_config_destroy(msg_cfg);
11814 
11815    if (!res) {
11816       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11817       vms->heard[msg] = 1;
11818       res = wait_file(chan, vms, vms->fn);
11819    }
11820 #endif
11821    return res;
11822 }
11823 
11824 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11825          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11826          signed char record_gain, struct vm_state *vms, char *flag)
11827 {
11828    /* Record message & let caller review or re-record it, or set options if applicable */
11829    int res = 0;
11830    int cmd = 0;
11831    int max_attempts = 3;
11832    int attempts = 0;
11833    int recorded = 0;
11834    int msg_exists = 0;
11835    signed char zero_gain = 0;
11836    char tempfile[PATH_MAX];
11837    char *acceptdtmf = "#";
11838    char *canceldtmf = "";
11839    int canceleddtmf = 0;
11840 
11841    /* Note that urgent and private are for flagging messages as such in the future */
11842 
11843    /* barf if no pointer passed to store duration in */
11844    if (duration == NULL) {
11845       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11846       return -1;
11847    }
11848 
11849    if (!outsidecaller)
11850       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11851    else
11852       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11853 
11854    cmd = '3';  /* Want to start by recording */
11855 
11856    while ((cmd >= 0) && (cmd != 't')) {
11857       switch (cmd) {
11858       case '1':
11859          if (!msg_exists) {
11860             /* In this case, 1 is to record a message */
11861             cmd = '3';
11862             break;
11863          } else {
11864             /* Otherwise 1 is to save the existing message */
11865             ast_verb(3, "Saving message as is\n");
11866             if (!outsidecaller) 
11867                ast_filerename(tempfile, recordfile, NULL);
11868             ast_stream_and_wait(chan, "vm-msgsaved", "");
11869             if (!outsidecaller) {
11870                /* Saves to IMAP server only if imapgreeting=yes */
11871                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11872                DISPOSE(recordfile, -1);
11873             }
11874             cmd = 't';
11875             return res;
11876          }
11877       case '2':
11878          /* Review */
11879          ast_verb(3, "Reviewing the message\n");
11880          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11881          break;
11882       case '3':
11883          msg_exists = 0;
11884          /* Record */
11885          if (recorded == 1) 
11886             ast_verb(3, "Re-recording the message\n");
11887          else  
11888             ast_verb(3, "Recording the message\n");
11889          
11890          if (recorded && outsidecaller) {
11891             cmd = ast_play_and_wait(chan, INTRO);
11892             cmd = ast_play_and_wait(chan, "beep");
11893          }
11894          recorded = 1;
11895          /* 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 */
11896          if (record_gain)
11897             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11898          if (ast_test_flag(vmu, VM_OPERATOR))
11899             canceldtmf = "0";
11900          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11901          if (strchr(canceldtmf, cmd)) {
11902          /* need this flag here to distinguish between pressing '0' during message recording or after */
11903             canceleddtmf = 1;
11904          }
11905          if (record_gain)
11906             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11907          if (cmd == -1) {
11908             /* User has hung up, no options to give */
11909             if (!outsidecaller) {
11910                /* user was recording a greeting and they hung up, so let's delete the recording. */
11911                ast_filedelete(tempfile, NULL);
11912             }     
11913             return cmd;
11914          }
11915          if (cmd == '0') {
11916             break;
11917          } else if (cmd == '*') {
11918             break;
11919 #if 0
11920          } else if (vmu->review && (*duration < 5)) {
11921             /* Message is too short */
11922             ast_verb(3, "Message too short\n");
11923             cmd = ast_play_and_wait(chan, "vm-tooshort");
11924             cmd = ast_filedelete(tempfile, NULL);
11925             break;
11926          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11927             /* Message is all silence */
11928             ast_verb(3, "Nothing recorded\n");
11929             cmd = ast_filedelete(tempfile, NULL);
11930             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11931             if (!cmd)
11932                cmd = ast_play_and_wait(chan, "vm-speakup");
11933             break;
11934 #endif
11935          } else {
11936             /* If all is well, a message exists */
11937             msg_exists = 1;
11938             cmd = 0;
11939          }
11940          break;
11941       case '4':
11942          if (outsidecaller) {  /* only mark vm messages */
11943             /* Mark Urgent */
11944             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11945                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11946                res = ast_play_and_wait(chan, "vm-marked-urgent");
11947                strcpy(flag, "Urgent");
11948             } else if (flag) {
11949                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11950                res = ast_play_and_wait(chan, "vm-urgent-removed");
11951                strcpy(flag, "");
11952             } else {
11953                ast_play_and_wait(chan, "vm-sorry");
11954             }
11955             cmd = 0;
11956          } else {
11957             cmd = ast_play_and_wait(chan, "vm-sorry");
11958          }
11959          break;
11960       case '5':
11961       case '6':
11962       case '7':
11963       case '8':
11964       case '9':
11965       case '*':
11966       case '#':
11967          cmd = ast_play_and_wait(chan, "vm-sorry");
11968          break;
11969 #if 0 
11970 /*  XXX Commented out for the moment because of the dangers of deleting
11971     a message while recording (can put the message numbers out of sync) */
11972       case '*':
11973          /* Cancel recording, delete message, offer to take another message*/
11974          cmd = ast_play_and_wait(chan, "vm-deleted");
11975          cmd = ast_filedelete(tempfile, NULL);
11976          if (outsidecaller) {
11977             res = vm_exec(chan, NULL);
11978             return res;
11979          }
11980          else
11981             return 1;
11982 #endif
11983       case '0':
11984          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
11985             cmd = ast_play_and_wait(chan, "vm-sorry");
11986             break;
11987          }
11988          if (msg_exists || recorded) {
11989             cmd = ast_play_and_wait(chan, "vm-saveoper");
11990             if (!cmd)
11991                cmd = ast_waitfordigit(chan, 3000);
11992             if (cmd == '1') {
11993                ast_filerename(tempfile, recordfile, NULL);
11994                ast_play_and_wait(chan, "vm-msgsaved");
11995                cmd = '0';
11996             } else if (cmd == '4') {
11997                if (flag) {
11998                   ast_play_and_wait(chan, "vm-marked-urgent");
11999                   strcpy(flag, "Urgent");
12000                }
12001                ast_play_and_wait(chan, "vm-msgsaved");
12002                cmd = '0';
12003             } else {
12004                ast_play_and_wait(chan, "vm-deleted");
12005                DELETE(tempfile, -1, tempfile, vmu);
12006                cmd = '0';
12007             }
12008          }
12009          return cmd;
12010       default:
12011          /* If the caller is an ouside caller, and the review option is enabled,
12012             allow them to review the message, but let the owner of the box review
12013             their OGM's */
12014          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
12015             return cmd;
12016          if (msg_exists) {
12017             cmd = ast_play_and_wait(chan, "vm-review");
12018             if (!cmd && outsidecaller) {
12019                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
12020                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
12021                } else if (flag) {
12022                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
12023                }
12024             }
12025          } else {
12026             cmd = ast_play_and_wait(chan, "vm-torerecord");
12027             if (!cmd)
12028                cmd = ast_waitfordigit(chan, 600);
12029          }
12030          
12031          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
12032             cmd = ast_play_and_wait(chan, "vm-reachoper");
12033             if (!cmd)
12034                cmd = ast_waitfordigit(chan, 600);
12035          }
12036 #if 0
12037          if (!cmd)
12038             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
12039 #endif
12040          if (!cmd)
12041             cmd = ast_waitfordigit(chan, 6000);
12042          if (!cmd) {
12043             attempts++;
12044          }
12045          if (attempts > max_attempts) {
12046             cmd = 't';
12047          }
12048       }
12049    }
12050    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
12051       /* Hang up or timeout, so delete the recording. */
12052       ast_filedelete(tempfile, NULL);
12053    }
12054 
12055    if (cmd != 't' && outsidecaller)
12056       ast_play_and_wait(chan, "vm-goodbye");
12057 
12058    return cmd;
12059 }
12060 
12061 /* This is a workaround so that menuselect displays a proper description
12062  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
12063  */
12064 
12065 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
12066       .load = load_module,
12067       .unload = unload_module,
12068       .reload = reload,
12069       );