Mon Sep 20 2010 00:20:07

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: 260927 $")
00089 
00090 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00091 #include <sys/time.h>
00092 #include <sys/stat.h>
00093 #include <sys/mman.h>
00094 #include <time.h>
00095 #include <dirent.h>
00096 
00097 #include "asterisk/logger.h"
00098 #include "asterisk/lock.h"
00099 #include "asterisk/file.h"
00100 #include "asterisk/channel.h"
00101 #include "asterisk/pbx.h"
00102 #include "asterisk/config.h"
00103 #include "asterisk/say.h"
00104 #include "asterisk/module.h"
00105 #include "asterisk/adsi.h"
00106 #include "asterisk/app.h"
00107 #include "asterisk/manager.h"
00108 #include "asterisk/dsp.h"
00109 #include "asterisk/localtime.h"
00110 #include "asterisk/cli.h"
00111 #include "asterisk/utils.h"
00112 #include "asterisk/stringfields.h"
00113 #include "asterisk/smdi.h"
00114 #include "asterisk/astobj2.h"
00115 #include "asterisk/event.h"
00116 #include "asterisk/taskprocessor.h"
00117 
00118 #ifdef ODBC_STORAGE
00119 #include "asterisk/res_odbc.h"
00120 #endif
00121 
00122 #ifdef IMAP_STORAGE
00123 #include "asterisk/threadstorage.h"
00124 #endif
00125 
00126 /*** DOCUMENTATION
00127    <application name="VoiceMail" language="en_US">
00128       <synopsis>
00129          Leave a Voicemail message.
00130       </synopsis>
00131       <syntax>
00132          <parameter name="mailboxs" argsep="&amp;" required="true">
00133             <argument name="mailbox1" argsep="@" required="true">
00134                <argument name="mailbox" required="true" />
00135                <argument name="context" />
00136             </argument>
00137             <argument name="mailbox2" argsep="@" multiple="true">
00138                <argument name="mailbox" required="true" />
00139                <argument name="context" />
00140             </argument>
00141          </parameter>
00142          <parameter name="options">
00143             <optionlist>
00144                <option name="b">
00145                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00146                </option>
00147                <option name="d">
00148                   <argument name="c" />
00149                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00150                   if played during the greeting. Context defaults to the current context.</para>
00151                </option>
00152                <option name="g">
00153                   <argument name="#" required="true" />
00154                   <para>Use the specified amount of gain when recording the voicemail
00155                   message. The units are whole-number decibels (dB). Only works on supported
00156                   technologies, which is DAHDI only.</para>
00157                </option>
00158                <option name="s">
00159                   <para>Skip the playback of instructions for leaving a message to the
00160                   calling party.</para>
00161                </option>
00162                <option name="u">
00163                   <para>Play the <literal>unavailable</literal> greeting.</para>
00164                </option>
00165                <option name="U">
00166                   <para>Mark message as <literal>URGENT</literal>.</para>
00167                </option>
00168                <option name="P">
00169                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00170                </option>
00171             </optionlist>
00172          </parameter>
00173       </syntax>
00174       <description>
00175          <para>This application allows the calling party to leave a message for the specified
00176          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00177          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00178          exist.</para>
00179          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00180          <enumlist>
00181             <enum name="0">
00182                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00183             </enum>
00184             <enum name="*">
00185                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00186             </enum>
00187          </enumlist>
00188          <para>This application will set the following channel variable upon completion:</para>
00189          <variablelist>
00190             <variable name="VMSTATUS">
00191                <para>This indicates the status of the execution of the VoiceMail application.</para>
00192                <value name="SUCCESS" />
00193                <value name="USEREXIT" />
00194                <value name="FAILED" />
00195             </variable>
00196          </variablelist>
00197       </description>
00198    </application>
00199    <application name="VoiceMailMain" language="en_US">
00200       <synopsis>
00201          Check Voicemail messages.
00202       </synopsis>
00203       <syntax>
00204          <parameter name="mailbox" required="true" argsep="@">
00205             <argument name="mailbox" />
00206             <argument name="context" />
00207          </parameter>
00208          <parameter name="options">
00209             <optionlist>
00210                <option name="p">
00211                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00212                   the mailbox that is entered by the caller.</para>
00213                </option>
00214                <option name="g">
00215                   <argument name="#" required="true" />
00216                   <para>Use the specified amount of gain when recording a voicemail message.
00217                   The units are whole-number decibels (dB).</para>
00218                </option>
00219                <option name="s">
00220                   <para>Skip checking the passcode for the mailbox.</para>
00221                </option>
00222                <option name="a">
00223                   <argument name="folder" required="true" />
00224                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00225                   Defaults to <literal>0</literal> (INBOX).</para>
00226                   <enumlist>
00227                      <enum name="0"><para>INBOX</para></enum>
00228                      <enum name="1"><para>Old</para></enum>
00229                      <enum name="2"><para>Work</para></enum>
00230                      <enum name="3"><para>Family</para></enum>
00231                      <enum name="4"><para>Friends</para></enum>
00232                      <enum name="5"><para>Cust1</para></enum>
00233                      <enum name="6"><para>Cust2</para></enum>
00234                      <enum name="7"><para>Cust3</para></enum>
00235                      <enum name="8"><para>Cust4</para></enum>
00236                      <enum name="9"><para>Cust5</para></enum>
00237                   </enumlist>
00238                </option>
00239             </optionlist>
00240          </parameter>
00241       </syntax>
00242       <description>
00243          <para>This application allows the calling party to check voicemail messages. A specific
00244          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00245          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00246          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00247          <literal>default</literal> context will be used.</para>
00248       </description>
00249    </application>
00250    <application name="MailboxExists" language="en_US">
00251       <synopsis>
00252          Check to see if Voicemail mailbox exists.
00253       </synopsis>
00254       <syntax>
00255          <parameter name="mailbox" required="true" argsep="@">
00256             <argument name="mailbox" required="true" />
00257             <argument name="context" />
00258          </parameter>
00259          <parameter name="options">
00260             <para>None options.</para>
00261          </parameter>
00262       </syntax>
00263       <description>
00264          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00265          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00266          will be used.</para>
00267          <para>This application will set the following channel variable upon completion:</para>
00268          <variablelist>
00269             <variable name="VMBOXEXISTSSTATUS">
00270                <para>This will contain the status of the execution of the MailboxExists application.
00271                Possible values include:</para>
00272                <value name="SUCCESS" />
00273                <value name="FAILED" />
00274             </variable>
00275          </variablelist>
00276       </description>
00277    </application>
00278    <application name="VMAuthenticate" language="en_US">
00279       <synopsis>
00280          Authenticate with Voicemail passwords.
00281       </synopsis>
00282       <syntax>
00283          <parameter name="mailbox" required="true" argsep="@">
00284             <argument name="mailbox" />
00285             <argument name="context" />
00286          </parameter>
00287          <parameter name="options">
00288             <optionlist>
00289                <option name="s">
00290                   <para>Skip playing the initial prompts.</para>
00291                </option>
00292             </optionlist>
00293          </parameter>
00294       </syntax>
00295       <description>
00296          <para>This application behaves the same way as the Authenticate application, but the passwords
00297          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00298          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00299          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00300          mailbox.</para>
00301       </description>
00302    </application>
00303    <function name="MAILBOX_EXISTS" language="en_US">
00304       <synopsis>
00305          Tell if a mailbox is configured.
00306       </synopsis>
00307       <syntax argsep="@">
00308          <parameter name="mailbox" required="true" />
00309          <parameter name="context" />
00310       </syntax>
00311       <description>
00312          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00313          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00314          context.</para>
00315       </description>
00316    </function>
00317  ***/
00318 
00319 #ifdef IMAP_STORAGE
00320 static char imapserver[48];
00321 static char imapport[8];
00322 static char imapflags[128];
00323 static char imapfolder[64];
00324 static char imapparentfolder[64] = "\0";
00325 static char greetingfolder[64];
00326 static char authuser[32];
00327 static char authpassword[42];
00328 static int imapversion = 1;
00329 
00330 static int expungeonhangup = 1;
00331 static int imapgreetings = 0;
00332 static char delimiter = '\0';
00333 
00334 struct vm_state;
00335 struct ast_vm_user;
00336 
00337 AST_THREADSTORAGE(ts_vmstate);
00338 
00339 /* Forward declarations for IMAP */
00340 static int init_mailstream(struct vm_state *vms, int box);
00341 static void write_file(char *filename, char *buffer, unsigned long len);
00342 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00343 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00344 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00345 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00346 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00347 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00348 static void vmstate_insert(struct vm_state *vms);
00349 static void vmstate_delete(struct vm_state *vms);
00350 static void set_update(MAILSTREAM * stream);
00351 static void init_vm_state(struct vm_state *vms);
00352 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00353 static void get_mailbox_delimiter(MAILSTREAM *stream);
00354 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00355 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00356 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00357 static void update_messages_by_imapuser(const char *user, unsigned long number);
00358 static int vm_delete(char *file);
00359 
00360 static int imap_remove_file (char *dir, int msgnum);
00361 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00362 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00363 static void check_quota(struct vm_state *vms, char *mailbox);
00364 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00365 struct vmstate {
00366    struct vm_state *vms;
00367    AST_LIST_ENTRY(vmstate) list;
00368 };
00369 
00370 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00371 
00372 #endif
00373 
00374 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00375 
00376 #define COMMAND_TIMEOUT 5000
00377 /* Don't modify these here; set your umask at runtime instead */
00378 #define  VOICEMAIL_DIR_MODE   0777
00379 #define  VOICEMAIL_FILE_MODE  0666
00380 #define  CHUNKSIZE   65536
00381 
00382 #define VOICEMAIL_CONFIG "voicemail.conf"
00383 #define ASTERISK_USERNAME "asterisk"
00384 
00385 /* Define fast-forward, pause, restart, and reverse keys
00386    while listening to a voicemail message - these are
00387    strings, not characters */
00388 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00389 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00390 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00391 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00392 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00393 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00394 
00395 /* Default mail command to mail voicemail. Change it with the
00396     mailcmd= command in voicemail.conf */
00397 #define SENDMAIL "/usr/sbin/sendmail -t"
00398 
00399 #define INTRO "vm-intro"
00400 
00401 #define MAXMSG 100
00402 #define MAXMSGLIMIT 9999
00403 
00404 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00405 
00406 #define BASELINELEN 72
00407 #define BASEMAXINLINE 256
00408 #ifdef IMAP_STORAGE
00409 #define ENDL "\r\n"
00410 #else
00411 #define ENDL "\n"
00412 #endif
00413 
00414 #define MAX_DATETIME_FORMAT   512
00415 #define MAX_NUM_CID_CONTEXTS 10
00416 
00417 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00418 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00419 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00420 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00421 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00422 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00423 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00424 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00425 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00426 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00427 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00428 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00429 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00430 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00431 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00432 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00433 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00434 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00435 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00436 #define ERROR_LOCK_PATH  -100
00437 #define OPERATOR_EXIT     300
00438 
00439 
00440 enum {
00441    NEW_FOLDER,
00442    OLD_FOLDER,
00443    WORK_FOLDER,
00444    FAMILY_FOLDER,
00445    FRIENDS_FOLDER,
00446    GREETINGS_FOLDER
00447 } vm_box;
00448 
00449 enum {
00450    OPT_SILENT =           (1 << 0),
00451    OPT_BUSY_GREETING =    (1 << 1),
00452    OPT_UNAVAIL_GREETING = (1 << 2),
00453    OPT_RECORDGAIN =       (1 << 3),
00454    OPT_PREPEND_MAILBOX =  (1 << 4),
00455    OPT_AUTOPLAY =         (1 << 6),
00456    OPT_DTMFEXIT =         (1 << 7),
00457    OPT_MESSAGE_Urgent =   (1 << 8),
00458    OPT_MESSAGE_PRIORITY = (1 << 9)
00459 } vm_option_flags;
00460 
00461 enum {
00462    OPT_ARG_RECORDGAIN = 0,
00463    OPT_ARG_PLAYFOLDER = 1,
00464    OPT_ARG_DTMFEXIT   = 2,
00465    /* This *must* be the last value in this enum! */
00466    OPT_ARG_ARRAY_SIZE = 3,
00467 } vm_option_args;
00468 
00469 AST_APP_OPTIONS(vm_app_options, {
00470    AST_APP_OPTION('s', OPT_SILENT),
00471    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00472    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00473    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00474    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00475    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00476    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00477    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00478    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00479 });
00480 
00481 static int load_config(int reload);
00482 
00483 /*! \page vmlang Voicemail Language Syntaxes Supported
00484 
00485    \par Syntaxes supported, not really language codes.
00486    \arg \b en    - English
00487    \arg \b de    - German
00488    \arg \b es    - Spanish
00489    \arg \b fr    - French
00490    \arg \b it    - Italian
00491    \arg \b nl    - Dutch
00492    \arg \b pt    - Portuguese
00493    \arg \b pt_BR - Portuguese (Brazil)
00494    \arg \b gr    - Greek
00495    \arg \b no    - Norwegian
00496    \arg \b se    - Swedish
00497    \arg \b tw    - Chinese (Taiwan)
00498    \arg \b ua - Ukrainian
00499 
00500 German requires the following additional soundfile:
00501 \arg \b 1F  einE (feminine)
00502 
00503 Spanish requires the following additional soundfile:
00504 \arg \b 1M      un (masculine)
00505 
00506 Dutch, Portuguese & Spanish require the following additional soundfiles:
00507 \arg \b vm-INBOXs singular of 'new'
00508 \arg \b vm-Olds      singular of 'old/heard/read'
00509 
00510 NB these are plural:
00511 \arg \b vm-INBOX  nieuwe (nl)
00512 \arg \b vm-Old    oude (nl)
00513 
00514 Polish uses:
00515 \arg \b vm-new-a  'new', feminine singular accusative
00516 \arg \b vm-new-e  'new', feminine plural accusative
00517 \arg \b vm-new-ych   'new', feminine plural genitive
00518 \arg \b vm-old-a  'old', feminine singular accusative
00519 \arg \b vm-old-e  'old', feminine plural accusative
00520 \arg \b vm-old-ych   'old', feminine plural genitive
00521 \arg \b digits/1-a   'one', not always same as 'digits/1'
00522 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00523 
00524 Swedish uses:
00525 \arg \b vm-nytt      singular of 'new'
00526 \arg \b vm-nya    plural of 'new'
00527 \arg \b vm-gammalt   singular of 'old'
00528 \arg \b vm-gamla  plural of 'old'
00529 \arg \b digits/ett   'one', not always same as 'digits/1'
00530 
00531 Norwegian uses:
00532 \arg \b vm-ny     singular of 'new'
00533 \arg \b vm-nye    plural of 'new'
00534 \arg \b vm-gammel singular of 'old'
00535 \arg \b vm-gamle  plural of 'old'
00536 
00537 Dutch also uses:
00538 \arg \b nl-om     'at'?
00539 
00540 Spanish also uses:
00541 \arg \b vm-youhaveno
00542 
00543 Italian requires the following additional soundfile:
00544 
00545 For vm_intro_it:
00546 \arg \b vm-nuovo  new
00547 \arg \b vm-nuovi  new plural
00548 \arg \b vm-vecchio   old
00549 \arg \b vm-vecchi old plural
00550 
00551 Chinese (Taiwan) requires the following additional soundfile:
00552 \arg \b vm-tong      A class-word for call (tong1)
00553 \arg \b vm-ri     A class-word for day (ri4)
00554 \arg \b vm-you    You (ni3)
00555 \arg \b vm-haveno   Have no (mei2 you3)
00556 \arg \b vm-have     Have (you3)
00557 \arg \b vm-listen   To listen (yao4 ting1)
00558 
00559 
00560 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00561 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00562 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00563 
00564 */
00565 
00566 struct baseio {
00567    int iocp;
00568    int iolen;
00569    int linelength;
00570    int ateof;
00571    unsigned char iobuf[BASEMAXINLINE];
00572 };
00573 
00574 /*! Structure for linked list of users 
00575  * Use ast_vm_user_destroy() to free one of these structures. */
00576 struct ast_vm_user {
00577    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00578    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00579    char password[80];               /*!< Secret pin code, numbers only */
00580    char fullname[80];               /*!< Full name, for directory app */
00581    char email[80];                  /*!< E-mail address */
00582    char *emailsubject;              /*!< E-mail subject */
00583    char *emailbody;                 /*!< E-mail body */
00584    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00585    char serveremail[80];            /*!< From: Mail address */
00586    char mailcmd[160];               /*!< Configurable mail command */
00587    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00588    char zonetag[80];                /*!< Time zone */
00589    char callback[80];
00590    char dialout[80];
00591    char uniqueid[80];               /*!< Unique integer identifier */
00592    char exit[80];
00593    char attachfmt[20];              /*!< Attachment format */
00594    unsigned int flags;              /*!< VM_ flags */ 
00595    int saydurationm;
00596    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00597    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00598    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00599 #ifdef IMAP_STORAGE
00600    char imapuser[80];               /*!< IMAP server login */
00601    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00602    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00603    int imapversion;                 /*!< If configuration changes, use the new values */
00604 #endif
00605    double volgain;                  /*!< Volume gain for voicemails sent via email */
00606    AST_LIST_ENTRY(ast_vm_user) list;
00607 };
00608 
00609 /*! Voicemail time zones */
00610 struct vm_zone {
00611    AST_LIST_ENTRY(vm_zone) list;
00612    char name[80];
00613    char timezone[80];
00614    char msg_format[512];
00615 };
00616 
00617 #define VMSTATE_MAX_MSG_ARRAY 256
00618 
00619 /*! Voicemail mailbox state */
00620 struct vm_state {
00621    char curbox[80];
00622    char username[80];
00623    char context[80];
00624    char curdir[PATH_MAX];
00625    char vmbox[PATH_MAX];
00626    char fn[PATH_MAX];
00627    char intro[PATH_MAX];
00628    int *deleted;
00629    int *heard;
00630    int curmsg;
00631    int lastmsg;
00632    int newmessages;
00633    int oldmessages;
00634    int urgentmessages;
00635    int starting;
00636    int repeats;
00637 #ifdef IMAP_STORAGE
00638    ast_mutex_t lock;
00639    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00640    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00641    MAILSTREAM *mailstream;
00642    int vmArrayIndex;
00643    char imapuser[80];                   /*!< IMAP server login */
00644    int imapversion;
00645    int interactive;
00646    char introfn[PATH_MAX];              /*!< Name of prepended file */
00647    unsigned int quota_limit;
00648    unsigned int quota_usage;
00649    struct vm_state *persist_vms;
00650 #endif
00651 };
00652 
00653 #ifdef ODBC_STORAGE
00654 static char odbc_database[80];
00655 static char odbc_table[80];
00656 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00657 #define DISPOSE(a,b) remove_file(a,b)
00658 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00659 #define EXISTS(a,b,c,d) (message_exists(a,b))
00660 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00661 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00662 #define DELETE(a,b,c,d) (delete_file(a,b))
00663 #else
00664 #ifdef IMAP_STORAGE
00665 #define DISPOSE(a,b) (imap_remove_file(a,b))
00666 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
00667 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00668 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00669 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00670 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00671 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00672 #else
00673 #define RETRIEVE(a,b,c,d)
00674 #define DISPOSE(a,b)
00675 #define STORE(a,b,c,d,e,f,g,h,i,j)
00676 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00677 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00678 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00679 #define DELETE(a,b,c,d) (vm_delete(c))
00680 #endif
00681 #endif
00682 
00683 static char VM_SPOOL_DIR[PATH_MAX];
00684 
00685 static char ext_pass_cmd[128];
00686 static char ext_pass_check_cmd[128];
00687 
00688 static int my_umask;
00689 
00690 #define PWDCHANGE_INTERNAL (1 << 1)
00691 #define PWDCHANGE_EXTERNAL (1 << 2)
00692 static int pwdchange = PWDCHANGE_INTERNAL;
00693 
00694 #ifdef ODBC_STORAGE
00695 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00696 #else
00697 # ifdef IMAP_STORAGE
00698 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00699 # else
00700 # define tdesc "Comedian Mail (Voicemail System)"
00701 # endif
00702 #endif
00703 
00704 static char userscontext[AST_MAX_EXTENSION] = "default";
00705 
00706 static char *addesc = "Comedian Mail";
00707 
00708 /* Leave a message */
00709 static char *app = "VoiceMail";
00710 
00711 /* Check mail, control, etc */
00712 static char *app2 = "VoiceMailMain";
00713 
00714 static char *app3 = "MailboxExists";
00715 static char *app4 = "VMAuthenticate";
00716 
00717 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00718 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00719 static char zonetag[80];
00720 static int maxsilence;
00721 static int maxmsg;
00722 static int maxdeletedmsg;
00723 static int silencethreshold = 128;
00724 static char serveremail[80];
00725 static char mailcmd[160];  /* Configurable mail cmd */
00726 static char externnotify[160]; 
00727 static struct ast_smdi_interface *smdi_iface = NULL;
00728 static char vmfmts[80];
00729 static double volgain;
00730 static int vmminsecs;
00731 static int vmmaxsecs;
00732 static int maxgreet;
00733 static int skipms;
00734 static int maxlogins;
00735 static int minpassword;
00736 
00737 /*! Poll mailboxes for changes since there is something external to
00738  *  app_voicemail that may change them. */
00739 static unsigned int poll_mailboxes;
00740 
00741 /*! Polling frequency */
00742 static unsigned int poll_freq;
00743 /*! By default, poll every 30 seconds */
00744 #define DEFAULT_POLL_FREQ 30
00745 
00746 AST_MUTEX_DEFINE_STATIC(poll_lock);
00747 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00748 static pthread_t poll_thread = AST_PTHREADT_NULL;
00749 static unsigned char poll_thread_run;
00750 
00751 /*! Subscription to ... MWI event subscriptions */
00752 static struct ast_event_sub *mwi_sub_sub;
00753 /*! Subscription to ... MWI event un-subscriptions */
00754 static struct ast_event_sub *mwi_unsub_sub;
00755 
00756 /*!
00757  * \brief An MWI subscription
00758  *
00759  * This is so we can keep track of which mailboxes are subscribed to.
00760  * This way, we know which mailboxes to poll when the pollmailboxes
00761  * option is being used.
00762  */
00763 struct mwi_sub {
00764    AST_RWLIST_ENTRY(mwi_sub) entry;
00765    int old_urgent;
00766    int old_new;
00767    int old_old;
00768    uint32_t uniqueid;
00769    char mailbox[1];
00770 };
00771 
00772 struct mwi_sub_task {
00773    const char *mailbox;
00774    const char *context;
00775    uint32_t uniqueid;
00776 };
00777 
00778 static struct ast_taskprocessor *mwi_subscription_tps;
00779 
00780 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00781 
00782 /* custom audio control prompts for voicemail playback */
00783 static char listen_control_forward_key[12];
00784 static char listen_control_reverse_key[12];
00785 static char listen_control_pause_key[12];
00786 static char listen_control_restart_key[12];
00787 static char listen_control_stop_key[12];
00788 
00789 /* custom password sounds */
00790 static char vm_password[80] = "vm-password";
00791 static char vm_newpassword[80] = "vm-newpassword";
00792 static char vm_passchanged[80] = "vm-passchanged";
00793 static char vm_reenterpassword[80] = "vm-reenterpassword";
00794 static char vm_mismatch[80] = "vm-mismatch";
00795 static char vm_invalid_password[80] = "vm-invalid-password";
00796 static char vm_pls_try_again[80] = "vm-pls-try-again";
00797 
00798 static struct ast_flags globalflags = {0};
00799 
00800 static int saydurationminfo;
00801 
00802 static char dialcontext[AST_MAX_CONTEXT] = "";
00803 static char callcontext[AST_MAX_CONTEXT] = "";
00804 static char exitcontext[AST_MAX_CONTEXT] = "";
00805 
00806 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00807 
00808 
00809 static char *emailbody = NULL;
00810 static char *emailsubject = NULL;
00811 static char *pagerbody = NULL;
00812 static char *pagersubject = NULL;
00813 static char fromstring[100];
00814 static char pagerfromstring[100];
00815 static char charset[32] = "ISO-8859-1";
00816 
00817 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00818 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00819 static int adsiver = 1;
00820 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00821 
00822 /* Forward declarations - generic */
00823 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00824 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
00825 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00826 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00827          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00828          signed char record_gain, struct vm_state *vms, char *flag);
00829 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00830 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00831 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
00832 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
00833 static void apply_options(struct ast_vm_user *vmu, const char *options);
00834 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
00835 static int is_valid_dtmf(const char *key);
00836 
00837 struct ao2_container *inprocess_container;
00838 
00839 struct inprocess {
00840    int count;
00841    char *context;
00842    char mailbox[0];
00843 };
00844 
00845 static int inprocess_hash_fn(const void *obj, const int flags)
00846 {
00847    const struct inprocess *i = obj;
00848    return atoi(i->mailbox);
00849 }
00850 
00851 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00852 {
00853    struct inprocess *i = obj, *j = arg;
00854    if (!strcmp(i->mailbox, j->mailbox)) {
00855       return 0;
00856    }
00857    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00858 }
00859 
00860 static int inprocess_count(const char *context, const char *mailbox, int delta)
00861 {
00862    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00863    arg->context = arg->mailbox + strlen(mailbox) + 1;
00864    strcpy(arg->mailbox, mailbox); /* SAFE */
00865    strcpy(arg->context, context); /* SAFE */
00866    ao2_lock(inprocess_container);
00867    if ((i = ao2_find(inprocess_container, arg, 0))) {
00868       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00869       ao2_unlock(inprocess_container);
00870       ao2_ref(i, -1);
00871       return ret;
00872    }
00873    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00874       ao2_unlock(inprocess_container);
00875       return 0;
00876    }
00877    i->context = i->mailbox + strlen(mailbox) + 1;
00878    strcpy(i->mailbox, mailbox); /* SAFE */
00879    strcpy(i->context, context); /* SAFE */
00880    i->count = delta;
00881    ao2_link(inprocess_container, i);
00882    ao2_unlock(inprocess_container);
00883    ao2_ref(i, -1);
00884    return 0;
00885 }
00886 
00887 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00888 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00889 #endif
00890 
00891 /*!
00892  * \brief Strips control and non 7-bit clean characters from input string.
00893  *
00894  * \note To map control and none 7-bit characters to a 7-bit clean characters
00895  *  please use ast_str_encode_mine().
00896  */
00897 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00898 {
00899    char *bufptr = buf;
00900    for (; *input; input++) {
00901       if (*input < 32) {
00902          continue;
00903       }
00904       *bufptr++ = *input;
00905       if (bufptr == buf + buflen - 1) {
00906          break;
00907       }
00908    }
00909    *bufptr = '\0';
00910    return buf;
00911 }
00912 
00913 
00914 /*!
00915  * \brief Sets default voicemail system options to a voicemail user.
00916  *
00917  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00918  * - all the globalflags
00919  * - the saydurationminfo
00920  * - the callcontext
00921  * - the dialcontext
00922  * - the exitcontext
00923  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00924  * - volume gain.
00925  */
00926 static void populate_defaults(struct ast_vm_user *vmu)
00927 {
00928    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00929    if (saydurationminfo)
00930       vmu->saydurationm = saydurationminfo;
00931    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00932    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00933    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00934    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00935    if (vmmaxsecs)
00936       vmu->maxsecs = vmmaxsecs;
00937    if (maxmsg)
00938       vmu->maxmsg = maxmsg;
00939    if (maxdeletedmsg)
00940       vmu->maxdeletedmsg = maxdeletedmsg;
00941    vmu->volgain = volgain;
00942    vmu->emailsubject = NULL;
00943    vmu->emailbody = NULL;
00944 }
00945 
00946 /*!
00947  * \brief Sets a a specific property value.
00948  * \param vmu The voicemail user object to work with.
00949  * \param var The name of the property to be set.
00950  * \param value The value to be set to the property.
00951  * 
00952  * The property name must be one of the understood properties. See the source for details.
00953  */
00954 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00955 {
00956    int x;
00957    if (!strcasecmp(var, "attach")) {
00958       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00959    } else if (!strcasecmp(var, "attachfmt")) {
00960       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00961    } else if (!strcasecmp(var, "serveremail")) {
00962       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00963    } else if (!strcasecmp(var, "language")) {
00964       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00965    } else if (!strcasecmp(var, "tz")) {
00966       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00967 #ifdef IMAP_STORAGE
00968    } else if (!strcasecmp(var, "imapuser")) {
00969       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00970       vmu->imapversion = imapversion;
00971    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00972       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00973       vmu->imapversion = imapversion;
00974    } else if (!strcasecmp(var, "imapvmshareid")) {
00975       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00976       vmu->imapversion = imapversion;
00977 #endif
00978    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00979       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00980    } else if (!strcasecmp(var, "saycid")){
00981       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00982    } else if (!strcasecmp(var,"sendvoicemail")){
00983       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00984    } else if (!strcasecmp(var, "review")){
00985       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00986    } else if (!strcasecmp(var, "tempgreetwarn")){
00987       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00988    } else if (!strcasecmp(var, "messagewrap")){
00989       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00990    } else if (!strcasecmp(var, "operator")) {
00991       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00992    } else if (!strcasecmp(var, "envelope")){
00993       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00994    } else if (!strcasecmp(var, "moveheard")){
00995       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00996    } else if (!strcasecmp(var, "sayduration")){
00997       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00998    } else if (!strcasecmp(var, "saydurationm")){
00999       if (sscanf(value, "%30d", &x) == 1) {
01000          vmu->saydurationm = x;
01001       } else {
01002          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01003       }
01004    } else if (!strcasecmp(var, "forcename")){
01005       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01006    } else if (!strcasecmp(var, "forcegreetings")){
01007       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01008    } else if (!strcasecmp(var, "callback")) {
01009       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01010    } else if (!strcasecmp(var, "dialout")) {
01011       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01012    } else if (!strcasecmp(var, "exitcontext")) {
01013       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01014    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01015       vmu->maxsecs = atoi(value);
01016       if (vmu->maxsecs <= 0) {
01017          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01018          vmu->maxsecs = vmmaxsecs;
01019       } else {
01020          vmu->maxsecs = atoi(value);
01021       }
01022       if (!strcasecmp(var, "maxmessage"))
01023          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01024    } else if (!strcasecmp(var, "maxmsg")) {
01025       vmu->maxmsg = atoi(value);
01026       if (vmu->maxmsg <= 0) {
01027          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01028          vmu->maxmsg = MAXMSG;
01029       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01030          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01031          vmu->maxmsg = MAXMSGLIMIT;
01032       }
01033    } else if (!strcasecmp(var, "backupdeleted")) {
01034       if (sscanf(value, "%30d", &x) == 1)
01035          vmu->maxdeletedmsg = x;
01036       else if (ast_true(value))
01037          vmu->maxdeletedmsg = MAXMSG;
01038       else
01039          vmu->maxdeletedmsg = 0;
01040 
01041       if (vmu->maxdeletedmsg < 0) {
01042          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01043          vmu->maxdeletedmsg = MAXMSG;
01044       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01045          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01046          vmu->maxdeletedmsg = MAXMSGLIMIT;
01047       }
01048    } else if (!strcasecmp(var, "volgain")) {
01049       sscanf(value, "%30lf", &vmu->volgain);
01050    } else if (!strcasecmp(var, "options")) {
01051       apply_options(vmu, value);
01052    }
01053 }
01054 
01055 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01056 {
01057    int fds[2], pid = 0;
01058 
01059    memset(buf, 0, len);
01060 
01061    if (pipe(fds)) {
01062       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01063    } else {
01064       /* good to go*/
01065       pid = ast_safe_fork(0);
01066 
01067       if (pid < 0) {
01068          /* ok maybe not */
01069          close(fds[0]);
01070          close(fds[1]);
01071          snprintf(buf, len, "FAILURE: Fork failed");
01072       } else if (pid) {
01073          /* parent */
01074          close(fds[1]);
01075          if (read(fds[0], buf, len) < 0) {
01076             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01077          }
01078          close(fds[0]);
01079       } else {
01080          /*  child */
01081          AST_DECLARE_APP_ARGS(arg,
01082             AST_APP_ARG(v)[20];
01083          );
01084          char *mycmd = ast_strdupa(command);
01085 
01086          close(fds[0]);
01087          dup2(fds[1], STDOUT_FILENO);
01088          close(fds[1]);
01089          ast_close_fds_above_n(STDOUT_FILENO);
01090 
01091          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01092 
01093          execv(arg.v[0], arg.v); 
01094          printf("FAILURE: %s", strerror(errno));
01095          _exit(0);
01096       }
01097    }
01098    return buf;
01099 }
01100 
01101 /*!
01102  * \brief Check that password meets minimum required length
01103  * \param vmu The voicemail user to change the password for.
01104  * \param password The password string to check
01105  *
01106  * \return zero on ok, 1 on not ok.
01107  */
01108 static int check_password(struct ast_vm_user *vmu, char *password)
01109 {
01110    /* check minimum length */
01111    if (strlen(password) < minpassword)
01112       return 1;
01113    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01114       char cmd[255], buf[255];
01115 
01116       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01117 
01118       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01119       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01120          ast_debug(5, "Result: %s\n", buf);
01121          if (!strncasecmp(buf, "VALID", 5)) {
01122             ast_debug(3, "Passed password check: '%s'\n", buf);
01123             return 0;
01124          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01125             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01126             return 0;
01127          } else {
01128             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01129             return 1;
01130          }
01131       }
01132    }
01133    return 0;
01134 }
01135 
01136 /*! 
01137  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01138  * \param vmu The voicemail user to change the password for.
01139  * \param password The new value to be set to the password for this user.
01140  * 
01141  * This only works if there is a realtime engine configured.
01142  * This is called from the (top level) vm_change_password.
01143  *
01144  * \return zero on success, -1 on error.
01145  */
01146 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01147 {
01148    int res = -1;
01149    if (!strcmp(vmu->password, password)) {
01150       /* No change (but an update would return 0 rows updated, so we opt out here) */
01151       return 0;
01152    }
01153 
01154    if (strlen(password) > 10) {
01155       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01156    }
01157    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01158       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01159       res = 0;
01160    }
01161    return res;
01162 }
01163 
01164 /*!
01165  * \brief Destructively Parse options and apply.
01166  */
01167 static void apply_options(struct ast_vm_user *vmu, const char *options)
01168 {  
01169    char *stringp;
01170    char *s;
01171    char *var, *value;
01172    stringp = ast_strdupa(options);
01173    while ((s = strsep(&stringp, "|"))) {
01174       value = s;
01175       if ((var = strsep(&value, "=")) && value) {
01176          apply_option(vmu, var, value);
01177       }
01178    }  
01179 }
01180 
01181 /*!
01182  * \brief Loads the options specific to a voicemail user.
01183  * 
01184  * This is called when a vm_user structure is being set up, such as from load_options.
01185  */
01186 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01187 {
01188    for (; var; var = var->next) {
01189       if (!strcasecmp(var->name, "vmsecret")) {
01190          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01191       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01192          if (ast_strlen_zero(retval->password))
01193             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01194       } else if (!strcasecmp(var->name, "uniqueid")) {
01195          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01196       } else if (!strcasecmp(var->name, "pager")) {
01197          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01198       } else if (!strcasecmp(var->name, "email")) {
01199          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01200       } else if (!strcasecmp(var->name, "fullname")) {
01201          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01202       } else if (!strcasecmp(var->name, "context")) {
01203          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01204       } else if (!strcasecmp(var->name, "emailsubject")) {
01205          retval->emailsubject = ast_strdup(var->value);
01206       } else if (!strcasecmp(var->name, "emailbody")) {
01207          retval->emailbody = ast_strdup(var->value);
01208 #ifdef IMAP_STORAGE
01209       } else if (!strcasecmp(var->name, "imapuser")) {
01210          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01211          retval->imapversion = imapversion;
01212       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01213          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01214          retval->imapversion = imapversion;
01215       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01216          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01217          retval->imapversion = imapversion;
01218 #endif
01219       } else
01220          apply_option(retval, var->name, var->value);
01221    }
01222 }
01223 
01224 /*!
01225  * \brief Determines if a DTMF key entered is valid.
01226  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
01227  *
01228  * Tests the character entered against the set of valid DTMF characters. 
01229  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01230  */
01231 static int is_valid_dtmf(const char *key)
01232 {
01233    int i;
01234    char *local_key = ast_strdupa(key);
01235 
01236    for (i = 0; i < strlen(key); ++i) {
01237       if (!strchr(VALID_DTMF, *local_key)) {
01238          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01239          return 0;
01240       }
01241       local_key++;
01242    }
01243    return 1;
01244 }
01245 
01246 /*!
01247  * \brief Finds a voicemail user from the realtime engine.
01248  * \param ivm
01249  * \param context
01250  * \param mailbox
01251  *
01252  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
01253  *
01254  * \return The ast_vm_user structure for the user that was found.
01255  */
01256 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01257 {
01258    struct ast_variable *var;
01259    struct ast_vm_user *retval;
01260 
01261    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01262       if (!ivm)
01263          ast_set_flag(retval, VM_ALLOCED);   
01264       else
01265          memset(retval, 0, sizeof(*retval));
01266       if (mailbox) 
01267          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01268       populate_defaults(retval);
01269       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01270          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01271       else
01272          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01273       if (var) {
01274          apply_options_full(retval, var);
01275          ast_variables_destroy(var);
01276       } else { 
01277          if (!ivm) 
01278             ast_free(retval);
01279          retval = NULL;
01280       }  
01281    } 
01282    return retval;
01283 }
01284 
01285 /*!
01286  * \brief Finds a voicemail user from the users file or the realtime engine.
01287  * \param ivm
01288  * \param context
01289  * \param mailbox
01290  * 
01291  * \return The ast_vm_user structure for the user that was found.
01292  */
01293 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01294 {
01295    /* This function could be made to generate one from a database, too */
01296    struct ast_vm_user *vmu=NULL, *cur;
01297    AST_LIST_LOCK(&users);
01298 
01299    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01300       context = "default";
01301 
01302    AST_LIST_TRAVERSE(&users, cur, list) {
01303 #ifdef IMAP_STORAGE
01304       if (cur->imapversion != imapversion) {
01305          continue;
01306       }
01307 #endif
01308       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01309          break;
01310       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01311          break;
01312    }
01313    if (cur) {
01314       /* Make a copy, so that on a reload, we have no race */
01315       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01316          memcpy(vmu, cur, sizeof(*vmu));
01317          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01318          AST_LIST_NEXT(vmu, list) = NULL;
01319       }
01320    } else
01321       vmu = find_user_realtime(ivm, context, mailbox);
01322    AST_LIST_UNLOCK(&users);
01323    return vmu;
01324 }
01325 
01326 /*!
01327  * \brief Resets a user password to a specified password.
01328  * \param context
01329  * \param mailbox
01330  * \param newpass
01331  *
01332  * This does the actual change password work, called by the vm_change_password() function.
01333  *
01334  * \return zero on success, -1 on error.
01335  */
01336 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01337 {
01338    /* This function could be made to generate one from a database, too */
01339    struct ast_vm_user *cur;
01340    int res = -1;
01341    AST_LIST_LOCK(&users);
01342    AST_LIST_TRAVERSE(&users, cur, list) {
01343       if ((!context || !strcasecmp(context, cur->context)) &&
01344          (!strcasecmp(mailbox, cur->mailbox)))
01345             break;
01346    }
01347    if (cur) {
01348       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01349       res = 0;
01350    }
01351    AST_LIST_UNLOCK(&users);
01352    return res;
01353 }
01354 
01355 /*! 
01356  * \brief The handler for the change password option.
01357  * \param vmu The voicemail user to work with.
01358  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01359  * This is called when a new user logs in for the first time and the option to force them to change their password is set.
01360  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01361  */
01362 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01363 {
01364    struct ast_config   *cfg=NULL;
01365    struct ast_variable *var=NULL;
01366    struct ast_category *cat=NULL;
01367    char *category=NULL, *value=NULL, *new=NULL;
01368    const char *tmp=NULL;
01369    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01370    if (!change_password_realtime(vmu, newpassword))
01371       return;
01372 
01373    /* check voicemail.conf */
01374    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01375       while ((category = ast_category_browse(cfg, category))) {
01376          if (!strcasecmp(category, vmu->context)) {
01377             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01378                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01379                break;
01380             }
01381             value = strstr(tmp,",");
01382             if (!value) {
01383                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01384                break;
01385             }
01386             new = alloca((strlen(value)+strlen(newpassword)+1));
01387             sprintf(new,"%s%s", newpassword, value);
01388             if (!(cat = ast_category_get(cfg, category))) {
01389                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01390                break;
01391             }
01392             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01393          }
01394       }
01395       /* save the results */
01396       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01397       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01398       ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01399    }
01400    category = NULL;
01401    var = NULL;
01402    /* check users.conf and update the password stored for the mailbox*/
01403    /* if no vmsecret entry exists create one. */
01404    if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01405       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01406       while ((category = ast_category_browse(cfg, category))) {
01407          ast_debug(4, "users.conf: %s\n", category);
01408          if (!strcasecmp(category, vmu->mailbox)) {
01409             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01410                ast_debug(3, "looks like we need to make vmsecret!\n");
01411                var = ast_variable_new("vmsecret", newpassword, "");
01412             } 
01413             new = alloca(strlen(newpassword)+1);
01414             sprintf(new, "%s", newpassword);
01415             if (!(cat = ast_category_get(cfg, category))) {
01416                ast_debug(4, "failed to get category!\n");
01417                break;
01418             }
01419             if (!var)      
01420                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01421             else
01422                ast_variable_append(cat, var);
01423          }
01424       }
01425       /* save the results and clean things up */
01426       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01427       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01428       ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01429    }
01430 }
01431 
01432 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01433 {
01434    char buf[255];
01435    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01436    if (!ast_safe_system(buf)) {
01437       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01438       /* Reset the password in memory, too */
01439       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01440    }
01441 }
01442 
01443 /*! 
01444  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01445  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01446  * \param len The length of the path string that was written out.
01447  * 
01448  * The path is constructed as 
01449  *    VM_SPOOL_DIRcontext/ext/folder
01450  *
01451  * \return zero on success, -1 on error.
01452  */
01453 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01454 {
01455    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01456 }
01457 
01458 /*! 
01459  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01460  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01461  * \param len The length of the path string that was written out.
01462  * 
01463  * The path is constructed as 
01464  *    VM_SPOOL_DIRcontext/ext/folder
01465  *
01466  * \return zero on success, -1 on error.
01467  */
01468 static int make_file(char *dest, const int len, const char *dir, const int num)
01469 {
01470    return snprintf(dest, len, "%s/msg%04d", dir, num);
01471 }
01472 
01473 /* same as mkstemp, but return a FILE * */
01474 static FILE *vm_mkftemp(char *template)
01475 {
01476    FILE *p = NULL;
01477    int pfd = mkstemp(template);
01478    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01479    if (pfd > -1) {
01480       p = fdopen(pfd, "w+");
01481       if (!p) {
01482          close(pfd);
01483          pfd = -1;
01484       }
01485    }
01486    return p;
01487 }
01488 
01489 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01490  * \param dest    String. base directory.
01491  * \param len     Length of dest.
01492  * \param context String. Ignored if is null or empty string.
01493  * \param ext     String. Ignored if is null or empty string.
01494  * \param folder  String. Ignored if is null or empty string. 
01495  * \return -1 on failure, 0 on success.
01496  */
01497 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01498 {
01499    mode_t   mode = VOICEMAIL_DIR_MODE;
01500    int res;
01501 
01502    make_dir(dest, len, context, ext, folder);
01503    if ((res = ast_mkdir(dest, mode))) {
01504       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01505       return -1;
01506    }
01507    return 0;
01508 }
01509 
01510 static const char *mbox(int id)
01511 {
01512    static const char *msgs[] = {
01513 #ifdef IMAP_STORAGE
01514       imapfolder,
01515 #else
01516       "INBOX",
01517 #endif
01518       "Old",
01519       "Work",
01520       "Family",
01521       "Friends",
01522       "Cust1",
01523       "Cust2",
01524       "Cust3",
01525       "Cust4",
01526       "Cust5",
01527       "Deleted",
01528       "Urgent"
01529    };
01530    return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
01531 }
01532 
01533 static void free_user(struct ast_vm_user *vmu)
01534 {
01535    if (ast_test_flag(vmu, VM_ALLOCED)) {
01536       if (vmu->emailbody != NULL) {
01537          ast_free(vmu->emailbody);
01538          vmu->emailbody = NULL;
01539       }
01540       if (vmu->emailsubject != NULL) {
01541          ast_free(vmu->emailsubject);
01542          vmu->emailsubject = NULL;
01543       }
01544       ast_free(vmu);
01545    }
01546 }
01547 
01548 /* All IMAP-specific functions should go in this block. This
01549  * keeps them from being spread out all over the code */
01550 #ifdef IMAP_STORAGE
01551 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01552 {
01553    char arg[10];
01554    struct vm_state *vms;
01555    unsigned long messageNum;
01556 
01557    /* If greetings aren't stored in IMAP, just delete the file */
01558    if (msgnum < 0 && !imapgreetings) {
01559       ast_filedelete(file, NULL);
01560       return;
01561    }
01562 
01563    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01564       ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
01565       return;
01566    }
01567 
01568    /* find real message number based on msgnum */
01569    /* this may be an index into vms->msgArray based on the msgnum. */
01570    messageNum = vms->msgArray[msgnum];
01571    if (messageNum == 0) {
01572       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01573       return;
01574    }
01575    if (option_debug > 2)
01576       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01577    /* delete message */
01578    snprintf (arg, sizeof(arg), "%lu",messageNum);
01579    ast_mutex_lock(&vms->lock);
01580    mail_setflag (vms->mailstream,arg,"\\DELETED");
01581    mail_expunge(vms->mailstream);
01582    ast_mutex_unlock(&vms->lock);
01583 }
01584 
01585 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01586 {
01587    struct vm_state *vms_p;
01588    char *file, *filename;
01589    char *attachment;
01590    int ret = 0, i;
01591    BODY *body;
01592 
01593    /* This function is only used for retrieval of IMAP greetings
01594     * regular messages are not retrieved this way, nor are greetings
01595     * if they are stored locally*/
01596    if (msgnum > -1 || !imapgreetings) {
01597       return 0;
01598    } else {
01599       file = strrchr(ast_strdupa(dir), '/');
01600       if (file)
01601          *file++ = '\0';
01602       else {
01603          ast_debug (1, "Failed to procure file name from directory passed.\n");
01604          return -1;
01605       }
01606    }
01607 
01608    /* check if someone is accessing this box right now... */
01609    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01610       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01611       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01612       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01613       * that's all we need to do.
01614       */
01615       if (!(vms_p = create_vm_state_from_user(vmu))) {
01616          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01617          return -1;
01618       }
01619    }
01620    
01621    /* Greetings will never have a prepended message */
01622    *vms_p->introfn = '\0';
01623 
01624    ast_mutex_lock(&vms_p->lock);
01625    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01626    if (!vms_p->mailstream) {
01627       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01628       ast_mutex_unlock(&vms_p->lock);
01629       return -1;
01630    }
01631 
01632    /*XXX Yuck, this could probably be done a lot better */
01633    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01634       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01635       /* We have the body, now we extract the file name of the first attachment. */
01636       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01637          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01638       } else {
01639          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01640          ast_mutex_unlock(&vms_p->lock);
01641          return -1;
01642       }
01643       filename = strsep(&attachment, ".");
01644       if (!strcmp(filename, file)) {
01645          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01646          vms_p->msgArray[vms_p->curmsg] = i + 1;
01647          save_body(body, vms_p, "2", attachment, 0);
01648          ast_mutex_unlock(&vms_p->lock);
01649          return 0;
01650       }
01651    }
01652    ast_mutex_unlock(&vms_p->lock);
01653 
01654    return -1;
01655 }
01656 
01657 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01658 {
01659    BODY *body;
01660    char *header_content;
01661    char *attachedfilefmt;
01662    char buf[80];
01663    struct vm_state *vms;
01664    char text_file[PATH_MAX];
01665    FILE *text_file_ptr;
01666    int res = 0;
01667    struct ast_vm_user *vmu;
01668 
01669    if (!(vmu = find_user(NULL, context, mailbox))) {
01670       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01671       return -1;
01672    }
01673    
01674    if (msgnum < 0) {
01675       if (imapgreetings) {
01676          res = imap_retrieve_greeting(dir, msgnum, vmu);
01677          goto exit;
01678       } else {
01679          res = 0;
01680          goto exit;
01681       }
01682    }
01683 
01684    /* Before anything can happen, we need a vm_state so that we can
01685     * actually access the imap server through the vms->mailstream
01686     */
01687    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01688       /* This should not happen. If it does, then I guess we'd
01689        * need to create the vm_state, extract which mailbox to
01690        * open, and then set up the msgArray so that the correct
01691        * IMAP message could be accessed. If I have seen correctly
01692        * though, the vms should be obtainable from the vmstates list
01693        * and should have its msgArray properly set up.
01694        */
01695       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01696       res = -1;
01697       goto exit;
01698    }
01699    
01700    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01701    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01702 
01703    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01704    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01705       res = 0;
01706       goto exit;
01707    }
01708 
01709    if (option_debug > 2)
01710       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01711    if (vms->msgArray[msgnum] == 0) {
01712       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01713       res = -1;
01714       goto exit;
01715    }
01716 
01717    /* This will only work for new messages... */
01718    ast_mutex_lock(&vms->lock);
01719    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01720    ast_mutex_unlock(&vms->lock);
01721    /* empty string means no valid header */
01722    if (ast_strlen_zero(header_content)) {
01723       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01724       res = -1;
01725       goto exit;
01726    }
01727 
01728    ast_mutex_lock(&vms->lock);
01729    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01730    ast_mutex_unlock(&vms->lock);
01731 
01732    /* We have the body, now we extract the file name of the first attachment. */
01733    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01734       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01735    } else {
01736       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01737       res = -1;
01738       goto exit;
01739    }
01740    
01741    /* Find the format of the attached file */
01742 
01743    strsep(&attachedfilefmt, ".");
01744    if (!attachedfilefmt) {
01745       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01746       res = -1;
01747       goto exit;
01748    }
01749    
01750    save_body(body, vms, "2", attachedfilefmt, 0);
01751    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01752       *vms->introfn = '\0';
01753    }
01754 
01755    /* Get info from headers!! */
01756    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01757 
01758    if (!(text_file_ptr = fopen(text_file, "w"))) {
01759       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01760    }
01761 
01762    fprintf(text_file_ptr, "%s\n", "[message]");
01763 
01764    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01765    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01766    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01767    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01768    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01769    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01770    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01771    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01772    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01773    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01774    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01775    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01776    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01777    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01778    fclose(text_file_ptr);
01779 
01780 exit:
01781    free_user(vmu);
01782    return res;
01783 }
01784 
01785 static int folder_int(const char *folder)
01786 {
01787    /*assume a NULL folder means INBOX*/
01788    if (!folder)
01789       return 0;
01790 #ifdef IMAP_STORAGE
01791    if (!strcasecmp(folder, imapfolder))
01792 #else
01793    if (!strcasecmp(folder, "INBOX"))
01794 #endif
01795       return 0;
01796    else if (!strcasecmp(folder, "Old"))
01797       return 1;
01798    else if (!strcasecmp(folder, "Work"))
01799       return 2;
01800    else if (!strcasecmp(folder, "Family"))
01801       return 3;
01802    else if (!strcasecmp(folder, "Friends"))
01803       return 4;
01804    else if (!strcasecmp(folder, "Cust1"))
01805       return 5;
01806    else if (!strcasecmp(folder, "Cust2"))
01807       return 6;
01808    else if (!strcasecmp(folder, "Cust3"))
01809       return 7;
01810    else if (!strcasecmp(folder, "Cust4"))
01811       return 8;
01812    else if (!strcasecmp(folder, "Cust5"))
01813       return 9;
01814    else /*assume they meant INBOX if folder is not found otherwise*/
01815       return 0;
01816 }
01817 
01818 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01819 {
01820    SEARCHPGM *pgm;
01821    SEARCHHEADER *hdr;
01822 
01823    struct ast_vm_user *vmu, vmus;
01824    struct vm_state *vms_p;
01825    int ret = 0;
01826    int fold = folder_int(folder);
01827    int urgent = 0;
01828    
01829    /* If URGENT, then look at INBOX */
01830    if (fold == 11) {
01831       fold = NEW_FOLDER;
01832       urgent = 1;
01833    }
01834 
01835    if (ast_strlen_zero(mailbox))
01836       return 0;
01837 
01838    /* We have to get the user before we can open the stream! */
01839    vmu = find_user(&vmus, context, mailbox);
01840    if (!vmu) {
01841       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01842       return -1;
01843    } else {
01844       /* No IMAP account available */
01845       if (vmu->imapuser[0] == '\0') {
01846          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01847          return -1;
01848       }
01849    }
01850    
01851    /* No IMAP account available */
01852    if (vmu->imapuser[0] == '\0') {
01853       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01854       free_user(vmu);
01855       return -1;
01856    }
01857 
01858    /* check if someone is accessing this box right now... */
01859    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01860    if (!vms_p) {
01861       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01862    }
01863    if (vms_p) {
01864       ast_debug(3, "Returning before search - user is logged in\n");
01865       if (fold == 0) { /* INBOX */
01866          return vms_p->newmessages;
01867       }
01868       if (fold == 1) { /* Old messages */
01869          return vms_p->oldmessages;
01870       }
01871       if (fold == 11) {/*Urgent messages*/
01872          return vms_p->urgentmessages;
01873       }
01874    }
01875 
01876    /* add one if not there... */
01877    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01878    if (!vms_p) {
01879       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01880    }
01881 
01882    if (!vms_p) {
01883       vms_p = create_vm_state_from_user(vmu);
01884    }
01885    ret = init_mailstream(vms_p, fold);
01886    if (!vms_p->mailstream) {
01887       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01888       return -1;
01889    }
01890    if (ret == 0) {
01891       ast_mutex_lock(&vms_p->lock);
01892       pgm = mail_newsearchpgm ();
01893       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01894       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01895       pgm->header = hdr;
01896       if (fold != 1) {
01897          pgm->unseen = 1;
01898          pgm->seen = 0;
01899       }
01900       /* In the special case where fold is 1 (old messages) we have to do things a bit
01901        * differently. Old messages are stored in the INBOX but are marked as "seen"
01902        */
01903       else {
01904          pgm->unseen = 0;
01905          pgm->seen = 1;
01906       }
01907       /* look for urgent messages */
01908       if (urgent) {
01909          pgm->flagged = 1;
01910          pgm->unflagged = 0;
01911       }
01912       pgm->undeleted = 1;
01913       pgm->deleted = 0;
01914 
01915       vms_p->vmArrayIndex = 0;
01916       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01917       if (fold == 0 && urgent == 0)
01918          vms_p->newmessages = vms_p->vmArrayIndex;
01919       if (fold == 1)
01920          vms_p->oldmessages = vms_p->vmArrayIndex;
01921       if (fold == 0 && urgent == 1)
01922          vms_p->urgentmessages = vms_p->vmArrayIndex;
01923       /*Freeing the searchpgm also frees the searchhdr*/
01924       mail_free_searchpgm(&pgm);
01925       ast_mutex_unlock(&vms_p->lock);
01926       vms_p->updated = 0;
01927       return vms_p->vmArrayIndex;
01928    } else {
01929       ast_mutex_lock(&vms_p->lock);
01930       mail_ping(vms_p->mailstream);
01931       ast_mutex_unlock(&vms_p->lock);
01932    }
01933    return 0;
01934 }
01935 
01936 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01937 {
01938    /* Check if mailbox is full */
01939    check_quota(vms, imapfolder);
01940    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01941       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01942       ast_play_and_wait(chan, "vm-mailboxfull");
01943       return -1;
01944    }
01945    
01946    /* Check if we have exceeded maxmsg */
01947    if (msgnum >= vmu->maxmsg  - inprocess_count(vmu->mailbox, vmu->context, 0)) {
01948       ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
01949       ast_play_and_wait(chan, "vm-mailboxfull");
01950       return -1;
01951    }
01952 
01953    return 0;
01954 }
01955 
01956 /*!
01957  * \brief Gets the number of messages that exist in a mailbox folder.
01958  * \param context
01959  * \param mailbox
01960  * \param folder
01961  * 
01962  * This method is used when IMAP backend is used.
01963  * \return The number of messages in this mailbox folder (zero or more).
01964  */
01965 static int messagecount(const char *context, const char *mailbox, const char *folder)
01966 {
01967    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
01968       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
01969    } else {
01970       return __messagecount(context, mailbox, folder);
01971    }
01972 }
01973 
01974 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)
01975 {
01976    char *myserveremail = serveremail;
01977    char fn[PATH_MAX];
01978    char introfn[PATH_MAX];
01979    char mailbox[256];
01980    char *stringp;
01981    FILE *p=NULL;
01982    char tmp[80] = "/tmp/astmail-XXXXXX";
01983    long len;
01984    void *buf;
01985    int tempcopy = 0;
01986    STRING str;
01987    int ret; /* for better error checking */
01988    char *imap_flags = NIL;
01989    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
01990 
01991     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
01992     if (msgnum < 0 && !imapgreetings) {
01993         return 0;
01994     }
01995    
01996    if (imap_check_limits(chan, vms, vmu, msgcount)) {
01997       return -1;
01998    }
01999 
02000    /* Set urgent flag for IMAP message */
02001    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02002       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02003       imap_flags="\\FLAGGED";
02004    }
02005    
02006    /* Attach only the first format */
02007    fmt = ast_strdupa(fmt);
02008    stringp = fmt;
02009    strsep(&stringp, "|");
02010 
02011    if (!ast_strlen_zero(vmu->serveremail))
02012       myserveremail = vmu->serveremail;
02013 
02014    if (msgnum > -1)
02015       make_file(fn, sizeof(fn), dir, msgnum);
02016    else
02017       ast_copy_string (fn, dir, sizeof(fn));
02018 
02019    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02020    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02021       *introfn = '\0';
02022    }
02023    
02024    if (ast_strlen_zero(vmu->email)) {
02025       /* We need the vmu->email to be set when we call make_email_file, but
02026        * if we keep it set, a duplicate e-mail will be created. So at the end
02027        * of this function, we will revert back to an empty string if tempcopy
02028        * is 1.
02029        */
02030       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02031       tempcopy = 1;
02032    }
02033 
02034    if (!strcmp(fmt, "wav49"))
02035       fmt = "WAV";
02036    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02037 
02038    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02039       command hangs. */
02040    if (!(p = vm_mkftemp(tmp))) {
02041       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02042       if (tempcopy)
02043          *(vmu->email) = '\0';
02044       return -1;
02045    }
02046 
02047    if (msgnum < 0 && imapgreetings) {
02048       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02049          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02050          return -1;
02051       }
02052       imap_delete_old_greeting(fn, vms);
02053    }
02054 
02055    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);
02056    /* read mail file to memory */
02057    len = ftell(p);
02058    rewind(p);
02059    if (!(buf = ast_malloc(len + 1))) {
02060       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02061       fclose(p);
02062       if (tempcopy)
02063          *(vmu->email) = '\0';
02064       return -1;
02065    }
02066    if (fread(buf, len, 1, p) < len) {
02067       if (ferror(p)) {
02068          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02069          return -1;
02070       }
02071    }
02072    ((char *)buf)[len] = '\0';
02073    INIT(&str, mail_string, buf, len);
02074    ret = init_mailstream(vms, NEW_FOLDER);
02075    if (ret == 0) {
02076       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02077       ast_mutex_lock(&vms->lock);
02078       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02079          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02080       ast_mutex_unlock(&vms->lock);
02081       fclose(p);
02082       unlink(tmp);
02083       ast_free(buf);
02084    } else {
02085       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
02086       fclose(p);
02087       unlink(tmp);
02088       ast_free(buf);
02089       return -1;
02090    }
02091    ast_debug(3, "%s stored\n", fn);
02092    
02093    if (tempcopy)
02094       *(vmu->email) = '\0';
02095    
02096    return 0;
02097 
02098 }
02099 
02100 /*!
02101  * \brief Gets the number of messages that exist in the inbox folder.
02102  * \param mailbox_context
02103  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02104  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02105  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02106  * 
02107  * This method is used when IMAP backend is used.
02108  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02109  *
02110  * \return zero on success, -1 on error.
02111  */
02112 
02113 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02114 {
02115    char tmp[PATH_MAX] = "";
02116    char *mailboxnc;
02117    char *context;
02118    char *mb;
02119    char *cur;
02120    if (newmsgs)
02121       *newmsgs = 0;
02122    if (oldmsgs)
02123       *oldmsgs = 0;
02124    if (urgentmsgs)
02125       *urgentmsgs = 0;
02126 
02127    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
02128    /* If no mailbox, return immediately */
02129    if (ast_strlen_zero(mailbox_context))
02130       return 0;
02131    
02132    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02133    context = strchr(tmp, '@');
02134    if (strchr(mailbox_context, ',')) {
02135       int tmpnew, tmpold, tmpurgent;
02136       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02137       mb = tmp;
02138       while ((cur = strsep(&mb, ", "))) {
02139          if (!ast_strlen_zero(cur)) {
02140             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02141                return -1;
02142             else {
02143                if (newmsgs)
02144                   *newmsgs += tmpnew; 
02145                if (oldmsgs)
02146                   *oldmsgs += tmpold;
02147                if (urgentmsgs)
02148                   *urgentmsgs += tmpurgent;
02149             }
02150          }
02151       }
02152       return 0;
02153    }
02154    if (context) {
02155       *context = '\0';
02156       mailboxnc = tmp;
02157       context++;
02158    } else {
02159       context = "default";
02160       mailboxnc = (char *)mailbox_context;
02161    }
02162    if (newmsgs) {
02163       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02164          return -1;
02165       }
02166    }
02167    if (oldmsgs) {
02168       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02169          return -1;
02170       }
02171    }
02172    if (urgentmsgs) {
02173       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02174          return -1;
02175       }
02176    }
02177    return 0;
02178 }
02179 
02180 /** 
02181  * \brief Determines if the given folder has messages.
02182  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02183  * \param folder the folder to look in
02184  *
02185  * This function is used when the mailbox is stored in an IMAP back end.
02186  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02187  * \return 1 if the folder has one or more messages. zero otherwise.
02188  */
02189 
02190 static int has_voicemail(const char *mailbox, const char *folder)
02191 {
02192    char tmp[256], *tmp2, *box, *context;
02193    ast_copy_string(tmp, mailbox, sizeof(tmp));
02194    tmp2 = tmp;
02195    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02196       while ((box = strsep(&tmp2, ",&"))) {
02197          if (!ast_strlen_zero(box)) {
02198             if (has_voicemail(box, folder)) {
02199                return 1;
02200             }
02201          }
02202       }
02203    }
02204    if ((context = strchr(tmp, '@'))) {
02205       *context++ = '\0';
02206    } else {
02207       context = "default";
02208    }
02209    return __messagecount(context, tmp, folder) ? 1 : 0;
02210 }
02211 
02212 /*!
02213  * \brief Copies a message from one mailbox to another.
02214  * \param chan
02215  * \param vmu
02216  * \param imbox
02217  * \param msgnum
02218  * \param duration
02219  * \param recip
02220  * \param fmt
02221  * \param dir
02222  *
02223  * This works with IMAP storage based mailboxes.
02224  *
02225  * \return zero on success, -1 on error.
02226  */
02227 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)
02228 {
02229    struct vm_state *sendvms = NULL, *destvms = NULL;
02230    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02231    if (msgnum >= recip->maxmsg) {
02232       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02233       return -1;
02234    }
02235    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02236       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02237       return -1;
02238    }
02239    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02240       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02241       return -1;
02242    }
02243    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02244    ast_mutex_lock(&sendvms->lock);
02245    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02246       ast_mutex_unlock(&sendvms->lock);
02247       return 0;
02248    }
02249    ast_mutex_unlock(&sendvms->lock);
02250    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02251    return -1;
02252 }
02253 
02254 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02255 {
02256    char tmp[256], *t = tmp;
02257    size_t left = sizeof(tmp);
02258    
02259    if (box == OLD_FOLDER) {
02260       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02261    } else {
02262       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02263    }
02264 
02265    if (box == NEW_FOLDER) {
02266       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02267    } else {
02268       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02269    }
02270 
02271    /* Build up server information */
02272    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02273 
02274    /* Add authentication user if present */
02275    if (!ast_strlen_zero(authuser))
02276       ast_build_string(&t, &left, "/authuser=%s", authuser);
02277 
02278    /* Add flags if present */
02279    if (!ast_strlen_zero(imapflags))
02280       ast_build_string(&t, &left, "/%s", imapflags);
02281 
02282    /* End with username */
02283 #if 1
02284    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02285 #else
02286    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02287 #endif
02288    if (box == NEW_FOLDER || box == OLD_FOLDER)
02289       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02290    else if (box == GREETINGS_FOLDER)
02291       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02292    else {   /* Other folders such as Friends, Family, etc... */
02293       if (!ast_strlen_zero(imapparentfolder)) {
02294          /* imapparentfolder would typically be set to INBOX */
02295          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02296       } else {
02297          snprintf(spec, len, "%s%s", tmp, mbox(box));
02298       }
02299    }
02300 }
02301 
02302 static int init_mailstream(struct vm_state *vms, int box)
02303 {
02304    MAILSTREAM *stream = NIL;
02305    long debug;
02306    char tmp[256];
02307    
02308    if (!vms) {
02309       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02310       return -1;
02311    }
02312    if (option_debug > 2)
02313       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02314    if (vms->mailstream == NIL || !vms->mailstream) {
02315       if (option_debug)
02316          ast_log (LOG_DEBUG,"mailstream not set.\n");
02317    } else {
02318       stream = vms->mailstream;
02319    }
02320    /* debug = T;  user wants protocol telemetry? */
02321    debug = NIL;  /* NO protocol telemetry? */
02322 
02323    if (delimiter == '\0') {      /* did not probe the server yet */
02324       char *cp;
02325 #ifdef USE_SYSTEM_IMAP
02326 #include <imap/linkage.c>
02327 #elif defined(USE_SYSTEM_CCLIENT)
02328 #include <c-client/linkage.c>
02329 #else
02330 #include "linkage.c"
02331 #endif
02332       /* Connect to INBOX first to get folders delimiter */
02333       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02334       ast_mutex_lock(&vms->lock);
02335       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02336       ast_mutex_unlock(&vms->lock);
02337       if (stream == NIL) {
02338          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02339          return -1;
02340       }
02341       get_mailbox_delimiter(stream);
02342       /* update delimiter in imapfolder */
02343       for (cp = imapfolder; *cp; cp++)
02344          if (*cp == '/')
02345             *cp = delimiter;
02346    }
02347    /* Now connect to the target folder */
02348    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02349    if (option_debug > 2)
02350       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02351    ast_mutex_lock(&vms->lock);
02352    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02353    ast_mutex_unlock(&vms->lock);
02354    if (vms->mailstream == NIL) {
02355       return -1;
02356    } else {
02357       return 0;
02358    }
02359 }
02360 
02361 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02362 {
02363    SEARCHPGM *pgm;
02364    SEARCHHEADER *hdr;
02365    int ret, urgent = 0;
02366 
02367    /* If Urgent, then look at INBOX */
02368    if (box == 11) {
02369       box = NEW_FOLDER;
02370       urgent = 1;
02371    }
02372 
02373    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02374    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02375    vms->imapversion = vmu->imapversion;
02376 
02377    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02378       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02379       return -1;
02380    }
02381    
02382    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02383    
02384    /* Check Quota */
02385    if  (box == 0)  {
02386       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02387       check_quota(vms,(char *)mbox(box));
02388    }
02389 
02390    ast_mutex_lock(&vms->lock);
02391    pgm = mail_newsearchpgm();
02392 
02393    /* Check IMAP folder for Asterisk messages only... */
02394    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02395    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02396    pgm->header = hdr;
02397    pgm->deleted = 0;
02398    pgm->undeleted = 1;
02399 
02400    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02401    if (box == NEW_FOLDER && urgent == 1) {
02402       pgm->unseen = 1;
02403       pgm->seen = 0;
02404       pgm->flagged = 1;
02405       pgm->unflagged = 0;
02406    } else if (box == NEW_FOLDER && urgent == 0) {
02407       pgm->unseen = 1;
02408       pgm->seen = 0;
02409       pgm->flagged = 0;
02410       pgm->unflagged = 1;
02411    } else if (box == OLD_FOLDER) {
02412       pgm->seen = 1;
02413       pgm->unseen = 0;
02414    }
02415 
02416    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02417 
02418    vms->vmArrayIndex = 0;
02419    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02420    vms->lastmsg = vms->vmArrayIndex - 1;
02421    mail_free_searchpgm(&pgm);
02422 
02423    ast_mutex_unlock(&vms->lock);
02424    return 0;
02425 }
02426 
02427 static void write_file(char *filename, char *buffer, unsigned long len)
02428 {
02429    FILE *output;
02430 
02431    output = fopen (filename, "w");
02432    if (fwrite(buffer, len, 1, output) != 1) {
02433       if (ferror(output)) {
02434          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02435       }
02436    }
02437    fclose (output);
02438 }
02439 
02440 static void update_messages_by_imapuser(const char *user, unsigned long number)
02441 {
02442    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02443 
02444    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02445       return;
02446    }
02447 
02448    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02449    vms->msgArray[vms->vmArrayIndex++] = number;
02450 }
02451 
02452 void mm_searched(MAILSTREAM *stream, unsigned long number)
02453 {
02454    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02455 
02456    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02457       return;
02458 
02459    update_messages_by_imapuser(user, number);
02460 }
02461 
02462 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02463 {
02464    struct ast_variable *var;
02465    struct ast_vm_user *vmu;
02466 
02467    vmu = ast_calloc(1, sizeof *vmu);
02468    if (!vmu)
02469       return NULL;
02470    ast_set_flag(vmu, VM_ALLOCED);
02471    populate_defaults(vmu);
02472 
02473    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02474    if (var) {
02475       apply_options_full(vmu, var);
02476       ast_variables_destroy(var);
02477       return vmu;
02478    } else {
02479       ast_free(vmu);
02480       return NULL;
02481    }
02482 }
02483 
02484 /* Interfaces to C-client */
02485 
02486 void mm_exists(MAILSTREAM * stream, unsigned long number)
02487 {
02488    /* mail_ping will callback here if new mail! */
02489    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02490    if (number == 0) return;
02491    set_update(stream);
02492 }
02493 
02494 
02495 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02496 {
02497    /* mail_ping will callback here if expunged mail! */
02498    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02499    if (number == 0) return;
02500    set_update(stream);
02501 }
02502 
02503 
02504 void mm_flags(MAILSTREAM * stream, unsigned long number)
02505 {
02506    /* mail_ping will callback here if read mail! */
02507    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02508    if (number == 0) return;
02509    set_update(stream);
02510 }
02511 
02512 
02513 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02514 {
02515    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02516    mm_log (string, errflg);
02517 }
02518 
02519 
02520 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02521 {
02522    if (delimiter == '\0') {
02523       delimiter = delim;
02524    }
02525 
02526    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02527    if (attributes & LATT_NOINFERIORS)
02528       ast_debug(5, "no inferiors\n");
02529    if (attributes & LATT_NOSELECT)
02530       ast_debug(5, "no select\n");
02531    if (attributes & LATT_MARKED)
02532       ast_debug(5, "marked\n");
02533    if (attributes & LATT_UNMARKED)
02534       ast_debug(5, "unmarked\n");
02535 }
02536 
02537 
02538 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02539 {
02540    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02541    if (attributes & LATT_NOINFERIORS)
02542       ast_debug(5, "no inferiors\n");
02543    if (attributes & LATT_NOSELECT)
02544       ast_debug(5, "no select\n");
02545    if (attributes & LATT_MARKED)
02546       ast_debug(5, "marked\n");
02547    if (attributes & LATT_UNMARKED)
02548       ast_debug(5, "unmarked\n");
02549 }
02550 
02551 
02552 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02553 {
02554    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02555    if (status->flags & SA_MESSAGES)
02556       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02557    if (status->flags & SA_RECENT)
02558       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02559    if (status->flags & SA_UNSEEN)
02560       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02561    if (status->flags & SA_UIDVALIDITY)
02562       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02563    if (status->flags & SA_UIDNEXT)
02564       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02565    ast_log(AST_LOG_NOTICE, "\n");
02566 }
02567 
02568 
02569 void mm_log(char *string, long errflg)
02570 {
02571    switch ((short) errflg) {
02572       case NIL:
02573          ast_debug(1,"IMAP Info: %s\n", string);
02574          break;
02575       case PARSE:
02576       case WARN:
02577          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02578          break;
02579       case ERROR:
02580          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02581          break;
02582    }
02583 }
02584 
02585 
02586 void mm_dlog(char *string)
02587 {
02588    ast_log(AST_LOG_NOTICE, "%s\n", string);
02589 }
02590 
02591 
02592 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02593 {
02594    struct ast_vm_user *vmu;
02595 
02596    ast_debug(4, "Entering callback mm_login\n");
02597 
02598    ast_copy_string(user, mb->user, MAILTMPLEN);
02599 
02600    /* We should only do this when necessary */
02601    if (!ast_strlen_zero(authpassword)) {
02602       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02603    } else {
02604       AST_LIST_TRAVERSE(&users, vmu, list) {
02605          if (!strcasecmp(mb->user, vmu->imapuser)) {
02606             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02607             break;
02608          }
02609       }
02610       if (!vmu) {
02611          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02612             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02613             free_user(vmu);
02614          }
02615       }
02616    }
02617 }
02618 
02619 
02620 void mm_critical(MAILSTREAM * stream)
02621 {
02622 }
02623 
02624 
02625 void mm_nocritical(MAILSTREAM * stream)
02626 {
02627 }
02628 
02629 
02630 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02631 {
02632    kill (getpid (), SIGSTOP);
02633    return NIL;
02634 }
02635 
02636 
02637 void mm_fatal(char *string)
02638 {
02639    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02640 }
02641 
02642 /* C-client callback to handle quota */
02643 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02644 {
02645    struct vm_state *vms;
02646    char *mailbox = stream->mailbox, *user;
02647    char buf[1024] = "";
02648    unsigned long usage = 0, limit = 0;
02649    
02650    while (pquota) {
02651       usage = pquota->usage;
02652       limit = pquota->limit;
02653       pquota = pquota->next;
02654    }
02655    
02656    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)))) {
02657       ast_log(AST_LOG_ERROR, "No state found.\n");
02658       return;
02659    }
02660 
02661    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02662 
02663    vms->quota_usage = usage;
02664    vms->quota_limit = limit;
02665 }
02666 
02667 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02668 {
02669    char *start, *eol_pnt;
02670    int taglen;
02671 
02672    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02673       return NULL;
02674 
02675    taglen = strlen(tag) + 1;
02676    if (taglen < 1)
02677       return NULL;
02678 
02679    if (!(start = strstr(header, tag)))
02680       return NULL;
02681 
02682    /* Since we can be called multiple times we should clear our buffer */
02683    memset(buf, 0, len);
02684 
02685    ast_copy_string(buf, start+taglen, len);
02686    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02687       *eol_pnt = '\0';
02688    return buf;
02689 }
02690 
02691 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02692 {
02693    char *start, *quote, *eol_pnt;
02694 
02695    if (ast_strlen_zero(mailbox))
02696       return NULL;
02697 
02698    if (!(start = strstr(mailbox, "/user=")))
02699       return NULL;
02700 
02701    ast_copy_string(buf, start+6, len);
02702 
02703    if (!(quote = strchr(buf, '\"'))) {
02704       if (!(eol_pnt = strchr(buf, '/')))
02705          eol_pnt = strchr(buf,'}');
02706       *eol_pnt = '\0';
02707       return buf;
02708    } else {
02709       eol_pnt = strchr(buf+1,'\"');
02710       *eol_pnt = '\0';
02711       return buf+1;
02712    }
02713 }
02714 
02715 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02716 {
02717    struct vm_state *vms_p;
02718 
02719    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02720    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02721       return vms_p;
02722    }
02723    if (option_debug > 4)
02724       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02725    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02726       return NULL;
02727    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02728    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02729    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02730    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02731    vms_p->imapversion = vmu->imapversion;
02732    if (option_debug > 4)
02733       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02734    vms_p->updated = 1;
02735    /* set mailbox to INBOX! */
02736    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02737    init_vm_state(vms_p);
02738    vmstate_insert(vms_p);
02739    return vms_p;
02740 }
02741 
02742 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02743 {
02744    struct vmstate *vlist = NULL;
02745 
02746    if (interactive) {
02747       struct vm_state *vms;
02748       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02749       vms = pthread_getspecific(ts_vmstate.key);
02750       return vms;
02751    }
02752 
02753    AST_LIST_LOCK(&vmstates);
02754    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02755       if (!vlist->vms) {
02756          ast_debug(3, "error: vms is NULL for %s\n", user);
02757          continue;
02758       }
02759       if (vlist->vms->imapversion != imapversion) {
02760          continue;
02761       }
02762       if (!vlist->vms->imapuser) {
02763          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02764          continue;
02765       }
02766 
02767       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02768          AST_LIST_UNLOCK(&vmstates);
02769          return vlist->vms;
02770       }
02771    }
02772    AST_LIST_UNLOCK(&vmstates);
02773 
02774    ast_debug(3, "%s not found in vmstates\n", user);
02775 
02776    return NULL;
02777 }
02778 
02779 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02780 {
02781 
02782    struct vmstate *vlist = NULL;
02783    const char *local_context = S_OR(context, "default");
02784 
02785    if (interactive) {
02786       struct vm_state *vms;
02787       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02788       vms = pthread_getspecific(ts_vmstate.key);
02789       return vms;
02790    }
02791 
02792    AST_LIST_LOCK(&vmstates);
02793    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02794       if (!vlist->vms) {
02795          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02796          continue;
02797       }
02798       if (vlist->vms->imapversion != imapversion) {
02799          continue;
02800       }
02801       if (!vlist->vms->username || !vlist->vms->context) {
02802          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02803          continue;
02804       }
02805 
02806       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);
02807       
02808       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02809          ast_debug(3, "Found it!\n");
02810          AST_LIST_UNLOCK(&vmstates);
02811          return vlist->vms;
02812       }
02813    }
02814    AST_LIST_UNLOCK(&vmstates);
02815 
02816    ast_debug(3, "%s not found in vmstates\n", mailbox);
02817 
02818    return NULL;
02819 }
02820 
02821 static void vmstate_insert(struct vm_state *vms) 
02822 {
02823    struct vmstate *v;
02824    struct vm_state *altvms;
02825 
02826    /* If interactive, it probably already exists, and we should
02827       use the one we already have since it is more up to date.
02828       We can compare the username to find the duplicate */
02829    if (vms->interactive == 1) {
02830       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02831       if (altvms) {  
02832          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02833          vms->newmessages = altvms->newmessages;
02834          vms->oldmessages = altvms->oldmessages;
02835          vms->vmArrayIndex = altvms->vmArrayIndex;
02836          vms->lastmsg = altvms->lastmsg;
02837          vms->curmsg = altvms->curmsg;
02838          /* get a pointer to the persistent store */
02839          vms->persist_vms = altvms;
02840          /* Reuse the mailstream? */
02841 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02842          vms->mailstream = altvms->mailstream;
02843 #else
02844          vms->mailstream = NIL;
02845 #endif
02846       }
02847       return;
02848    }
02849 
02850    if (!(v = ast_calloc(1, sizeof(*v))))
02851       return;
02852    
02853    v->vms = vms;
02854 
02855    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02856 
02857    AST_LIST_LOCK(&vmstates);
02858    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02859    AST_LIST_UNLOCK(&vmstates);
02860 }
02861 
02862 static void vmstate_delete(struct vm_state *vms) 
02863 {
02864    struct vmstate *vc = NULL;
02865    struct vm_state *altvms = NULL;
02866 
02867    /* If interactive, we should copy pertinent info
02868       back to the persistent state (to make update immediate) */
02869    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02870       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02871       altvms->newmessages = vms->newmessages;
02872       altvms->oldmessages = vms->oldmessages;
02873       altvms->updated = 1;
02874       vms->mailstream = mail_close(vms->mailstream);
02875 
02876       /* Interactive states are not stored within the persistent list */
02877       return;
02878    }
02879    
02880    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02881    
02882    AST_LIST_LOCK(&vmstates);
02883    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02884       if (vc->vms == vms) {
02885          AST_LIST_REMOVE_CURRENT(list);
02886          break;
02887       }
02888    }
02889    AST_LIST_TRAVERSE_SAFE_END
02890    AST_LIST_UNLOCK(&vmstates);
02891    
02892    if (vc) {
02893       ast_mutex_destroy(&vc->vms->lock);
02894       ast_free(vc);
02895    }
02896    else
02897       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02898 }
02899 
02900 static void set_update(MAILSTREAM * stream) 
02901 {
02902    struct vm_state *vms;
02903    char *mailbox = stream->mailbox, *user;
02904    char buf[1024] = "";
02905 
02906    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02907       if (user && option_debug > 2)
02908          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02909       return;
02910    }
02911 
02912    ast_debug(3, "User %s mailbox set for update.\n", user);
02913 
02914    vms->updated = 1; /* Set updated flag since mailbox changed */
02915 }
02916 
02917 static void init_vm_state(struct vm_state *vms) 
02918 {
02919    int x;
02920    vms->vmArrayIndex = 0;
02921    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02922       vms->msgArray[x] = 0;
02923    }
02924    ast_mutex_init(&vms->lock);
02925 }
02926 
02927 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02928 {
02929    char *body_content;
02930    char *body_decoded;
02931    char *fn = is_intro ? vms->introfn : vms->fn;
02932    unsigned long len;
02933    unsigned long newlen;
02934    char filename[256];
02935    
02936    if (!body || body == NIL)
02937       return -1;
02938 
02939    ast_mutex_lock(&vms->lock);
02940    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02941    ast_mutex_unlock(&vms->lock);
02942    if (body_content != NIL) {
02943       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02944       /* ast_debug(1,body_content); */
02945       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02946       /* If the body of the file is empty, return an error */
02947       if (!newlen) {
02948          return -1;
02949       }
02950       write_file(filename, (char *) body_decoded, newlen);
02951    } else {
02952       ast_debug(5, "Body of message is NULL.\n");
02953       return -1;
02954    }
02955    return 0;
02956 }
02957 
02958 /*! 
02959  * \brief Get delimiter via mm_list callback 
02960  * \param stream
02961  *
02962  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02963  */
02964 /* MUTEX should already be held */
02965 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02966    char tmp[50];
02967    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02968    mail_list(stream, tmp, "*");
02969 }
02970 
02971 /*! 
02972  * \brief Check Quota for user 
02973  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02974  * \param mailbox the mailbox to check the quota for.
02975  *
02976  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02977  */
02978 static void check_quota(struct vm_state *vms, char *mailbox) {
02979    ast_mutex_lock(&vms->lock);
02980    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02981    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02982    if (vms && vms->mailstream != NULL) {
02983       imap_getquotaroot(vms->mailstream, mailbox);
02984    } else {
02985       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02986    }
02987    ast_mutex_unlock(&vms->lock);
02988 }
02989 
02990 #endif /* IMAP_STORAGE */
02991 
02992 /*! \brief Lock file path
02993     only return failure if ast_lock_path returns 'timeout',
02994    not if the path does not exist or any other reason
02995 */
02996 static int vm_lock_path(const char *path)
02997 {
02998    switch (ast_lock_path(path)) {
02999    case AST_LOCK_TIMEOUT:
03000       return -1;
03001    default:
03002       return 0;
03003    }
03004 }
03005 
03006 
03007 #ifdef ODBC_STORAGE
03008 struct generic_prepare_struct {
03009    char *sql;
03010    int argc;
03011    char **argv;
03012 };
03013 
03014 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03015 {
03016    struct generic_prepare_struct *gps = data;
03017    int res, i;
03018    SQLHSTMT stmt;
03019 
03020    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03021    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03022       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03023       return NULL;
03024    }
03025    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
03026    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03027       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03028       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03029       return NULL;
03030    }
03031    for (i = 0; i < gps->argc; i++)
03032       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03033 
03034    return stmt;
03035 }
03036 
03037 /*!
03038  * \brief Retrieves a file from an ODBC data store.
03039  * \param dir the path to the file to be retreived.
03040  * \param msgnum the message number, such as within a mailbox folder.
03041  * 
03042  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03043  * 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.
03044  *
03045  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03046  * The output is the message information file with the name msgnum and the extension .txt
03047  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03048  * 
03049  * \return 0 on success, -1 on error.
03050  */
03051 static int retrieve_file(char *dir, int msgnum)
03052 {
03053    int x = 0;
03054    int res;
03055    int fd=-1;
03056    size_t fdlen = 0;
03057    void *fdm = MAP_FAILED;
03058    SQLSMALLINT colcount=0;
03059    SQLHSTMT stmt;
03060    char sql[PATH_MAX];
03061    char fmt[80]="";
03062    char *c;
03063    char coltitle[256];
03064    SQLSMALLINT collen;
03065    SQLSMALLINT datatype;
03066    SQLSMALLINT decimaldigits;
03067    SQLSMALLINT nullable;
03068    SQLULEN colsize;
03069    SQLLEN colsize2;
03070    FILE *f=NULL;
03071    char rowdata[80];
03072    char fn[PATH_MAX];
03073    char full_fn[PATH_MAX];
03074    char msgnums[80];
03075    char *argv[] = { dir, msgnums };
03076    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03077 
03078    struct odbc_obj *obj;
03079    obj = ast_odbc_request_obj(odbc_database, 0);
03080    if (obj) {
03081       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03082       c = strchr(fmt, '|');
03083       if (c)
03084          *c = '\0';
03085       if (!strcasecmp(fmt, "wav49"))
03086          strcpy(fmt, "WAV");
03087       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03088       if (msgnum > -1)
03089          make_file(fn, sizeof(fn), dir, msgnum);
03090       else
03091          ast_copy_string(fn, dir, sizeof(fn));
03092 
03093       /* Create the information file */
03094       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03095       
03096       if (!(f = fopen(full_fn, "w+"))) {
03097          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03098          goto yuck;
03099       }
03100       
03101       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03102       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03103       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03104       if (!stmt) {
03105          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03106          ast_odbc_release_obj(obj);
03107          goto yuck;
03108       }
03109       res = SQLFetch(stmt);
03110       if (res == SQL_NO_DATA) {
03111          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03112          ast_odbc_release_obj(obj);
03113          goto yuck;
03114       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03115          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03116          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03117          ast_odbc_release_obj(obj);
03118          goto yuck;
03119       }
03120       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03121       if (fd < 0) {
03122          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03123          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03124          ast_odbc_release_obj(obj);
03125          goto yuck;
03126       }
03127       res = SQLNumResultCols(stmt, &colcount);
03128       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03129          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03130          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03131          ast_odbc_release_obj(obj);
03132          goto yuck;
03133       }
03134       if (f) 
03135          fprintf(f, "[message]\n");
03136       for (x=0;x<colcount;x++) {
03137          rowdata[0] = '\0';
03138          collen = sizeof(coltitle);
03139          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
03140                   &datatype, &colsize, &decimaldigits, &nullable);
03141          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03142             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03143             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03144             ast_odbc_release_obj(obj);
03145             goto yuck;
03146          }
03147          if (!strcasecmp(coltitle, "recording")) {
03148             off_t offset;
03149             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03150             fdlen = colsize2;
03151             if (fd > -1) {
03152                char tmp[1]="";
03153                lseek(fd, fdlen - 1, SEEK_SET);
03154                if (write(fd, tmp, 1) != 1) {
03155                   close(fd);
03156                   fd = -1;
03157                   continue;
03158                }
03159                /* Read out in small chunks */
03160                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03161                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03162                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03163                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03164                      ast_odbc_release_obj(obj);
03165                      goto yuck;
03166                   } else {
03167                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03168                      munmap(fdm, CHUNKSIZE);
03169                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03170                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03171                         unlink(full_fn);
03172                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03173                         ast_odbc_release_obj(obj);
03174                         goto yuck;
03175                      }
03176                   }
03177                }
03178                if (truncate(full_fn, fdlen) < 0) {
03179                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03180                }
03181             }
03182          } else {
03183             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03184             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03185                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03186                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03187                ast_odbc_release_obj(obj);
03188                goto yuck;
03189             }
03190             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03191                fprintf(f, "%s=%s\n", coltitle, rowdata);
03192          }
03193       }
03194       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03195       ast_odbc_release_obj(obj);
03196    } else
03197       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03198 yuck: 
03199    if (f)
03200       fclose(f);
03201    if (fd > -1)
03202       close(fd);
03203    return x - 1;
03204 }
03205 
03206 /*!
03207  * \brief Determines the highest message number in use for a given user and mailbox folder.
03208  * \param vmu 
03209  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03210  *
03211  * This method is used when mailboxes are stored in an ODBC back end.
03212  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03213  *
03214  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03215  */
03216 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03217 {
03218    int x = 0;
03219    int res;
03220    SQLHSTMT stmt;
03221    char sql[PATH_MAX];
03222    char rowdata[20];
03223    char *argv[] = { dir };
03224    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03225 
03226    struct odbc_obj *obj;
03227    obj = ast_odbc_request_obj(odbc_database, 0);
03228    if (obj) {
03229       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03230       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03231       if (!stmt) {
03232          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03233          ast_odbc_release_obj(obj);
03234          goto yuck;
03235       }
03236       res = SQLFetch(stmt);
03237       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03238          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03239          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03240          ast_odbc_release_obj(obj);
03241          goto yuck;
03242       }
03243       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03244       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03245          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03246          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03247          ast_odbc_release_obj(obj);
03248          goto yuck;
03249       }
03250       if (sscanf(rowdata, "%30d", &x) != 1)
03251          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03252       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03253       ast_odbc_release_obj(obj);
03254    } else
03255       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03256 yuck: 
03257    return x - 1;
03258 }
03259 
03260 /*!
03261  * \brief Determines if the specified message exists.
03262  * \param dir the folder the mailbox folder to look for messages. 
03263  * \param msgnum the message index to query for.
03264  *
03265  * This method is used when mailboxes are stored in an ODBC back end.
03266  *
03267  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03268  */
03269 static int message_exists(char *dir, int msgnum)
03270 {
03271    int x = 0;
03272    int res;
03273    SQLHSTMT stmt;
03274    char sql[PATH_MAX];
03275    char rowdata[20];
03276    char msgnums[20];
03277    char *argv[] = { dir, msgnums };
03278    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03279 
03280    struct odbc_obj *obj;
03281    obj = ast_odbc_request_obj(odbc_database, 0);
03282    if (obj) {
03283       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03284       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03285       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03286       if (!stmt) {
03287          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03288          ast_odbc_release_obj(obj);
03289          goto yuck;
03290       }
03291       res = SQLFetch(stmt);
03292       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03293          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03294          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03295          ast_odbc_release_obj(obj);
03296          goto yuck;
03297       }
03298       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03299       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03300          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03301          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03302          ast_odbc_release_obj(obj);
03303          goto yuck;
03304       }
03305       if (sscanf(rowdata, "%30d", &x) != 1)
03306          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03307       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03308       ast_odbc_release_obj(obj);
03309    } else
03310       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03311 yuck: 
03312    return x;
03313 }
03314 
03315 /*!
03316  * \brief returns the one-based count for messages.
03317  * \param vmu
03318  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03319  *
03320  * This method is used when mailboxes are stored in an ODBC back end.
03321  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03322  * one-based messages.
03323  * This method just calls last_message_index and returns +1 of its value.
03324  *
03325  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03326  */
03327 static int count_messages(struct ast_vm_user *vmu, char *dir)
03328 {
03329    return last_message_index(vmu, dir) + 1;
03330 }
03331 
03332 /*!
03333  * \brief Deletes a message from the mailbox folder.
03334  * \param sdir The mailbox folder to work in.
03335  * \param smsg The message index to be deleted.
03336  *
03337  * This method is used when mailboxes are stored in an ODBC back end.
03338  * The specified message is directly deleted from the database 'voicemessages' table.
03339  * 
03340  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03341  */
03342 static void delete_file(const char *sdir, int smsg)
03343 {
03344    SQLHSTMT stmt;
03345    char sql[PATH_MAX];
03346    char msgnums[20];
03347    char *argv[] = { NULL, msgnums };
03348    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03349    struct odbc_obj *obj;
03350 
03351    argv[0] = ast_strdupa(sdir);
03352 
03353    obj = ast_odbc_request_obj(odbc_database, 0);
03354    if (obj) {
03355       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03356       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03357       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03358       if (!stmt)
03359          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03360       else
03361          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03362       ast_odbc_release_obj(obj);
03363    } else
03364       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03365    return;  
03366 }
03367 
03368 /*!
03369  * \brief Copies a voicemail from one mailbox to another.
03370  * \param sdir the folder for which to look for the message to be copied.
03371  * \param smsg the index of the message to be copied.
03372  * \param ddir the destination folder to copy the message into.
03373  * \param dmsg the index to be used for the copied message.
03374  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03375  * \param dmailboxcontext The context for the destination user.
03376  *
03377  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03378  */
03379 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03380 {
03381    SQLHSTMT stmt;
03382    char sql[512];
03383    char msgnums[20];
03384    char msgnumd[20];
03385    struct odbc_obj *obj;
03386    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03387    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03388 
03389    delete_file(ddir, dmsg);
03390    obj = ast_odbc_request_obj(odbc_database, 0);
03391    if (obj) {
03392       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03393       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03394       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);
03395       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03396       if (!stmt)
03397          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03398       else
03399          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03400       ast_odbc_release_obj(obj);
03401    } else
03402       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03403    return;  
03404 }
03405 
03406 struct insert_data {
03407    char *sql;
03408    const char *dir;
03409    const char *msgnums;
03410    void *data;
03411    SQLLEN datalen;
03412    SQLLEN indlen;
03413    const char *context;
03414    const char *macrocontext;
03415    const char *callerid;
03416    const char *origtime;
03417    const char *duration;
03418    const char *mailboxuser;
03419    const char *mailboxcontext;
03420    const char *category;
03421    const char *flag;
03422 };
03423 
03424 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03425 {
03426    struct insert_data *data = vdata;
03427    int res;
03428    SQLHSTMT stmt;
03429 
03430    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03431    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03432       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03433       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03434       return NULL;
03435    }
03436 
03437    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03438    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03439    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03440    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03441    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03442    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03443    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03444    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03445    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03446    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03447    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03448    if (!ast_strlen_zero(data->category)) {
03449       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03450    }
03451    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03452    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03453       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03454       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03455       return NULL;
03456    }
03457 
03458    return stmt;
03459 }
03460 
03461 /*!
03462  * \brief Stores a voicemail into the database.
03463  * \param dir the folder the mailbox folder to store the message.
03464  * \param mailboxuser the user owning the mailbox folder.
03465  * \param mailboxcontext
03466  * \param msgnum the message index for the message to be stored.
03467  *
03468  * This method is used when mailboxes are stored in an ODBC back end.
03469  * The message sound file and information file is looked up on the file system. 
03470  * A SQL query is invoked to store the message into the (MySQL) database.
03471  *
03472  * \return the zero on success -1 on error.
03473  */
03474 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03475 {
03476    int res = 0;
03477    int fd = -1;
03478    void *fdm = MAP_FAILED;
03479    size_t fdlen = -1;
03480    SQLHSTMT stmt;
03481    char sql[PATH_MAX];
03482    char msgnums[20];
03483    char fn[PATH_MAX];
03484    char full_fn[PATH_MAX];
03485    char fmt[80]="";
03486    char *c;
03487    struct ast_config *cfg=NULL;
03488    struct odbc_obj *obj;
03489    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03490       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03491    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03492 
03493    delete_file(dir, msgnum);
03494    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03495       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03496       return -1;
03497    }
03498 
03499    do {
03500       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03501       c = strchr(fmt, '|');
03502       if (c)
03503          *c = '\0';
03504       if (!strcasecmp(fmt, "wav49"))
03505          strcpy(fmt, "WAV");
03506       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03507       if (msgnum > -1)
03508          make_file(fn, sizeof(fn), dir, msgnum);
03509       else
03510          ast_copy_string(fn, dir, sizeof(fn));
03511       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03512       cfg = ast_config_load(full_fn, config_flags);
03513       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03514       fd = open(full_fn, O_RDWR);
03515       if (fd < 0) {
03516          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03517          res = -1;
03518          break;
03519       }
03520       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03521          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03522             idata.context = "";
03523          }
03524          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03525             idata.macrocontext = "";
03526          }
03527          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03528             idata.callerid = "";
03529          }
03530          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03531             idata.origtime = "";
03532          }
03533          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03534             idata.duration = "";
03535          }
03536          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03537             idata.category = "";
03538          }
03539          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03540             idata.flag = "";
03541          }
03542       }
03543       fdlen = lseek(fd, 0, SEEK_END);
03544       lseek(fd, 0, SEEK_SET);
03545       printf("Length is %zd\n", fdlen);
03546       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03547       if (fdm == MAP_FAILED) {
03548          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03549          res = -1;
03550          break;
03551       } 
03552       idata.data = fdm;
03553       idata.datalen = idata.indlen = fdlen;
03554 
03555       if (!ast_strlen_zero(idata.category)) 
03556          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03557       else
03558          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03559 
03560       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03561          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03562       } else {
03563          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03564          res = -1;
03565       }
03566    } while (0);
03567    if (obj) {
03568       ast_odbc_release_obj(obj);
03569    }
03570    if (cfg)
03571       ast_config_destroy(cfg);
03572    if (fdm != MAP_FAILED)
03573       munmap(fdm, fdlen);
03574    if (fd > -1)
03575       close(fd);
03576    return res;
03577 }
03578 
03579 /*!
03580  * \brief Renames a message in a mailbox folder.
03581  * \param sdir The folder of the message to be renamed.
03582  * \param smsg The index of the message to be renamed.
03583  * \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.
03584  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03585  * \param ddir The destination folder for the message to be renamed into
03586  * \param dmsg The destination message for the message to be renamed.
03587  *
03588  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03589  * 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.
03590  * 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.
03591  */
03592 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03593 {
03594    SQLHSTMT stmt;
03595    char sql[PATH_MAX];
03596    char msgnums[20];
03597    char msgnumd[20];
03598    struct odbc_obj *obj;
03599    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03600    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03601 
03602    delete_file(ddir, dmsg);
03603    obj = ast_odbc_request_obj(odbc_database, 0);
03604    if (obj) {
03605       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03606       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03607       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03608       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03609       if (!stmt)
03610          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03611       else
03612          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03613       ast_odbc_release_obj(obj);
03614    } else
03615       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03616    return;  
03617 }
03618 
03619 /*!
03620  * \brief Removes a voicemail message file.
03621  * \param dir the path to the message file.
03622  * \param msgnum the unique number for the message within the mailbox.
03623  *
03624  * Removes the message content file and the information file.
03625  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03626  * Typical use is to clean up after a RETRIEVE operation. 
03627  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03628  * \return zero on success, -1 on error.
03629  */
03630 static int remove_file(char *dir, int msgnum)
03631 {
03632    char fn[PATH_MAX];
03633    char full_fn[PATH_MAX];
03634    char msgnums[80];
03635    
03636    if (msgnum > -1) {
03637       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03638       make_file(fn, sizeof(fn), dir, msgnum);
03639    } else
03640       ast_copy_string(fn, dir, sizeof(fn));
03641    ast_filedelete(fn, NULL);  
03642    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03643    unlink(full_fn);
03644    return 0;
03645 }
03646 #else
03647 #ifndef IMAP_STORAGE
03648 /*!
03649  * \brief Find all .txt files - even if they are not in sequence from 0000.
03650  * \param vmu
03651  * \param dir
03652  *
03653  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03654  *
03655  * \return the count of messages, zero or more.
03656  */
03657 static int count_messages(struct ast_vm_user *vmu, char *dir)
03658 {
03659 
03660    int vmcount = 0;
03661    DIR *vmdir = NULL;
03662    struct dirent *vment = NULL;
03663 
03664    if (vm_lock_path(dir))
03665       return ERROR_LOCK_PATH;
03666 
03667    if ((vmdir = opendir(dir))) {
03668       while ((vment = readdir(vmdir))) {
03669          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03670             vmcount++;
03671          }
03672       }
03673       closedir(vmdir);
03674    }
03675    ast_unlock_path(dir);
03676    
03677    return vmcount;
03678 }
03679 
03680 /*!
03681  * \brief Renames a message in a mailbox folder.
03682  * \param sfn The path to the mailbox information and data file to be renamed.
03683  * \param dfn The path for where the message data and information files will be renamed to.
03684  *
03685  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03686  */
03687 static void rename_file(char *sfn, char *dfn)
03688 {
03689    char stxt[PATH_MAX];
03690    char dtxt[PATH_MAX];
03691    ast_filerename(sfn,dfn,NULL);
03692    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03693    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03694    if (ast_check_realtime("voicemail_data")) {
03695       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03696    }
03697    rename(stxt, dtxt);
03698 }
03699 
03700 /*! 
03701  * \brief Determines the highest message number in use for a given user and mailbox folder.
03702  * \param vmu 
03703  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03704  *
03705  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03706  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03707  *
03708  * \note Should always be called with a lock already set on dir.
03709  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03710  */
03711 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03712 {
03713    int x;
03714    unsigned char map[MAXMSGLIMIT] = "";
03715    DIR *msgdir;
03716    struct dirent *msgdirent;
03717    int msgdirint;
03718 
03719    /* Reading the entire directory into a file map scales better than
03720     * doing a stat repeatedly on a predicted sequence.  I suspect this
03721     * is partially due to stat(2) internally doing a readdir(2) itself to
03722     * find each file. */
03723    if (!(msgdir = opendir(dir))) {
03724       return -1;
03725    }
03726 
03727    while ((msgdirent = readdir(msgdir))) {
03728       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03729          map[msgdirint] = 1;
03730    }
03731    closedir(msgdir);
03732 
03733    for (x = 0; x < vmu->maxmsg; x++) {
03734       if (map[x] == 0)
03735          break;
03736    }
03737 
03738    return x - 1;
03739 }
03740 
03741 #endif /* #ifndef IMAP_STORAGE */
03742 #endif /* #else of #ifdef ODBC_STORAGE */
03743 #ifndef IMAP_STORAGE
03744 /*!
03745  * \brief Utility function to copy a file.
03746  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03747  * \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.
03748  *
03749  * 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.
03750  * The copy operation copies up to 4096 bytes at once.
03751  *
03752  * \return zero on success, -1 on error.
03753  */
03754 static int copy(char *infile, char *outfile)
03755 {
03756    int ifd;
03757    int ofd;
03758    int res;
03759    int len;
03760    char buf[4096];
03761 
03762 #ifdef HARDLINK_WHEN_POSSIBLE
03763    /* Hard link if possible; saves disk space & is faster */
03764    if (link(infile, outfile)) {
03765 #endif
03766       if ((ifd = open(infile, O_RDONLY)) < 0) {
03767          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03768          return -1;
03769       }
03770       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03771          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03772          close(ifd);
03773          return -1;
03774       }
03775       do {
03776          len = read(ifd, buf, sizeof(buf));
03777          if (len < 0) {
03778             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03779             close(ifd);
03780             close(ofd);
03781             unlink(outfile);
03782          }
03783          if (len) {
03784             res = write(ofd, buf, len);
03785             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03786                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03787                close(ifd);
03788                close(ofd);
03789                unlink(outfile);
03790             }
03791          }
03792       } while (len);
03793       close(ifd);
03794       close(ofd);
03795       return 0;
03796 #ifdef HARDLINK_WHEN_POSSIBLE
03797    } else {
03798       /* Hard link succeeded */
03799       return 0;
03800    }
03801 #endif
03802 }
03803 
03804 /*!
03805  * \brief Copies a voicemail information (envelope) file.
03806  * \param frompath
03807  * \param topath 
03808  *
03809  * Every voicemail has the data (.wav) file, and the information file.
03810  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03811  * This is used by the COPY macro when not using IMAP storage.
03812  */
03813 static void copy_plain_file(char *frompath, char *topath)
03814 {
03815    char frompath2[PATH_MAX], topath2[PATH_MAX];
03816    struct ast_variable *tmp,*var = NULL;
03817    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03818    ast_filecopy(frompath, topath, NULL);
03819    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03820    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03821    if (ast_check_realtime("voicemail_data")) {
03822       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03823       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03824       for (tmp = var; tmp; tmp = tmp->next) {
03825          if (!strcasecmp(tmp->name, "origmailbox")) {
03826             origmailbox = tmp->value;
03827          } else if (!strcasecmp(tmp->name, "context")) {
03828             context = tmp->value;
03829          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03830             macrocontext = tmp->value;
03831          } else if (!strcasecmp(tmp->name, "exten")) {
03832             exten = tmp->value;
03833          } else if (!strcasecmp(tmp->name, "priority")) {
03834             priority = tmp->value;
03835          } else if (!strcasecmp(tmp->name, "callerchan")) {
03836             callerchan = tmp->value;
03837          } else if (!strcasecmp(tmp->name, "callerid")) {
03838             callerid = tmp->value;
03839          } else if (!strcasecmp(tmp->name, "origdate")) {
03840             origdate = tmp->value;
03841          } else if (!strcasecmp(tmp->name, "origtime")) {
03842             origtime = tmp->value;
03843          } else if (!strcasecmp(tmp->name, "category")) {
03844             category = tmp->value;
03845          } else if (!strcasecmp(tmp->name, "duration")) {
03846             duration = tmp->value;
03847          }
03848       }
03849       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);
03850    }
03851    copy(frompath2, topath2);
03852    ast_variables_destroy(var);
03853 }
03854 #endif
03855 
03856 /*! 
03857  * \brief Removes the voicemail sound and information file.
03858  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03859  *
03860  * This is used by the DELETE macro when voicemails are stored on the file system.
03861  *
03862  * \return zero on success, -1 on error.
03863  */
03864 static int vm_delete(char *file)
03865 {
03866    char *txt;
03867    int txtsize = 0;
03868 
03869    txtsize = (strlen(file) + 5)*sizeof(char);
03870    txt = alloca(txtsize);
03871    /* Sprintf here would safe because we alloca'd exactly the right length,
03872     * but trying to eliminate all sprintf's anyhow
03873     */
03874    if (ast_check_realtime("voicemail_data")) {
03875       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03876    }
03877    snprintf(txt, txtsize, "%s.txt", file);
03878    unlink(txt);
03879    return ast_filedelete(file, NULL);
03880 }
03881 
03882 /*!
03883  * \brief utility used by inchar(), for base_encode()
03884  */
03885 static int inbuf(struct baseio *bio, FILE *fi)
03886 {
03887    int l;
03888 
03889    if (bio->ateof)
03890       return 0;
03891 
03892    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03893       if (ferror(fi))
03894          return -1;
03895 
03896       bio->ateof = 1;
03897       return 0;
03898    }
03899 
03900    bio->iolen= l;
03901    bio->iocp= 0;
03902 
03903    return 1;
03904 }
03905 
03906 /*!
03907  * \brief utility used by base_encode()
03908  */
03909 static int inchar(struct baseio *bio, FILE *fi)
03910 {
03911    if (bio->iocp>=bio->iolen) {
03912       if (!inbuf(bio, fi))
03913          return EOF;
03914    }
03915 
03916    return bio->iobuf[bio->iocp++];
03917 }
03918 
03919 /*!
03920  * \brief utility used by base_encode()
03921  */
03922 static int ochar(struct baseio *bio, int c, FILE *so)
03923 {
03924    if (bio->linelength >= BASELINELEN) {
03925       if (fputs(ENDL, so) == EOF) {
03926          return -1;
03927       }
03928 
03929       bio->linelength= 0;
03930    }
03931 
03932    if (putc(((unsigned char) c), so) == EOF) {
03933       return -1;
03934    }
03935 
03936    bio->linelength++;
03937 
03938    return 1;
03939 }
03940 
03941 /*!
03942  * \brief Performs a base 64 encode algorithm on the contents of a File
03943  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03944  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03945  *
03946  * 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 ?
03947  *
03948  * \return zero on success, -1 on error.
03949  */
03950 static int base_encode(char *filename, FILE *so)
03951 {
03952    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03953       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03954       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03955       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03956    int i,hiteof= 0;
03957    FILE *fi;
03958    struct baseio bio;
03959 
03960    memset(&bio, 0, sizeof(bio));
03961    bio.iocp = BASEMAXINLINE;
03962 
03963    if (!(fi = fopen(filename, "rb"))) {
03964       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03965       return -1;
03966    }
03967 
03968    while (!hiteof){
03969       unsigned char igroup[3], ogroup[4];
03970       int c,n;
03971 
03972       igroup[0]= igroup[1]= igroup[2]= 0;
03973 
03974       for (n= 0;n<3;n++) {
03975          if ((c = inchar(&bio, fi)) == EOF) {
03976             hiteof= 1;
03977             break;
03978          }
03979 
03980          igroup[n]= (unsigned char)c;
03981       }
03982 
03983       if (n> 0) {
03984          ogroup[0]= dtable[igroup[0]>>2];
03985          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03986          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03987          ogroup[3]= dtable[igroup[2]&0x3F];
03988 
03989          if (n<3) {
03990             ogroup[3]= '=';
03991 
03992             if (n<2)
03993                ogroup[2]= '=';
03994          }
03995 
03996          for (i= 0;i<4;i++)
03997             ochar(&bio, ogroup[i], so);
03998       }
03999    }
04000 
04001    fclose(fi);
04002 
04003    if (fputs(ENDL, so) == EOF) {
04004       return 0;
04005    }
04006 
04007    return 1;
04008 }
04009 
04010 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)
04011 {
04012    char callerid[256];
04013    char fromdir[256], fromfile[256];
04014    struct ast_config *msg_cfg;
04015    const char *origcallerid, *origtime;
04016    char origcidname[80], origcidnum[80], origdate[80];
04017    int inttime;
04018    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04019 
04020    /* Prepare variables for substitution in email body and subject */
04021    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04022    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04023    snprintf(passdata, passdatasize, "%d", msgnum);
04024    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
04025    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04026    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04027    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04028       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04029    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04030    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04031    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04032    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04033    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04034 
04035    /* Retrieve info from VM attribute file */
04036    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04037    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04038    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04039       strcat(fromfile, ".txt");
04040    }
04041    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04042       if (option_debug > 0) {
04043          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04044       }
04045       return;
04046    }
04047 
04048    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04049       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04050       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04051       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04052       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04053    }
04054 
04055    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04056       struct timeval tv = { inttime, };
04057       struct ast_tm tm;
04058       ast_localtime(&tv, &tm, NULL);
04059       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04060       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04061    }
04062    ast_config_destroy(msg_cfg);
04063 }
04064 
04065 /*!
04066  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04067  * \param from The string to work with.
04068  * \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.
04069  * 
04070  * \return The destination string with quotes wrapped on it (the to field).
04071  */
04072 static char *quote(const char *from, char *to, size_t len)
04073 {
04074    char *ptr = to;
04075    *ptr++ = '"';
04076    for (; ptr < to + len - 1; from++) {
04077       if (*from == '"')
04078          *ptr++ = '\\';
04079       else if (*from == '\0')
04080          break;
04081       *ptr++ = *from;
04082    }
04083    if (ptr < to + len - 1)
04084       *ptr++ = '"';
04085    *ptr = '\0';
04086    return to;
04087 }
04088 
04089 /*! \brief
04090  * fill in *tm for current time according to the proper timezone, if any.
04091  * Return tm so it can be used as a function argument.
04092  */
04093 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04094 {
04095    const struct vm_zone *z = NULL;
04096    struct timeval t = ast_tvnow();
04097 
04098    /* Does this user have a timezone specified? */
04099    if (!ast_strlen_zero(vmu->zonetag)) {
04100       /* Find the zone in the list */
04101       AST_LIST_LOCK(&zones);
04102       AST_LIST_TRAVERSE(&zones, z, list) {
04103          if (!strcmp(z->name, vmu->zonetag))
04104             break;
04105       }
04106       AST_LIST_UNLOCK(&zones);
04107    }
04108    ast_localtime(&t, tm, z ? z->timezone : NULL);
04109    return tm;
04110 }
04111 
04112 /*!\brief Check if the string would need encoding within the MIME standard, to
04113  * avoid confusing certain mail software that expects messages to be 7-bit
04114  * clean.
04115  */
04116 static int check_mime(const char *str)
04117 {
04118    for (; *str; str++) {
04119       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04120          return 1;
04121       }
04122    }
04123    return 0;
04124 }
04125 
04126 /*!\brief Encode a string according to the MIME rules for encoding strings
04127  * that are not 7-bit clean or contain control characters.
04128  *
04129  * Additionally, if the encoded string would exceed the MIME limit of 76
04130  * characters per line, then the encoding will be broken up into multiple
04131  * sections, separated by a space character, in order to facilitate
04132  * breaking up the associated header across multiple lines.
04133  *
04134  * \param start A string to be encoded
04135  * \param end An expandable buffer for holding the result
04136  * \param preamble The length of the first line already used for this string,
04137  * to ensure that each line maintains a maximum length of 76 chars.
04138  * \param postamble the length of any additional characters appended to the
04139  * line, used to ensure proper field wrapping.
04140  * \retval The encoded string.
04141  */
04142 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
04143 {
04144    char tmp[80];
04145    int first_section = 1;
04146    size_t endlen = 0, tmplen = 0;
04147    *end = '\0';
04148 
04149    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04150    for (; *start; start++) {
04151       int need_encoding = 0;
04152       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04153          need_encoding = 1;
04154       }
04155       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04156          (first_section && !need_encoding && preamble + tmplen > 72) ||
04157          (!first_section && need_encoding && tmplen > 70) ||
04158          (!first_section && !need_encoding && tmplen > 72)) {
04159          /* Start new line */
04160          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04161          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04162          first_section = 0;
04163       }
04164       if (need_encoding && *start == ' ') {
04165          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04166       } else if (need_encoding) {
04167          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04168       } else {
04169          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04170       }
04171    }
04172    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04173    return end;
04174 }
04175 
04176 /*!
04177  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04178  * \param p The output file to generate the email contents into.
04179  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04180  * \param vmu The voicemail user who is sending the voicemail.
04181  * \param msgnum The message index in the mailbox folder.
04182  * \param context 
04183  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04184  * \param cidnum The caller ID number.
04185  * \param cidname The caller ID name.
04186  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04187  * \param format The message sound file format. i.e. .wav
04188  * \param duration The time of the message content, in seconds.
04189  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04190  * \param chan
04191  * \param category
04192  * \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.
04193  *
04194  * 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.
04195  */
04196 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)
04197 {
04198    char date[256];
04199    char host[MAXHOSTNAMELEN] = "";
04200    char who[256];
04201    char bound[256];
04202    char dur[256];
04203    struct ast_tm tm;
04204    char enc_cidnum[256] = "", enc_cidname[256] = "";
04205    char *passdata = NULL, *passdata2;
04206    size_t len_passdata = 0, len_passdata2, tmplen;
04207    char *greeting_attachment; 
04208    char filename[256];
04209 
04210 
04211    /* One alloca for multiple fields */
04212    len_passdata2 = strlen(vmu->fullname);
04213    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04214       len_passdata2 = tmplen;
04215    }
04216    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04217       len_passdata2 = tmplen;
04218    }
04219    len_passdata2 = len_passdata2 * 3 + 200;
04220    passdata2 = alloca(len_passdata2);
04221 
04222    if (cidnum) {
04223       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04224    }
04225    if (cidname) {
04226       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04227    }
04228    gethostname(host, sizeof(host) - 1);
04229 
04230    if (strchr(srcemail, '@'))
04231       ast_copy_string(who, srcemail, sizeof(who));
04232    else 
04233       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04234    
04235    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04236    if (greeting_attachment)
04237       *greeting_attachment++ = '\0';
04238 
04239    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04240    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04241    fprintf(p, "Date: %s" ENDL, date);
04242 
04243    /* Set date format for voicemail mail */
04244    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04245 
04246    if (!ast_strlen_zero(fromstring)) {
04247       struct ast_channel *ast;
04248       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04249          char *ptr;
04250          memset(passdata2, 0, len_passdata2);
04251          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04252          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04253          len_passdata = strlen(passdata2) * 3 + 300;
04254          passdata = alloca(len_passdata);
04255          if (check_mime(passdata2)) {
04256             int first_line = 1;
04257             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04258             while ((ptr = strchr(passdata, ' '))) {
04259                *ptr = '\0';
04260                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04261                first_line = 0;
04262                passdata = ptr + 1;
04263             }
04264             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04265          } else {
04266             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04267          }
04268          ast_channel_free(ast);
04269       } else {
04270          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04271       }
04272    } else {
04273       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04274    }
04275 
04276    if (check_mime(vmu->fullname)) {
04277       int first_line = 1;
04278       char *ptr;
04279       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04280       while ((ptr = strchr(passdata2, ' '))) {
04281          *ptr = '\0';
04282          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04283          first_line = 0;
04284          passdata2 = ptr + 1;
04285       }
04286       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04287    } else {
04288       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04289    }
04290    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04291       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04292       struct ast_channel *ast;
04293       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04294          int vmlen = strlen(e_subj) * 3 + 200;
04295          /* Only allocate more space if the previous was not large enough */
04296          if (vmlen > len_passdata) {
04297             passdata = alloca(vmlen);
04298             len_passdata = vmlen;
04299          }
04300 
04301          memset(passdata, 0, len_passdata);
04302          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04303          pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
04304          if (check_mime(passdata)) {
04305             int first_line = 1;
04306             char *ptr;
04307             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04308             while ((ptr = strchr(passdata2, ' '))) {
04309                *ptr = '\0';
04310                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04311                first_line = 0;
04312                passdata2 = ptr + 1;
04313             }
04314             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04315          } else {
04316             fprintf(p, "Subject: %s" ENDL, passdata);
04317          }
04318          ast_channel_free(ast);
04319       } else {
04320          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04321       }
04322    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04323       if (ast_strlen_zero(flag)) {
04324          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04325       } else {
04326          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04327       }
04328    } else {
04329       if (ast_strlen_zero(flag)) {
04330          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04331       } else {
04332          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04333       }
04334    }
04335 
04336    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04337    if (imap) {
04338       /* additional information needed for IMAP searching */
04339       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04340       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04341       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04342       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04343 #ifdef IMAP_STORAGE
04344       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04345 #else
04346       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04347 #endif
04348       /* flag added for Urgent */
04349       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04350       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04351       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04352       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04353       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04354       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04355       if (!ast_strlen_zero(category)) {
04356          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04357       } else {
04358          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04359       }
04360       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04361       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04362       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04363    }
04364    if (!ast_strlen_zero(cidnum)) {
04365       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04366    }
04367    if (!ast_strlen_zero(cidname)) {
04368       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04369    }
04370    fprintf(p, "MIME-Version: 1.0" ENDL);
04371    if (attach_user_voicemail) {
04372       /* Something unique. */
04373       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04374 
04375       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04376       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04377       fprintf(p, "--%s" ENDL, bound);
04378    }
04379    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04380    if (emailbody || vmu->emailbody) {
04381       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04382       struct ast_channel *ast;
04383       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04384          char *passdata;
04385          int vmlen = strlen(e_body) * 3 + 200;
04386          passdata = alloca(vmlen);
04387          memset(passdata, 0, vmlen);
04388          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04389          pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
04390 #ifdef IMAP_STORAGE
04391          {
04392             /* Convert body to native line terminators for IMAP backend */
04393             char *line = passdata, *next;
04394             do {
04395                /* Terminate line before outputting it to the file */
04396                if ((next = strchr(line, '\n'))) {
04397                   *next++ = '\0';
04398                }
04399                fprintf(p, "%s" ENDL, line);
04400                line = next;
04401             } while (!ast_strlen_zero(line));
04402          }
04403 #else
04404          fprintf(p, "%s" ENDL, passdata);
04405 #endif
04406          ast_channel_free(ast);
04407       } else
04408          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04409    } else if (msgnum > -1) {
04410       if (strcmp(vmu->mailbox, mailbox)) {
04411          /* Forwarded type */
04412          struct ast_config *msg_cfg;
04413          const char *v;
04414          int inttime;
04415          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04416          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04417          /* Retrieve info from VM attribute file */
04418          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04419          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04420          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04421             strcat(fromfile, ".txt");
04422          }
04423          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04424             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04425                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04426             }
04427 
04428             /* You might be tempted to do origdate, except that a) it's in the wrong
04429              * format, and b) it's missing for IMAP recordings. */
04430             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04431                struct timeval tv = { inttime, };
04432                struct ast_tm tm;
04433                ast_localtime(&tv, &tm, NULL);
04434                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04435             }
04436             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04437                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04438                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04439                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04440                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04441                date, origcallerid, origdate);
04442             ast_config_destroy(msg_cfg);
04443          } else {
04444             goto plain_message;
04445          }
04446       } else {
04447 plain_message:
04448          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04449             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04450             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04451             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04452             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04453       }
04454    } else {
04455       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04456             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04457    }
04458 
04459    if (imap || attach_user_voicemail) {
04460       if (!ast_strlen_zero(attach2)) {
04461          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04462          ast_debug(5, "creating second attachment filename %s\n", filename);
04463          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04464          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04465          ast_debug(5, "creating attachment filename %s\n", filename);
04466          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04467       } else {
04468          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04469          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04470          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04471       }
04472    }
04473 }
04474 
04475 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)
04476 {
04477    char tmpdir[256], newtmp[256];
04478    char fname[256];
04479    char tmpcmd[256];
04480    int tmpfd = -1;
04481 
04482    /* Eww. We want formats to tell us their own MIME type */
04483    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04484 
04485    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04486       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04487       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04488       tmpfd = mkstemp(newtmp);
04489       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04490       ast_debug(3, "newtmp: %s\n", newtmp);
04491       if (tmpfd > -1) {
04492          int soxstatus;
04493          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04494          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04495             attach = newtmp;
04496             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04497          } else {
04498             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04499                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04500             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04501          }
04502       }
04503    }
04504    fprintf(p, "--%s" ENDL, bound);
04505    if (msgnum > -1)
04506       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04507    else
04508       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04509    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04510    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04511    if (msgnum > -1)
04512       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04513    else
04514       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04515    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04516    base_encode(fname, p);
04517    if (last)
04518       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04519    if (tmpfd > -1) {
04520       unlink(fname);
04521       close(tmpfd);
04522       unlink(newtmp);
04523    }
04524    return 0;
04525 }
04526 #undef ENDL
04527 
04528 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)
04529 {
04530    FILE *p=NULL;
04531    char tmp[80] = "/tmp/astmail-XXXXXX";
04532    char tmp2[256];
04533 
04534    if (vmu && ast_strlen_zero(vmu->email)) {
04535       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04536       return(0);
04537    }
04538    if (!strcmp(format, "wav49"))
04539       format = "WAV";
04540    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));
04541    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04542       command hangs */
04543    if ((p = vm_mkftemp(tmp)) == NULL) {
04544       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04545       return -1;
04546    } else {
04547       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04548       fclose(p);
04549       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04550       ast_safe_system(tmp2);
04551       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04552    }
04553    return 0;
04554 }
04555 
04556 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)
04557 {
04558    char date[256];
04559    char host[MAXHOSTNAMELEN] = "";
04560    char who[256];
04561    char dur[PATH_MAX];
04562    char tmp[80] = "/tmp/astmail-XXXXXX";
04563    char tmp2[PATH_MAX];
04564    struct ast_tm tm;
04565    FILE *p;
04566 
04567    if ((p = vm_mkftemp(tmp)) == NULL) {
04568       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04569       return -1;
04570    }
04571    gethostname(host, sizeof(host)-1);
04572    if (strchr(srcemail, '@'))
04573       ast_copy_string(who, srcemail, sizeof(who));
04574    else 
04575       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04576    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04577    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04578    fprintf(p, "Date: %s\n", date);
04579 
04580    if (*pagerfromstring) {
04581       struct ast_channel *ast;
04582       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04583          char *passdata;
04584          int vmlen = strlen(fromstring)*3 + 200;
04585          passdata = alloca(vmlen);
04586          memset(passdata, 0, vmlen);
04587          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04588          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04589          fprintf(p, "From: %s <%s>\n", passdata, who);
04590          ast_channel_free(ast);
04591       } else 
04592          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04593    } else
04594       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04595    fprintf(p, "To: %s\n", pager);
04596    if (pagersubject) {
04597       struct ast_channel *ast;
04598       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04599          char *passdata;
04600          int vmlen = strlen(pagersubject) * 3 + 200;
04601          passdata = alloca(vmlen);
04602          memset(passdata, 0, vmlen);
04603          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04604          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04605          fprintf(p, "Subject: %s\n\n", passdata);
04606          ast_channel_free(ast);
04607       } else
04608          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04609    } else {
04610       if (ast_strlen_zero(flag)) {
04611          fprintf(p, "Subject: New VM\n\n");
04612       } else {
04613          fprintf(p, "Subject: New %s VM\n\n", flag);
04614       }
04615    }
04616 
04617    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04618    if (pagerbody) {
04619       struct ast_channel *ast;
04620       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04621          char *passdata;
04622          int vmlen = strlen(pagerbody) * 3 + 200;
04623          passdata = alloca(vmlen);
04624          memset(passdata, 0, vmlen);
04625          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04626          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04627          fprintf(p, "%s\n", passdata);
04628          ast_channel_free(ast);
04629       } else
04630          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04631    } else {
04632       fprintf(p, "New %s long %s msg in box %s\n"
04633             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04634    }
04635    fclose(p);
04636    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04637    ast_safe_system(tmp2);
04638    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04639    return 0;
04640 }
04641 
04642 /*!
04643  * \brief Gets the current date and time, as formatted string.
04644  * \param s The buffer to hold the output formatted date.
04645  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04646  * 
04647  * The date format string used is "%a %b %e %r UTC %Y".
04648  * 
04649  * \return zero on success, -1 on error.
04650  */
04651 static int get_date(char *s, int len)
04652 {
04653    struct ast_tm tm;
04654    struct timeval t = ast_tvnow();
04655    
04656    ast_localtime(&t, &tm, "UTC");
04657 
04658    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04659 }
04660 
04661 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04662 {
04663    int res;
04664    char fn[PATH_MAX];
04665    char dest[PATH_MAX];
04666 
04667    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04668 
04669    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04670       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04671       return -1;
04672    }
04673 
04674    RETRIEVE(fn, -1, ext, context);
04675    if (ast_fileexists(fn, NULL, NULL) > 0) {
04676       res = ast_stream_and_wait(chan, fn, ecodes);
04677       if (res) {
04678          DISPOSE(fn, -1);
04679          return res;
04680       }
04681    } else {
04682       /* Dispose just in case */
04683       DISPOSE(fn, -1);
04684       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04685       if (res)
04686          return res;
04687       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04688       if (res)
04689          return res;
04690    }
04691    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04692    return res;
04693 }
04694 
04695 static void free_zone(struct vm_zone *z)
04696 {
04697    ast_free(z);
04698 }
04699 
04700 #ifdef ODBC_STORAGE
04701 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04702 {
04703    int x = -1;
04704    int res;
04705    SQLHSTMT stmt = NULL;
04706    char sql[PATH_MAX];
04707    char rowdata[20];
04708    char tmp[PATH_MAX] = "";
04709    struct odbc_obj *obj = NULL;
04710    char *context;
04711    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04712 
04713    if (newmsgs)
04714       *newmsgs = 0;
04715    if (oldmsgs)
04716       *oldmsgs = 0;
04717    if (urgentmsgs)
04718       *urgentmsgs = 0;
04719 
04720    /* If no mailbox, return immediately */
04721    if (ast_strlen_zero(mailbox))
04722       return 0;
04723 
04724    ast_copy_string(tmp, mailbox, sizeof(tmp));
04725 
04726    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04727       int u, n, o;
04728       char *next, *remaining = tmp;
04729       while ((next = strsep(&remaining, " ,"))) {
04730          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04731             return -1;
04732          }
04733          if (urgentmsgs) {
04734             *urgentmsgs += u;
04735          }
04736          if (newmsgs) {
04737             *newmsgs += n;
04738          }
04739          if (oldmsgs) {
04740             *oldmsgs += o;
04741          }
04742       }
04743       return 0;
04744    }
04745 
04746    context = strchr(tmp, '@');
04747    if (context) {
04748       *context = '\0';
04749       context++;
04750    } else
04751       context = "default";
04752 
04753    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04754       do {
04755          if (newmsgs) {
04756             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04757             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04758                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04759                break;
04760             }
04761             res = SQLFetch(stmt);
04762             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04763                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04764                break;
04765             }
04766             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04767             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04768                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04769                break;
04770             }
04771             *newmsgs = atoi(rowdata);
04772             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04773          }
04774 
04775          if (oldmsgs) {
04776             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04777             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04778                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04779                break;
04780             }
04781             res = SQLFetch(stmt);
04782             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04783                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04784                break;
04785             }
04786             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04787             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04788                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04789                break;
04790             }
04791             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04792             *oldmsgs = atoi(rowdata);
04793          }
04794 
04795          if (urgentmsgs) {
04796             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04797             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04798                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04799                break;
04800             }
04801             res = SQLFetch(stmt);
04802             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04803                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04804                break;
04805             }
04806             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04807             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04808                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04809                break;
04810             }
04811             *urgentmsgs = atoi(rowdata);
04812          }
04813 
04814          x = 0;
04815       } while (0);
04816    } else {
04817       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04818    }
04819 
04820    if (stmt) {
04821       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04822    }
04823    if (obj) {
04824       ast_odbc_release_obj(obj);
04825    }
04826 
04827    return x;
04828 }
04829 
04830 /*!
04831  * \brief Gets the number of messages that exist in a mailbox folder.
04832  * \param context
04833  * \param mailbox
04834  * \param folder
04835  * 
04836  * This method is used when ODBC backend is used.
04837  * \return The number of messages in this mailbox folder (zero or more).
04838  */
04839 static int messagecount(const char *context, const char *mailbox, const char *folder)
04840 {
04841    struct odbc_obj *obj = NULL;
04842    int nummsgs = 0;
04843    int res;
04844    SQLHSTMT stmt = NULL;
04845    char sql[PATH_MAX];
04846    char rowdata[20];
04847    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04848    if (!folder)
04849       folder = "INBOX";
04850    /* If no mailbox, return immediately */
04851    if (ast_strlen_zero(mailbox))
04852       return 0;
04853 
04854    obj = ast_odbc_request_obj(odbc_database, 0);
04855    if (obj) {
04856       if (!strcmp(folder, "INBOX")) {
04857          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);
04858       } else {
04859          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04860       }
04861       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04862       if (!stmt) {
04863          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04864          goto yuck;
04865       }
04866       res = SQLFetch(stmt);
04867       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04868          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04869          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04870          goto yuck;
04871       }
04872       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04873       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04874          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04875          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04876          goto yuck;
04877       }
04878       nummsgs = atoi(rowdata);
04879       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04880    } else
04881       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04882 
04883 yuck:
04884    if (obj)
04885       ast_odbc_release_obj(obj);
04886    return nummsgs;
04887 }
04888 
04889 /** 
04890  * \brief Determines if the given folder has messages.
04891  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04892  * 
04893  * This function is used when the mailbox is stored in an ODBC back end.
04894  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04895  * \return 1 if the folder has one or more messages. zero otherwise.
04896  */
04897 static int has_voicemail(const char *mailbox, const char *folder)
04898 {
04899    char tmp[256], *tmp2 = tmp, *box, *context;
04900    ast_copy_string(tmp, mailbox, sizeof(tmp));
04901    while ((context = box = strsep(&tmp2, ",&"))) {
04902       strsep(&context, "@");
04903       if (ast_strlen_zero(context))
04904          context = "default";
04905       if (messagecount(context, box, folder))
04906          return 1;
04907    }
04908    return 0;
04909 }
04910 #endif
04911 #ifndef IMAP_STORAGE
04912 /*! 
04913  * \brief Copies a message from one mailbox to another.
04914  * \param chan
04915  * \param vmu
04916  * \param imbox
04917  * \param msgnum
04918  * \param duration
04919  * \param recip
04920  * \param fmt
04921  * \param dir
04922  *
04923  * This is only used by file storage based mailboxes.
04924  *
04925  * \return zero on success, -1 on error.
04926  */
04927 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)
04928 {
04929    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04930    const char *frombox = mbox(imbox);
04931    int recipmsgnum;
04932    int res = 0;
04933 
04934    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04935 
04936    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04937       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04938    } else {
04939       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04940    }
04941    
04942    if (!dir)
04943       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04944    else
04945       ast_copy_string(fromdir, dir, sizeof(fromdir));
04946 
04947    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04948    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04949 
04950    if (vm_lock_path(todir))
04951       return ERROR_LOCK_PATH;
04952 
04953    recipmsgnum = last_message_index(recip, todir) + 1;
04954    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04955       make_file(topath, sizeof(topath), todir, recipmsgnum);
04956       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04957          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04958       } else {
04959          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04960           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04961           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04962           * much worse problem happening and IMAP storage doesn't call this function
04963           */
04964          copy_plain_file(frompath, topath);
04965          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04966          vm_delete(topath);
04967       }
04968    } else {
04969       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04970       res = -1;
04971    }
04972    ast_unlock_path(todir);
04973    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04974    
04975    return res;
04976 }
04977 #endif
04978 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04979 
04980 static int messagecount(const char *context, const char *mailbox, const char *folder)
04981 {
04982    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
04983 }
04984 
04985 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04986 {
04987    DIR *dir;
04988    struct dirent *de;
04989    char fn[256];
04990    int ret = 0;
04991 
04992    /* If no mailbox, return immediately */
04993    if (ast_strlen_zero(mailbox))
04994       return 0;
04995 
04996    if (ast_strlen_zero(folder))
04997       folder = "INBOX";
04998    if (ast_strlen_zero(context))
04999       context = "default";
05000 
05001    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05002 
05003    if (!(dir = opendir(fn)))
05004       return 0;
05005 
05006    while ((de = readdir(dir))) {
05007       if (!strncasecmp(de->d_name, "msg", 3)) {
05008          if (shortcircuit) {
05009             ret = 1;
05010             break;
05011          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05012             ret++;
05013          }
05014       }
05015    }
05016 
05017    closedir(dir);
05018 
05019    return ret;
05020 }
05021 
05022 /** 
05023  * \brief Determines if the given folder has messages.
05024  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05025  * \param folder the folder to look in
05026  *
05027  * This function is used when the mailbox is stored in a filesystem back end.
05028  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05029  * \return 1 if the folder has one or more messages. zero otherwise.
05030  */
05031 static int has_voicemail(const char *mailbox, const char *folder)
05032 {
05033    char tmp[256], *tmp2 = tmp, *box, *context;
05034    ast_copy_string(tmp, mailbox, sizeof(tmp));
05035    if (ast_strlen_zero(folder)) {
05036       folder = "INBOX";
05037    }
05038    while ((box = strsep(&tmp2, ",&"))) {
05039       if ((context = strchr(box, '@')))
05040          *context++ = '\0';
05041       else
05042          context = "default";
05043       if (__has_voicemail(context, box, folder, 1))
05044          return 1;
05045       /* If we are checking INBOX, we should check Urgent as well */
05046       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05047          return 1;
05048       }
05049    }
05050    return 0;
05051 }
05052 
05053 
05054 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05055 {
05056    char tmp[256];
05057    char *context;
05058 
05059    /* If no mailbox, return immediately */
05060    if (ast_strlen_zero(mailbox))
05061       return 0;
05062 
05063    if (newmsgs)
05064       *newmsgs = 0;
05065    if (oldmsgs)
05066       *oldmsgs = 0;
05067    if (urgentmsgs)
05068       *urgentmsgs = 0;
05069 
05070    if (strchr(mailbox, ',')) {
05071       int tmpnew, tmpold, tmpurgent;
05072       char *mb, *cur;
05073 
05074       ast_copy_string(tmp, mailbox, sizeof(tmp));
05075       mb = tmp;
05076       while ((cur = strsep(&mb, ", "))) {
05077          if (!ast_strlen_zero(cur)) {
05078             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05079                return -1;
05080             else {
05081                if (newmsgs)
05082                   *newmsgs += tmpnew; 
05083                if (oldmsgs)
05084                   *oldmsgs += tmpold;
05085                if (urgentmsgs)
05086                   *urgentmsgs += tmpurgent;
05087             }
05088          }
05089       }
05090       return 0;
05091    }
05092 
05093    ast_copy_string(tmp, mailbox, sizeof(tmp));
05094    
05095    if ((context = strchr(tmp, '@')))
05096       *context++ = '\0';
05097    else
05098       context = "default";
05099 
05100    if (newmsgs)
05101       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05102    if (oldmsgs)
05103       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05104    if (urgentmsgs)
05105       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05106 
05107    return 0;
05108 }
05109 
05110 #endif
05111 
05112 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05113 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05114 {
05115    int urgentmsgs = 0;
05116    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05117    if (newmsgs) {
05118       *newmsgs += urgentmsgs;
05119    }
05120    return res;
05121 }
05122 
05123 static void run_externnotify(char *context, char *extension, const char *flag)
05124 {
05125    char arguments[255];
05126    char ext_context[256] = "";
05127    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05128    struct ast_smdi_mwi_message *mwi_msg;
05129 
05130    if (!ast_strlen_zero(context))
05131       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05132    else
05133       ast_copy_string(ext_context, extension, sizeof(ext_context));
05134 
05135    if (smdi_iface) {
05136       if (ast_app_has_voicemail(ext_context, NULL)) 
05137          ast_smdi_mwi_set(smdi_iface, extension);
05138       else
05139          ast_smdi_mwi_unset(smdi_iface, extension);
05140 
05141       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05142          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05143          if (!strncmp(mwi_msg->cause, "INV", 3))
05144             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05145          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05146             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05147          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05148          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05149       } else {
05150          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05151       }
05152    }
05153 
05154    if (!ast_strlen_zero(externnotify)) {
05155       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05156          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05157       } else {
05158          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05159          ast_debug(1, "Executing %s\n", arguments);
05160          ast_safe_system(arguments);
05161       }
05162    }
05163 }
05164 
05165 /*!
05166  * \brief Variables used for saving a voicemail.
05167  *
05168  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05169  */
05170 struct leave_vm_options {
05171    unsigned int flags;
05172    signed char record_gain;
05173    char *exitcontext;
05174 };
05175 
05176 /*!
05177  * \brief Prompts the user and records a voicemail to a mailbox.
05178  * \param chan
05179  * \param ext
05180  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05181  * 
05182  * 
05183  * 
05184  * \return zero on success, -1 on error.
05185  */
05186 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05187 {
05188 #ifdef IMAP_STORAGE
05189    int newmsgs, oldmsgs;
05190 #else
05191    char urgdir[PATH_MAX];
05192 #endif
05193    char txtfile[PATH_MAX];
05194    char tmptxtfile[PATH_MAX];
05195    struct vm_state *vms = NULL;
05196    char callerid[256];
05197    FILE *txt;
05198    char date[256];
05199    int txtdes;
05200    int res = 0;
05201    int msgnum;
05202    int duration = 0;
05203    int ausemacro = 0;
05204    int ousemacro = 0;
05205    int ouseexten = 0;
05206    char tmpdur[16];
05207    char priority[16];
05208    char origtime[16];
05209    char dir[PATH_MAX];
05210    char tmpdir[PATH_MAX];
05211    char fn[PATH_MAX];
05212    char prefile[PATH_MAX] = "";
05213    char tempfile[PATH_MAX] = "";
05214    char ext_context[256] = "";
05215    char fmt[80];
05216    char *context;
05217    char ecodes[17] = "#";
05218    struct ast_str *tmp = ast_str_create(16);
05219    char *tmpptr;
05220    struct ast_vm_user *vmu;
05221    struct ast_vm_user svm;
05222    const char *category = NULL;
05223    const char *code;
05224    const char *alldtmf = "0123456789ABCD*#";
05225    char flag[80];
05226 
05227    ast_str_set(&tmp, 0, "%s", ext);
05228    ext = ast_str_buffer(tmp);
05229    if ((context = strchr(ext, '@'))) {
05230       *context++ = '\0';
05231       tmpptr = strchr(context, '&');
05232    } else {
05233       tmpptr = strchr(ext, '&');
05234    }
05235 
05236    if (tmpptr)
05237       *tmpptr++ = '\0';
05238 
05239    ast_channel_lock(chan);
05240    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05241       category = ast_strdupa(category);
05242    }
05243    ast_channel_unlock(chan);
05244 
05245    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05246       ast_copy_string(flag, "Urgent", sizeof(flag));
05247    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05248       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05249    } else {
05250       flag[0] = '\0';
05251    }
05252 
05253    ast_debug(3, "Before find_user\n");
05254    if (!(vmu = find_user(&svm, context, ext))) {
05255       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05256       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05257       ast_free(tmp);
05258       return res;
05259    }
05260    /* Setup pre-file if appropriate */
05261    if (strcmp(vmu->context, "default"))
05262       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05263    else
05264       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05265 
05266    /* Set the path to the prefile. Will be one of 
05267       VM_SPOOL_DIRcontext/ext/busy
05268       VM_SPOOL_DIRcontext/ext/unavail
05269       Depending on the flag set in options.
05270    */
05271    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05272       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05273    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05274       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05275    }
05276    /* Set the path to the tmpfile as
05277       VM_SPOOL_DIR/context/ext/temp
05278       and attempt to create the folder structure.
05279    */
05280    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05281    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05282       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05283       ast_free(tmp);
05284       return -1;
05285    }
05286    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05287    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05288       ast_copy_string(prefile, tempfile, sizeof(prefile));
05289 
05290    DISPOSE(tempfile, -1);
05291    /* It's easier just to try to make it than to check for its existence */
05292    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05293 
05294    /* Check current or macro-calling context for special extensions */
05295    if (ast_test_flag(vmu, VM_OPERATOR)) {
05296       if (!ast_strlen_zero(vmu->exit)) {
05297          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05298             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05299             ouseexten = 1;
05300          }
05301       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05302          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05303          ouseexten = 1;
05304       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05305          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05306          ousemacro = 1;
05307       }
05308    }
05309 
05310    if (!ast_strlen_zero(vmu->exit)) {
05311       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05312          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05313    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05314       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05315    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05316       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05317       ausemacro = 1;
05318    }
05319 
05320    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05321       for (code = alldtmf; *code; code++) {
05322          char e[2] = "";
05323          e[0] = *code;
05324          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05325             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05326       }
05327    }
05328 
05329    /* Play the beginning intro if desired */
05330    if (!ast_strlen_zero(prefile)) {
05331 #ifdef ODBC_STORAGE
05332       int success = 
05333 #endif
05334          RETRIEVE(prefile, -1, ext, context);
05335       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05336          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05337             res = ast_waitstream(chan, ecodes);
05338 #ifdef ODBC_STORAGE
05339          if (success == -1) {
05340             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05341             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05342             store_file(prefile, vmu->mailbox, vmu->context, -1);
05343          }
05344 #endif
05345       } else {
05346          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05347          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05348       }
05349       DISPOSE(prefile, -1);
05350       if (res < 0) {
05351          ast_debug(1, "Hang up during prefile playback\n");
05352          free_user(vmu);
05353          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05354          ast_free(tmp);
05355          return -1;
05356       }
05357    }
05358    if (res == '#') {
05359       /* On a '#' we skip the instructions */
05360       ast_set_flag(options, OPT_SILENT);
05361       res = 0;
05362    }
05363    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05364       res = ast_stream_and_wait(chan, INTRO, ecodes);
05365       if (res == '#') {
05366          ast_set_flag(options, OPT_SILENT);
05367          res = 0;
05368       }
05369    }
05370    if (res > 0)
05371       ast_stopstream(chan);
05372    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05373     other than the operator -- an automated attendant or mailbox login for example */
05374    if (res == '*') {
05375       chan->exten[0] = 'a';
05376       chan->exten[1] = '\0';
05377       if (!ast_strlen_zero(vmu->exit)) {
05378          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05379       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05380          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05381       }
05382       chan->priority = 0;
05383       free_user(vmu);
05384       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05385       ast_free(tmp);
05386       return 0;
05387    }
05388 
05389    /* Check for a '0' here */
05390    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05391    transfer:
05392       if (ouseexten || ousemacro) {
05393          chan->exten[0] = 'o';
05394          chan->exten[1] = '\0';
05395          if (!ast_strlen_zero(vmu->exit)) {
05396             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05397          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05398             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05399          }
05400          ast_play_and_wait(chan, "transfer");
05401          chan->priority = 0;
05402          free_user(vmu);
05403          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05404       }
05405       ast_free(tmp);
05406       return OPERATOR_EXIT;
05407    }
05408 
05409    /* Allow all other digits to exit Voicemail and return to the dialplan */
05410    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05411       if (!ast_strlen_zero(options->exitcontext))
05412          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05413       free_user(vmu);
05414       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05415       ast_free(tmp);
05416       return res;
05417    }
05418 
05419    if (res < 0) {
05420       free_user(vmu);
05421       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05422       ast_free(tmp);
05423       return -1;
05424    }
05425    /* The meat of recording the message...  All the announcements and beeps have been played*/
05426    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05427    if (!ast_strlen_zero(fmt)) {
05428       msgnum = 0;
05429 
05430 #ifdef IMAP_STORAGE
05431       /* Is ext a mailbox? */
05432       /* must open stream for this user to get info! */
05433       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05434       if (res < 0) {
05435          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05436          ast_free(tmp);
05437          return -1;
05438       }
05439       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05440       /* It is possible under certain circumstances that inboxcount did not
05441        * create a vm_state when it was needed. This is a catchall which will
05442        * rarely be used.
05443        */
05444          if (!(vms = create_vm_state_from_user(vmu))) {
05445             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05446             ast_free(tmp);
05447             return -1;
05448          }
05449       }
05450       vms->newmessages++;
05451       
05452       /* here is a big difference! We add one to it later */
05453       msgnum = newmsgs + oldmsgs;
05454       ast_debug(3, "Messagecount set to %d\n",msgnum);
05455       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05456       /* set variable for compatibility */
05457       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05458 
05459       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05460          goto leave_vm_out;
05461       }
05462 #else
05463       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05464          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05465          if (!res)
05466             res = ast_waitstream(chan, "");
05467          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05468          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05469          inprocess_count(vmu->mailbox, vmu->context, -1);
05470          goto leave_vm_out;
05471       }
05472 
05473 #endif
05474       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05475       txtdes = mkstemp(tmptxtfile);
05476       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05477       if (txtdes < 0) {
05478          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05479          if (!res)
05480             res = ast_waitstream(chan, "");
05481          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05482          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05483          inprocess_count(vmu->mailbox, vmu->context, -1);
05484          goto leave_vm_out;
05485       }
05486 
05487       /* Now play the beep once we have the message number for our next message. */
05488       if (res >= 0) {
05489          /* Unless we're *really* silent, try to send the beep */
05490          res = ast_stream_and_wait(chan, "beep", "");
05491       }
05492             
05493       /* Store information in real-time storage */
05494       if (ast_check_realtime("voicemail_data")) {
05495          snprintf(priority, sizeof(priority), "%d", chan->priority);
05496          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05497          get_date(date, sizeof(date));
05498          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);
05499       }
05500 
05501       /* Store information */
05502       txt = fdopen(txtdes, "w+");
05503       if (txt) {
05504          get_date(date, sizeof(date));
05505          fprintf(txt, 
05506             ";\n"
05507             "; Message Information file\n"
05508             ";\n"
05509             "[message]\n"
05510             "origmailbox=%s\n"
05511             "context=%s\n"
05512             "macrocontext=%s\n"
05513             "exten=%s\n"
05514             "priority=%d\n"
05515             "callerchan=%s\n"
05516             "callerid=%s\n"
05517             "origdate=%s\n"
05518             "origtime=%ld\n"
05519             "category=%s\n",
05520             ext,
05521             chan->context,
05522             chan->macrocontext, 
05523             chan->exten,
05524             chan->priority,
05525             chan->name,
05526             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05527             date, (long)time(NULL),
05528             category ? category : "");
05529       } else {
05530          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05531          inprocess_count(vmu->mailbox, vmu->context, -1);
05532          if (ast_check_realtime("voicemail_data")) {
05533             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05534          }
05535          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05536          goto leave_vm_out;
05537       }
05538       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05539 
05540       if (txt) {
05541          fprintf(txt, "flag=%s\n", flag);
05542          if (duration < vmminsecs) {
05543             fclose(txt);
05544             if (option_verbose > 2) 
05545                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05546             ast_filedelete(tmptxtfile, NULL);
05547             unlink(tmptxtfile);
05548             if (ast_check_realtime("voicemail_data")) {
05549                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05550             }
05551             inprocess_count(vmu->mailbox, vmu->context, -1);
05552          } else {
05553             fprintf(txt, "duration=%d\n", duration);
05554             fclose(txt);
05555             if (vm_lock_path(dir)) {
05556                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05557                /* Delete files */
05558                ast_filedelete(tmptxtfile, NULL);
05559                unlink(tmptxtfile);
05560                inprocess_count(vmu->mailbox, vmu->context, -1);
05561             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05562                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05563                unlink(tmptxtfile);
05564                ast_unlock_path(dir);
05565                inprocess_count(vmu->mailbox, vmu->context, -1);
05566                if (ast_check_realtime("voicemail_data")) {
05567                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05568                }
05569             } else {
05570 #ifndef IMAP_STORAGE
05571                msgnum = last_message_index(vmu, dir) + 1;
05572 #endif
05573                make_file(fn, sizeof(fn), dir, msgnum);
05574 
05575                /* assign a variable with the name of the voicemail file */ 
05576 #ifndef IMAP_STORAGE
05577                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05578 #else
05579                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05580 #endif
05581 
05582                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05583                ast_filerename(tmptxtfile, fn, NULL);
05584                rename(tmptxtfile, txtfile);
05585                inprocess_count(vmu->mailbox, vmu->context, -1);
05586 
05587                /* Properly set permissions on voicemail text descriptor file.
05588                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05589                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05590                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05591 
05592                ast_unlock_path(dir);
05593                if (ast_check_realtime("voicemail_data")) {
05594                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05595                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05596                }
05597                /* We must store the file first, before copying the message, because
05598                 * ODBC storage does the entire copy with SQL.
05599                 */
05600                if (ast_fileexists(fn, NULL, NULL) > 0) {
05601                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05602                }
05603 
05604                /* Are there to be more recipients of this message? */
05605                while (tmpptr) {
05606                   struct ast_vm_user recipu, *recip;
05607                   char *exten, *cntx;
05608                
05609                   exten = strsep(&tmpptr, "&");
05610                   cntx = strchr(exten, '@');
05611                   if (cntx) {
05612                      *cntx = '\0';
05613                      cntx++;
05614                   }
05615                   if ((recip = find_user(&recipu, cntx, exten))) {
05616                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05617                      free_user(recip);
05618                   }
05619                }
05620 #ifndef IMAP_STORAGE
05621                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05622                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05623                   char sfn[PATH_MAX];
05624                   char dfn[PATH_MAX];
05625                   int x;
05626                   /* It's easier just to try to make it than to check for its existence */
05627                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05628                   x = last_message_index(vmu, urgdir) + 1;
05629                   make_file(sfn, sizeof(sfn), dir, msgnum);
05630                   make_file(dfn, sizeof(dfn), urgdir, x);
05631                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05632                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05633                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05634                   ast_copy_string(fn, dfn, sizeof(fn));
05635                   msgnum = x;
05636                }
05637 #endif
05638                /* Notification needs to happen after the copy, though. */
05639                if (ast_fileexists(fn, NULL, NULL)) {
05640 #ifdef IMAP_STORAGE
05641                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05642 #else
05643                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05644 #endif
05645                }
05646 
05647                /* Disposal needs to happen after the optional move and copy */
05648                if (ast_fileexists(fn, NULL, NULL)) {
05649                   DISPOSE(dir, msgnum);
05650                }
05651             }
05652          }
05653       } else {
05654          inprocess_count(vmu->mailbox, vmu->context, -1);
05655       }
05656       if (res == '0') {
05657          goto transfer;
05658       } else if (res > 0)
05659          res = 0;
05660 
05661       if (duration < vmminsecs)
05662          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05663          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05664       else
05665          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05666    } else
05667       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05668 leave_vm_out:
05669    free_user(vmu);
05670 
05671 #ifdef IMAP_STORAGE
05672    /* expunge message - use UID Expunge if supported on IMAP server*/
05673    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05674    if (expungeonhangup == 1) {
05675       ast_mutex_lock(&vms->lock);
05676 #ifdef HAVE_IMAP_TK2006
05677       if (LEVELUIDPLUS (vms->mailstream)) {
05678          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05679       } else 
05680 #endif
05681          mail_expunge(vms->mailstream);
05682       ast_mutex_unlock(&vms->lock);
05683    }
05684 #endif
05685 
05686    ast_free(tmp);
05687    return res;
05688 }
05689 
05690 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05691 {
05692    int d;
05693    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05694    return d;
05695 }
05696 
05697 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05698 {
05699 #ifdef IMAP_STORAGE
05700    /* we must use mbox(x) folder names, and copy the message there */
05701    /* simple. huh? */
05702    char sequence[10];
05703    char mailbox[256];
05704    int res;
05705 
05706    /* get the real IMAP message number for this message */
05707    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05708    
05709    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05710    ast_mutex_lock(&vms->lock);
05711    /* if save to Old folder, put in INBOX as read */
05712    if (box == OLD_FOLDER) {
05713       mail_setflag(vms->mailstream, sequence, "\\Seen");
05714       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05715    } else if (box == NEW_FOLDER) {
05716       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05717       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05718    }
05719    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05720       ast_mutex_unlock(&vms->lock);
05721       return 0;
05722    }
05723    /* Create the folder if it don't exist */
05724    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05725    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05726    if (mail_create(vms->mailstream, mailbox) == NIL) 
05727       ast_debug(5, "Folder exists.\n");
05728    else
05729       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05730    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05731    ast_mutex_unlock(&vms->lock);
05732    return res;
05733 #else
05734    char *dir = vms->curdir;
05735    char *username = vms->username;
05736    char *context = vmu->context;
05737    char sfn[PATH_MAX];
05738    char dfn[PATH_MAX];
05739    char ddir[PATH_MAX];
05740    const char *dbox = mbox(box);
05741    int x, i;
05742    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05743 
05744    if (vm_lock_path(ddir))
05745       return ERROR_LOCK_PATH;
05746 
05747    x = last_message_index(vmu, ddir) + 1;
05748 
05749    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05750       x--;
05751       for (i = 1; i <= x; i++) {
05752          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05753          make_file(sfn, sizeof(sfn), ddir, i);
05754          make_file(dfn, sizeof(dfn), ddir, i - 1);
05755          if (EXISTS(ddir, i, sfn, NULL)) {
05756             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05757          } else
05758             break;
05759       }
05760    } else {
05761       if (x >= vmu->maxmsg) {
05762          ast_unlock_path(ddir);
05763          return -1;
05764       }
05765    }
05766    make_file(sfn, sizeof(sfn), dir, msg);
05767    make_file(dfn, sizeof(dfn), ddir, x);
05768    if (strcmp(sfn, dfn)) {
05769       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05770    }
05771    ast_unlock_path(ddir);
05772 #endif
05773    return 0;
05774 }
05775 
05776 static int adsi_logo(unsigned char *buf)
05777 {
05778    int bytes = 0;
05779    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05780    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05781    return bytes;
05782 }
05783 
05784 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05785 {
05786    unsigned char buf[256];
05787    int bytes=0;
05788    int x;
05789    char num[5];
05790 
05791    *useadsi = 0;
05792    bytes += ast_adsi_data_mode(buf + bytes);
05793    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05794 
05795    bytes = 0;
05796    bytes += adsi_logo(buf);
05797    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05798 #ifdef DISPLAY
05799    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05800 #endif
05801    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05802    bytes += ast_adsi_data_mode(buf + bytes);
05803    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05804 
05805    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05806       bytes = 0;
05807       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05808       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05809       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05810       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05811       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05812       return 0;
05813    }
05814 
05815 #ifdef DISPLAY
05816    /* Add a dot */
05817    bytes = 0;
05818    bytes += ast_adsi_logo(buf);
05819    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05820    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05821    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05822    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05823 #endif
05824    bytes = 0;
05825    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05826    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05827    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05828    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05829    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05830    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05831    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05832 
05833 #ifdef DISPLAY
05834    /* Add another dot */
05835    bytes = 0;
05836    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05837    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05838 
05839    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05840    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05841 #endif
05842 
05843    bytes = 0;
05844    /* These buttons we load but don't use yet */
05845    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05846    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05847    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05848    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05849    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05850    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05851    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05852 
05853 #ifdef DISPLAY
05854    /* Add another dot */
05855    bytes = 0;
05856    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05857    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05858    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05859 #endif
05860 
05861    bytes = 0;
05862    for (x=0;x<5;x++) {
05863       snprintf(num, sizeof(num), "%d", x);
05864       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05865    }
05866    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05867    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05868 
05869 #ifdef DISPLAY
05870    /* Add another dot */
05871    bytes = 0;
05872    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05873    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05874    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05875 #endif
05876 
05877    if (ast_adsi_end_download(chan)) {
05878       bytes = 0;
05879       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05880       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05881       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05882       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05883       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05884       return 0;
05885    }
05886    bytes = 0;
05887    bytes += ast_adsi_download_disconnect(buf + bytes);
05888    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05889    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05890 
05891    ast_debug(1, "Done downloading scripts...\n");
05892 
05893 #ifdef DISPLAY
05894    /* Add last dot */
05895    bytes = 0;
05896    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05897    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05898 #endif
05899    ast_debug(1, "Restarting session...\n");
05900 
05901    bytes = 0;
05902    /* Load the session now */
05903    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05904       *useadsi = 1;
05905       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05906    } else
05907       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05908 
05909    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05910    return 0;
05911 }
05912 
05913 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05914 {
05915    int x;
05916    if (!ast_adsi_available(chan))
05917       return;
05918    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05919    if (x < 0)
05920       return;
05921    if (!x) {
05922       if (adsi_load_vmail(chan, useadsi)) {
05923          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05924          return;
05925       }
05926    } else
05927       *useadsi = 1;
05928 }
05929 
05930 static void adsi_login(struct ast_channel *chan)
05931 {
05932    unsigned char buf[256];
05933    int bytes=0;
05934    unsigned char keys[8];
05935    int x;
05936    if (!ast_adsi_available(chan))
05937       return;
05938 
05939    for (x=0;x<8;x++)
05940       keys[x] = 0;
05941    /* Set one key for next */
05942    keys[3] = ADSI_KEY_APPS + 3;
05943 
05944    bytes += adsi_logo(buf + bytes);
05945    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05946    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05947    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05948    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05949    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05950    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05951    bytes += ast_adsi_set_keys(buf + bytes, keys);
05952    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05953    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05954 }
05955 
05956 static void adsi_password(struct ast_channel *chan)
05957 {
05958    unsigned char buf[256];
05959    int bytes=0;
05960    unsigned char keys[8];
05961    int x;
05962    if (!ast_adsi_available(chan))
05963       return;
05964 
05965    for (x=0;x<8;x++)
05966       keys[x] = 0;
05967    /* Set one key for next */
05968    keys[3] = ADSI_KEY_APPS + 3;
05969 
05970    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05971    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05972    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05973    bytes += ast_adsi_set_keys(buf + bytes, keys);
05974    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05975    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05976 }
05977 
05978 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05979 {
05980    unsigned char buf[256];
05981    int bytes=0;
05982    unsigned char keys[8];
05983    int x,y;
05984 
05985    if (!ast_adsi_available(chan))
05986       return;
05987 
05988    for (x=0;x<5;x++) {
05989       y = ADSI_KEY_APPS + 12 + start + x;
05990       if (y > ADSI_KEY_APPS + 12 + 4)
05991          y = 0;
05992       keys[x] = ADSI_KEY_SKT | y;
05993    }
05994    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05995    keys[6] = 0;
05996    keys[7] = 0;
05997 
05998    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05999    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06000    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06001    bytes += ast_adsi_set_keys(buf + bytes, keys);
06002    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06003 
06004    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06005 }
06006 
06007 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06008 {
06009    int bytes=0;
06010    unsigned char buf[256]; 
06011    char buf1[256], buf2[256];
06012    char fn2[PATH_MAX];
06013 
06014    char cid[256]="";
06015    char *val;
06016    char *name, *num;
06017    char datetime[21]="";
06018    FILE *f;
06019 
06020    unsigned char keys[8];
06021 
06022    int x;
06023 
06024    if (!ast_adsi_available(chan))
06025       return;
06026 
06027    /* Retrieve important info */
06028    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06029    f = fopen(fn2, "r");
06030    if (f) {
06031       while (!feof(f)) {   
06032          if (!fgets((char *)buf, sizeof(buf), f)) {
06033             continue;
06034          }
06035          if (!feof(f)) {
06036             char *stringp=NULL;
06037             stringp = (char *)buf;
06038             strsep(&stringp, "=");
06039             val = strsep(&stringp, "=");
06040             if (!ast_strlen_zero(val)) {
06041                if (!strcmp((char *)buf, "callerid"))
06042                   ast_copy_string(cid, val, sizeof(cid));
06043                if (!strcmp((char *)buf, "origdate"))
06044                   ast_copy_string(datetime, val, sizeof(datetime));
06045             }
06046          }
06047       }
06048       fclose(f);
06049    }
06050    /* New meaning for keys */
06051    for (x=0;x<5;x++)
06052       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06053    keys[6] = 0x0;
06054    keys[7] = 0x0;
06055 
06056    if (!vms->curmsg) {
06057       /* No prev key, provide "Folder" instead */
06058       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06059    }
06060    if (vms->curmsg >= vms->lastmsg) {
06061       /* If last message ... */
06062       if (vms->curmsg) {
06063          /* but not only message, provide "Folder" instead */
06064          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06065          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06066 
06067       } else {
06068          /* Otherwise if only message, leave blank */
06069          keys[3] = 1;
06070       }
06071    }
06072 
06073    if (!ast_strlen_zero(cid)) {
06074       ast_callerid_parse(cid, &name, &num);
06075       if (!name)
06076          name = num;
06077    } else
06078       name = "Unknown Caller";
06079 
06080    /* If deleted, show "undeleted" */
06081 
06082    if (vms->deleted[vms->curmsg])
06083       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06084 
06085    /* Except "Exit" */
06086    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06087    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06088       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06089    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06090 
06091    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06092    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06093    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06094    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06095    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06096    bytes += ast_adsi_set_keys(buf + bytes, keys);
06097    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06098 
06099    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06100 }
06101 
06102 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06103 {
06104    int bytes=0;
06105    unsigned char buf[256];
06106    unsigned char keys[8];
06107 
06108    int x;
06109 
06110    if (!ast_adsi_available(chan))
06111       return;
06112 
06113    /* New meaning for keys */
06114    for (x=0;x<5;x++)
06115       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06116 
06117    keys[6] = 0x0;
06118    keys[7] = 0x0;
06119 
06120    if (!vms->curmsg) {
06121       /* No prev key, provide "Folder" instead */
06122       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06123    }
06124    if (vms->curmsg >= vms->lastmsg) {
06125       /* If last message ... */
06126       if (vms->curmsg) {
06127          /* but not only message, provide "Folder" instead */
06128          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06129       } else {
06130          /* Otherwise if only message, leave blank */
06131          keys[3] = 1;
06132       }
06133    }
06134 
06135    /* If deleted, show "undeleted" */
06136    if (vms->deleted[vms->curmsg]) 
06137       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06138 
06139    /* Except "Exit" */
06140    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06141    bytes += ast_adsi_set_keys(buf + bytes, keys);
06142    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06143 
06144    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06145 }
06146 
06147 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06148 {
06149    unsigned char buf[256] = "";
06150    char buf1[256] = "", buf2[256] = "";
06151    int bytes=0;
06152    unsigned char keys[8];
06153    int x;
06154 
06155    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06156    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06157    if (!ast_adsi_available(chan))
06158       return;
06159    if (vms->newmessages) {
06160       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06161       if (vms->oldmessages) {
06162          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06163          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06164       } else {
06165          snprintf(buf2, sizeof(buf2), "%s.", newm);
06166       }
06167    } else if (vms->oldmessages) {
06168       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06169       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06170    } else {
06171       strcpy(buf1, "You have no messages.");
06172       buf2[0] = ' ';
06173       buf2[1] = '\0';
06174    }
06175    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06176    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06177    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06178 
06179    for (x=0;x<6;x++)
06180       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06181    keys[6] = 0;
06182    keys[7] = 0;
06183 
06184    /* Don't let them listen if there are none */
06185    if (vms->lastmsg < 0)
06186       keys[0] = 1;
06187    bytes += ast_adsi_set_keys(buf + bytes, keys);
06188 
06189    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06190 
06191    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06192 }
06193 
06194 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06195 {
06196    unsigned char buf[256] = "";
06197    char buf1[256] = "", buf2[256] = "";
06198    int bytes=0;
06199    unsigned char keys[8];
06200    int x;
06201 
06202    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06203 
06204    if (!ast_adsi_available(chan))
06205       return;
06206 
06207    /* Original command keys */
06208    for (x=0;x<6;x++)
06209       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06210 
06211    keys[6] = 0;
06212    keys[7] = 0;
06213 
06214    if ((vms->lastmsg + 1) < 1)
06215       keys[0] = 0;
06216 
06217    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06218       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06219 
06220    if (vms->lastmsg + 1)
06221       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06222    else
06223       strcpy(buf2, "no messages.");
06224    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06225    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06226    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06227    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06228    bytes += ast_adsi_set_keys(buf + bytes, keys);
06229 
06230    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06231 
06232    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06233    
06234 }
06235 
06236 /*
06237 static void adsi_clear(struct ast_channel *chan)
06238 {
06239    char buf[256];
06240    int bytes=0;
06241    if (!ast_adsi_available(chan))
06242       return;
06243    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06244    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06245 
06246    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06247 }
06248 */
06249 
06250 static void adsi_goodbye(struct ast_channel *chan)
06251 {
06252    unsigned char buf[256];
06253    int bytes=0;
06254 
06255    if (!ast_adsi_available(chan))
06256       return;
06257    bytes += adsi_logo(buf + bytes);
06258    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06259    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06260    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06261    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06262 
06263    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06264 }
06265 
06266 /*!\brief get_folder: Folder menu
06267  * Plays "press 1 for INBOX messages" etc.
06268  * Should possibly be internationalized
06269  */
06270 static int get_folder(struct ast_channel *chan, int start)
06271 {
06272    int x;
06273    int d;
06274    char fn[PATH_MAX];
06275    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06276    if (d)
06277       return d;
06278    for (x = start; x< 5; x++) {  /* For all folders */
06279       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06280          return d;
06281       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06282       if (d)
06283          return d;
06284       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06285       d = vm_play_folder_name(chan, fn);
06286       if (d)
06287          return d;
06288       d = ast_waitfordigit(chan, 500);
06289       if (d)
06290          return d;
06291    }
06292    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06293    if (d)
06294       return d;
06295    d = ast_waitfordigit(chan, 4000);
06296    return d;
06297 }
06298 
06299 /*!
06300  * \brief plays a prompt and waits for a keypress.
06301  * \param chan
06302  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06303  * \param start Does not appear to be used at this time.
06304  *
06305  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06306  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06307  * prompting for the number inputs that correspond to the available folders.
06308  * 
06309  * \return zero on success, or -1 on error.
06310  */
06311 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06312 {
06313    int res = 0;
06314    int loops = 0;
06315    res = ast_play_and_wait(chan, fn);  /* Folder name */
06316    while (((res < '0') || (res > '9')) &&
06317          (res != '#') && (res >= 0) &&
06318          loops < 4) {
06319       res = get_folder(chan, 0);
06320       loops++;
06321    }
06322    if (loops == 4) { /* give up */
06323       return '#';
06324    }
06325    return res;
06326 }
06327 
06328 /*!
06329  * \brief presents the option to prepend to an existing message when forwarding it.
06330  * \param chan
06331  * \param vmu
06332  * \param curdir
06333  * \param curmsg
06334  * \param vmfmts
06335  * \param context
06336  * \param record_gain
06337  * \param duration
06338  * \param vms
06339  *
06340  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06341  *
06342  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06343  * \return zero on success, -1 on error.
06344  */
06345 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06346          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06347 {
06348 #ifdef IMAP_STORAGE
06349    int res;
06350 #endif
06351    int cmd = 0;
06352    int retries = 0, prepend_duration = 0, already_recorded = 0;
06353    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06354    char textfile[PATH_MAX];
06355    struct ast_config *msg_cfg;
06356    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06357 #ifndef IMAP_STORAGE
06358    signed char zero_gain = 0;
06359 #endif
06360    const char *duration_str;
06361 
06362    /* Must always populate duration correctly */
06363    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06364    strcpy(textfile, msgfile);
06365    strcpy(backup, msgfile);
06366    strcpy(backup_textfile, msgfile);
06367    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06368    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06369    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06370 
06371    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06372       *duration = atoi(duration_str);
06373    } else {
06374       *duration = 0;
06375    }
06376 
06377    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06378       if (cmd)
06379          retries = 0;
06380       switch (cmd) {
06381       case '1': 
06382 
06383 #ifdef IMAP_STORAGE
06384          /* Record new intro file */
06385          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06386          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06387          res = ast_play_and_wait(chan, INTRO);
06388          res = ast_play_and_wait(chan, "beep");
06389          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06390          cmd = 't';
06391 #else
06392 
06393          /* prepend a message to the current message, update the metadata and return */
06394 
06395          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06396          strcpy(textfile, msgfile);
06397          strncat(textfile, ".txt", sizeof(textfile) - 1);
06398          *duration = 0;
06399 
06400          /* if we can't read the message metadata, stop now */
06401          if (!msg_cfg) {
06402             cmd = 0;
06403             break;
06404          }
06405          
06406          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06407          if (already_recorded) {
06408             ast_filecopy(backup, msgfile, NULL);
06409             copy(backup_textfile, textfile);
06410          }
06411          else {
06412             ast_filecopy(msgfile, backup, NULL);
06413             copy(textfile,backup_textfile);
06414          }
06415          already_recorded = 1;
06416 
06417          if (record_gain)
06418             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06419 
06420          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06421          if (record_gain)
06422             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06423 
06424          
06425          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06426             *duration = atoi(duration_str);
06427 
06428          if (prepend_duration) {
06429             struct ast_category *msg_cat;
06430             /* need enough space for a maximum-length message duration */
06431             char duration_buf[12];
06432 
06433             *duration += prepend_duration;
06434             msg_cat = ast_category_get(msg_cfg, "message");
06435             snprintf(duration_buf, 11, "%ld", *duration);
06436             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06437                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06438             }
06439          }
06440 
06441 #endif
06442          break;
06443       case '2': 
06444          /* NULL out introfile so we know there is no intro! */
06445 #ifdef IMAP_STORAGE
06446          *vms->introfn = '\0';
06447 #endif
06448          cmd = 't';
06449          break;
06450       case '*':
06451          cmd = '*';
06452          break;
06453       default: 
06454          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06455             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06456          if (!cmd)
06457             cmd = ast_play_and_wait(chan,"vm-starmain");
06458             /* "press star to return to the main menu" */
06459          if (!cmd)
06460             cmd = ast_waitfordigit(chan,6000);
06461          if (!cmd)
06462             retries++;
06463          if (retries > 3)
06464             cmd = 't';
06465       }
06466    }
06467 
06468    if (msg_cfg)
06469       ast_config_destroy(msg_cfg);
06470    if (prepend_duration)
06471       *duration = prepend_duration;
06472 
06473    if (already_recorded && cmd == -1) {
06474       /* restore original message if prepention cancelled */
06475       ast_filerename(backup, msgfile, NULL);
06476       rename(backup_textfile, textfile);
06477    }
06478 
06479    if (cmd == 't' || cmd == 'S')
06480       cmd = 0;
06481    return cmd;
06482 }
06483 
06484 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06485 {
06486    struct ast_event *event;
06487    char *mailbox, *context;
06488 
06489    /* Strip off @default */
06490    context = mailbox = ast_strdupa(box);
06491    strsep(&context, "@");
06492    if (ast_strlen_zero(context))
06493       context = "default";
06494 
06495    if (!(event = ast_event_new(AST_EVENT_MWI,
06496          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06497          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06498          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06499          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06500          AST_EVENT_IE_END))) {
06501       return;
06502    }
06503 
06504    ast_event_queue_and_cache(event);
06505 }
06506 
06507 /*!
06508  * \brief Sends email notification that a user has a new voicemail waiting for them.
06509  * \param chan
06510  * \param vmu
06511  * \param vms
06512  * \param msgnum
06513  * \param duration
06514  * \param fmt
06515  * \param cidnum The Caller ID phone number value.
06516  * \param cidname The Caller ID name value.
06517  *
06518  * \return zero on success, -1 on error.
06519  */
06520 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)
06521 {
06522    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06523    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06524    const char *category;
06525    char *myserveremail = serveremail;
06526 
06527    ast_channel_lock(chan);
06528    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06529       category = ast_strdupa(category);
06530    }
06531    ast_channel_unlock(chan);
06532 
06533    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06534    make_file(fn, sizeof(fn), todir, msgnum);
06535    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06536 
06537    if (!ast_strlen_zero(vmu->attachfmt)) {
06538       if (strstr(fmt, vmu->attachfmt))
06539          fmt = vmu->attachfmt;
06540       else
06541          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);
06542    }
06543 
06544    /* Attach only the first format */
06545    fmt = ast_strdupa(fmt);
06546    stringp = fmt;
06547    strsep(&stringp, "|");
06548 
06549    if (!ast_strlen_zero(vmu->serveremail))
06550       myserveremail = vmu->serveremail;
06551 
06552    if (!ast_strlen_zero(vmu->email)) {
06553       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06554 
06555       if (attach_user_voicemail)
06556          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06557 
06558       /* XXX possible imap issue, should category be NULL XXX */
06559       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06560 
06561       if (attach_user_voicemail)
06562          DISPOSE(todir, msgnum);
06563    }
06564 
06565    if (!ast_strlen_zero(vmu->pager)) {
06566       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06567    }
06568 
06569    if (ast_test_flag(vmu, VM_DELETE))
06570       DELETE(todir, msgnum, fn, vmu);
06571 
06572    /* Leave voicemail for someone */
06573    if (ast_app_has_voicemail(ext_context, NULL)) 
06574       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06575 
06576    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06577 
06578    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);
06579    run_externnotify(vmu->context, vmu->mailbox, flag);
06580 
06581 #ifdef IMAP_STORAGE
06582    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06583    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06584       vm_imap_delete(NULL, vms->curmsg, vmu);
06585       vms->newmessages--;  /* Fix new message count */
06586    }
06587 #endif
06588 
06589    return 0;
06590 }
06591 
06592 /*!
06593  * \brief Sends a voicemail message to a mailbox recipient.
06594  * \param ast_channel
06595  * \param context
06596  * \param vms
06597  * \param sender
06598  * \param fmt
06599  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06600  *             Will be 0 when called to forward an existing message (option 8)
06601  *             Will be 1 when called to leave a message (option 3->5)
06602  * \param record_gain 
06603  *
06604  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06605  * 
06606  * When in the leave message mode (is_new_message == 1):
06607  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06608  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06609  *
06610  * When in the forward message mode (is_new_message == 0):
06611  *   - retreives the current message to be forwarded
06612  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06613  *   - determines the target mailbox and folders
06614  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06615  *
06616  * \return zero on success, -1 on error.
06617  */
06618 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)
06619 {
06620 #ifdef IMAP_STORAGE
06621    int todircount=0;
06622    struct vm_state *dstvms;
06623 #endif
06624    char username[70]="";
06625    char fn[PATH_MAX]; /* for playback of name greeting */
06626    char ecodes[16] = "#";
06627    int res = 0, cmd = 0;
06628    struct ast_vm_user *receiver = NULL, *vmtmp;
06629    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06630    char *stringp;
06631    const char *s;
06632    int saved_messages = 0, found = 0;
06633    int valid_extensions = 0;
06634    char *dir;
06635    int curmsg;
06636    char urgent_str[7] = "";
06637    char tmptxtfile[PATH_MAX];
06638    int prompt_played = 0;
06639 #ifndef IMAP_STORAGE
06640    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06641 #endif
06642    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06643       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06644    }
06645 
06646    if (vms == NULL) return -1;
06647    dir = vms->curdir;
06648    curmsg = vms->curmsg;
06649 
06650    tmptxtfile[0] = '\0';
06651    while (!res && !valid_extensions) {
06652       int use_directory = 0;
06653       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06654          int done = 0;
06655          int retries = 0;
06656          cmd=0;
06657          while ((cmd >= 0) && !done ){
06658             if (cmd)
06659                retries = 0;
06660             switch (cmd) {
06661             case '1': 
06662                use_directory = 0;
06663                done = 1;
06664                break;
06665             case '2': 
06666                use_directory = 1;
06667                done=1;
06668                break;
06669             case '*': 
06670                cmd = 't';
06671                done = 1;
06672                break;
06673             default: 
06674                /* Press 1 to enter an extension press 2 to use the directory */
06675                cmd = ast_play_and_wait(chan,"vm-forward");
06676                if (!cmd)
06677                   cmd = ast_waitfordigit(chan,3000);
06678                if (!cmd)
06679                   retries++;
06680                if (retries > 3) {
06681                   cmd = 't';
06682                   done = 1;
06683                }
06684                
06685             }
06686          }
06687          if (cmd < 0 || cmd == 't')
06688             break;
06689       }
06690       
06691       if (use_directory) {
06692          /* use app_directory */
06693          
06694          char old_context[sizeof(chan->context)];
06695          char old_exten[sizeof(chan->exten)];
06696          int old_priority;
06697          struct ast_app* directory_app;
06698 
06699          directory_app = pbx_findapp("Directory");
06700          if (directory_app) {
06701             char vmcontext[256];
06702             /* make backup copies */
06703             memcpy(old_context, chan->context, sizeof(chan->context));
06704             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06705             old_priority = chan->priority;
06706             
06707             /* call the the Directory, changes the channel */
06708             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06709             res = pbx_exec(chan, directory_app, vmcontext);
06710             
06711             ast_copy_string(username, chan->exten, sizeof(username));
06712             
06713             /* restore the old context, exten, and priority */
06714             memcpy(chan->context, old_context, sizeof(chan->context));
06715             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06716             chan->priority = old_priority;
06717          } else {
06718             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06719             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06720          }
06721       } else {
06722          /* Ask for an extension */
06723          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06724          prompt_played++;
06725          if (res || prompt_played > 4)
06726             break;
06727          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06728             break;
06729       }
06730       
06731       /* start all over if no username */
06732       if (ast_strlen_zero(username))
06733          continue;
06734       stringp = username;
06735       s = strsep(&stringp, "*");
06736       /* start optimistic */
06737       valid_extensions = 1;
06738       while (s) {
06739          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06740             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06741             found++;
06742          } else {
06743             /* XXX Optimization for the future.  When we encounter a single bad extension,
06744              * bailing out on all of the extensions may not be the way to go.  We should
06745              * probably just bail on that single extension, then allow the user to enter
06746              * several more. XXX
06747              */
06748             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06749                free_user(receiver);
06750             }
06751             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06752             valid_extensions = 0;
06753             break;
06754          }
06755 
06756          /* play name if available, else play extension number */
06757          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06758          RETRIEVE(fn, -1, s, receiver->context);
06759          if (ast_fileexists(fn, NULL, NULL) > 0) {
06760             res = ast_stream_and_wait(chan, fn, ecodes);
06761             if (res) {
06762                DISPOSE(fn, -1);
06763                return res;
06764             }
06765          } else {
06766             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06767          }
06768          DISPOSE(fn, -1);
06769 
06770          s = strsep(&stringp, "*");
06771       }
06772       /* break from the loop of reading the extensions */
06773       if (valid_extensions)
06774          break;
06775       /* "I am sorry, that's not a valid extension.  Please try again." */
06776       res = ast_play_and_wait(chan, "pbx-invalid");
06777    }
06778    /* check if we're clear to proceed */
06779    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06780       return res;
06781    if (is_new_message == 1) {
06782       struct leave_vm_options leave_options;
06783       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06784       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06785 
06786       /* Send VoiceMail */
06787       memset(&leave_options, 0, sizeof(leave_options));
06788       leave_options.record_gain = record_gain;
06789       cmd = leave_voicemail(chan, mailbox, &leave_options);
06790    } else {
06791       /* Forward VoiceMail */
06792       long duration = 0;
06793       struct vm_state vmstmp;
06794       int copy_msg_result = 0;
06795       memcpy(&vmstmp, vms, sizeof(vmstmp));
06796 
06797       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06798 
06799       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06800       if (!cmd) {
06801          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06802 #ifdef IMAP_STORAGE
06803             int attach_user_voicemail;
06804             char *myserveremail = serveremail;
06805             
06806             /* get destination mailbox */
06807             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06808             if (!dstvms) {
06809                dstvms = create_vm_state_from_user(vmtmp);
06810             }
06811             if (dstvms) {
06812                init_mailstream(dstvms, 0);
06813                if (!dstvms->mailstream) {
06814                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06815                } else {
06816                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06817                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06818                }
06819             } else {
06820                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06821             }
06822             if (!ast_strlen_zero(vmtmp->serveremail))
06823                myserveremail = vmtmp->serveremail;
06824             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06825             /* NULL category for IMAP storage */
06826             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);
06827 #else
06828             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06829 #endif
06830             saved_messages++;
06831             AST_LIST_REMOVE_CURRENT(list);
06832             free_user(vmtmp);
06833             if (res)
06834                break;
06835          }
06836          AST_LIST_TRAVERSE_SAFE_END;
06837          if (saved_messages > 0 && !copy_msg_result) {
06838             /* give confirmation that the message was saved */
06839             /* commented out since we can't forward batches yet
06840             if (saved_messages == 1)
06841                res = ast_play_and_wait(chan, "vm-message");
06842             else
06843                res = ast_play_and_wait(chan, "vm-messages");
06844             if (!res)
06845                res = ast_play_and_wait(chan, "vm-saved"); */
06846 #ifdef IMAP_STORAGE
06847             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06848             if (ast_strlen_zero(vmstmp.introfn))
06849 #endif
06850             res = ast_play_and_wait(chan, "vm-msgsaved");
06851          }
06852 #ifndef IMAP_STORAGE
06853          else {
06854             /* with IMAP, mailbox full warning played by imap_check_limits */
06855             res = ast_play_and_wait(chan, "vm-mailboxfull");
06856          }
06857          /* Restore original message without prepended message if backup exists */
06858          make_file(msgfile, sizeof(msgfile), dir, curmsg);
06859          strcpy(textfile, msgfile);
06860          strcpy(backup, msgfile);
06861          strcpy(backup_textfile, msgfile);
06862          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06863          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06864          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06865          if (ast_fileexists(backup, NULL, NULL) > 0) {
06866             ast_filerename(backup, msgfile, NULL);
06867             rename(backup_textfile, textfile);
06868          }
06869 #endif
06870       }
06871       DISPOSE(dir, curmsg);
06872    }
06873 
06874    /* If anything failed above, we still have this list to free */
06875    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06876       free_user(vmtmp);
06877    return res ? res : cmd;
06878 }
06879 
06880 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06881 {
06882    int res;
06883    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06884       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06885    return res;
06886 }
06887 
06888 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06889 {
06890    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);
06891 }
06892 
06893 static int play_message_category(struct ast_channel *chan, const char *category)
06894 {
06895    int res = 0;
06896 
06897    if (!ast_strlen_zero(category))
06898       res = ast_play_and_wait(chan, category);
06899 
06900    if (res) {
06901       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06902       res = 0;
06903    }
06904 
06905    return res;
06906 }
06907 
06908 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06909 {
06910    int res = 0;
06911    struct vm_zone *the_zone = NULL;
06912    time_t t;
06913 
06914    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06915       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06916       return 0;
06917    }
06918 
06919    /* Does this user have a timezone specified? */
06920    if (!ast_strlen_zero(vmu->zonetag)) {
06921       /* Find the zone in the list */
06922       struct vm_zone *z;
06923       AST_LIST_LOCK(&zones);
06924       AST_LIST_TRAVERSE(&zones, z, list) {
06925          if (!strcmp(z->name, vmu->zonetag)) {
06926             the_zone = z;
06927             break;
06928          }
06929       }
06930       AST_LIST_UNLOCK(&zones);
06931    }
06932 
06933 /* No internal variable parsing for now, so we'll comment it out for the time being */
06934 #if 0
06935    /* Set the DIFF_* variables */
06936    ast_localtime(&t, &time_now, NULL);
06937    tv_now = ast_tvnow();
06938    ast_localtime(&tv_now, &time_then, NULL);
06939 
06940    /* Day difference */
06941    if (time_now.tm_year == time_then.tm_year)
06942       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06943    else
06944       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06945    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06946 
06947    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06948 #endif
06949    if (the_zone) {
06950       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06951    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
06952       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06953    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
06954       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06955    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
06956       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);
06957    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
06958       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06959    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
06960       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06961    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
06962       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06963    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
06964       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);
06965    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
06966       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06967    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
06968       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
06969    } else {
06970       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06971    }
06972 #if 0
06973    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06974 #endif
06975    return res;
06976 }
06977 
06978 
06979 
06980 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06981 {
06982    int res = 0;
06983    int i;
06984    char *callerid, *name;
06985    char prefile[PATH_MAX] = "";
06986    
06987 
06988    /* If voicemail cid is not enabled, or we didn't get cid or context from
06989     * the attribute file, leave now.
06990     *
06991     * TODO Still need to change this so that if this function is called by the
06992     * message envelope (and someone is explicitly requesting to hear the CID),
06993     * it does not check to see if CID is enabled in the config file.
06994     */
06995    if ((cid == NULL)||(context == NULL))
06996       return res;
06997 
06998    /* Strip off caller ID number from name */
06999    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07000    ast_callerid_parse(cid, &name, &callerid);
07001    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07002       /* Check for internal contexts and only */
07003       /* say extension when the call didn't come from an internal context in the list */
07004       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07005          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07006          if ((strcmp(cidinternalcontexts[i], context) == 0))
07007             break;
07008       }
07009       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07010          if (!res) {
07011             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07012             if (!ast_strlen_zero(prefile)) {
07013             /* See if we can find a recorded name for this person instead of their extension number */
07014                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07015                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07016                   if (!callback)
07017                      res = wait_file2(chan, vms, "vm-from");
07018                   res = ast_stream_and_wait(chan, prefile, "");
07019                } else {
07020                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07021                   /* Say "from extension" as one saying to sound smoother */
07022                   if (!callback)
07023                      res = wait_file2(chan, vms, "vm-from-extension");
07024                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07025                }
07026             }
07027          }
07028       } else if (!res) {
07029          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07030          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07031          if (!callback)
07032             res = wait_file2(chan, vms, "vm-from-phonenumber");
07033          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07034       }
07035    } else {
07036       /* Number unknown */
07037       ast_debug(1, "VM-CID: From an unknown number\n");
07038       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07039       res = wait_file2(chan, vms, "vm-unknown-caller");
07040    }
07041    return res;
07042 }
07043 
07044 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07045 {
07046    int res = 0;
07047    int durationm;
07048    int durations;
07049    /* Verify that we have a duration for the message */
07050    if (duration == NULL)
07051       return res;
07052 
07053    /* Convert from seconds to minutes */
07054    durations=atoi(duration);
07055    durationm=(durations / 60);
07056 
07057    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07058 
07059    if ((!res) && (durationm >= minduration)) {
07060       res = wait_file2(chan, vms, "vm-duration");
07061 
07062       /* POLISH syntax */
07063       if (!strncasecmp(chan->language, "pl", 2)) {
07064          div_t num = div(durationm, 10);
07065 
07066          if (durationm == 1) {
07067             res = ast_play_and_wait(chan, "digits/1z");
07068             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07069          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07070             if (num.rem == 2) {
07071                if (!num.quot) {
07072                   res = ast_play_and_wait(chan, "digits/2-ie");
07073                } else {
07074                   res = say_and_wait(chan, durationm - 2 , chan->language);
07075                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07076                }
07077             } else {
07078                res = say_and_wait(chan, durationm, chan->language);
07079             }
07080             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07081          } else {
07082             res = say_and_wait(chan, durationm, chan->language);
07083             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07084          }
07085       /* DEFAULT syntax */
07086       } else {
07087          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07088          res = wait_file2(chan, vms, "vm-minutes");
07089       }
07090    }
07091    return res;
07092 }
07093 
07094 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07095 {
07096    int res = 0;
07097    char filename[256], *cid;
07098    const char *origtime, *context, *category, *duration, *flag;
07099    struct ast_config *msg_cfg;
07100    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07101 
07102    vms->starting = 0;
07103    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07104    adsi_message(chan, vms);
07105    if (!vms->curmsg)
07106       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07107    else if (vms->curmsg == vms->lastmsg)
07108       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07109 
07110    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07111    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07112    msg_cfg = ast_config_load(filename, config_flags);
07113    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07114       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07115       return 0;
07116    }
07117    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07118 
07119    /* Play the word urgent if we are listening to urgent messages */
07120    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07121       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07122    }
07123 
07124    if (!res) {
07125       /* POLISH syntax */
07126       if (!strncasecmp(chan->language, "pl", 2)) {
07127          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07128             int ten, one;
07129             char nextmsg[256];
07130             ten = (vms->curmsg + 1) / 10;
07131             one = (vms->curmsg + 1) % 10;
07132 
07133             if (vms->curmsg < 20) {
07134                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07135                res = wait_file2(chan, vms, nextmsg);
07136             } else {
07137                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07138                res = wait_file2(chan, vms, nextmsg);
07139                if (one > 0) {
07140                   if (!res) {
07141                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07142                      res = wait_file2(chan, vms, nextmsg);
07143                   }
07144                }
07145             }
07146          }
07147          if (!res)
07148             res = wait_file2(chan, vms, "vm-message");
07149       /* HEBREW syntax */
07150       } else if (!strncasecmp(chan->language, "he", 2)) {
07151          if (!vms->curmsg) {
07152             res = wait_file2(chan, vms, "vm-message");
07153             res = wait_file2(chan, vms, "vm-first");
07154          } else if (vms->curmsg == vms->lastmsg) {
07155             res = wait_file2(chan, vms, "vm-message");
07156             res = wait_file2(chan, vms, "vm-last");
07157          } else {
07158             res = wait_file2(chan, vms, "vm-message");
07159             res = wait_file2(chan, vms, "vm-number");
07160             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07161          }
07162       } else {
07163          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07164             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07165          } else { /* DEFAULT syntax */
07166             res = wait_file2(chan, vms, "vm-message");
07167          }
07168          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07169             if (!res) {
07170                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07171             }
07172          }
07173       }
07174    }
07175 
07176    if (!msg_cfg) {
07177       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07178       return 0;
07179    }
07180 
07181    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07182       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07183       DISPOSE(vms->curdir, vms->curmsg);
07184       ast_config_destroy(msg_cfg);
07185       return 0;
07186    }
07187 
07188    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07189    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07190    category = ast_variable_retrieve(msg_cfg, "message", "category");
07191 
07192    context = ast_variable_retrieve(msg_cfg, "message", "context");
07193    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07194       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07195    if (!res) {
07196       res = play_message_category(chan, category);
07197    }
07198    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07199       res = play_message_datetime(chan, vmu, origtime, filename);
07200    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07201       res = play_message_callerid(chan, vms, cid, context, 0);
07202    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07203       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07204    /* Allow pressing '1' to skip envelope / callerid */
07205    if (res == '1')
07206       res = 0;
07207    ast_config_destroy(msg_cfg);
07208 
07209    if (!res) {
07210       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07211       vms->heard[vms->curmsg] = 1;
07212 #ifdef IMAP_STORAGE
07213       /*IMAP storage stores any prepended message from a forward
07214        * as a separate file from the rest of the message
07215        */
07216       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07217          wait_file(chan, vms, vms->introfn);
07218       }
07219 #endif
07220       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07221          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07222          res = 0;
07223       }
07224    }
07225    DISPOSE(vms->curdir, vms->curmsg);
07226    return res;
07227 }
07228 
07229 #ifdef IMAP_STORAGE
07230 static int imap_remove_file(char *dir, int msgnum)
07231 {
07232    char fn[PATH_MAX];
07233    char full_fn[PATH_MAX];
07234    char intro[PATH_MAX] = {0,};
07235    
07236    if (msgnum > -1) {
07237       make_file(fn, sizeof(fn), dir, msgnum);
07238       snprintf(intro, sizeof(intro), "%sintro", fn);
07239    } else
07240       ast_copy_string(fn, dir, sizeof(fn));
07241    
07242    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07243       ast_filedelete(fn, NULL);
07244       if (!ast_strlen_zero(intro)) {
07245          ast_filedelete(intro, NULL);
07246       }
07247       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07248       unlink(full_fn);
07249    }
07250    return 0;
07251 }
07252 
07253 
07254 
07255 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07256 {
07257    char *file, *filename;
07258    char *attachment;
07259    char arg[10];
07260    int i;
07261    BODY* body;
07262 
07263    
07264    file = strrchr(ast_strdupa(dir), '/');
07265    if (file)
07266       *file++ = '\0';
07267    else {
07268       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07269       return -1;
07270    }
07271 
07272    ast_mutex_lock(&vms->lock);
07273    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07274       mail_fetchstructure(vms->mailstream, i + 1, &body);
07275       /* We have the body, now we extract the file name of the first attachment. */
07276       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07277          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07278       } else {
07279          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07280          ast_mutex_unlock(&vms->lock);
07281          return -1;
07282       }
07283       filename = strsep(&attachment, ".");
07284       if (!strcmp(filename, file)) {
07285          sprintf (arg,"%d", i+1);
07286          mail_setflag (vms->mailstream,arg,"\\DELETED");
07287       }
07288    }
07289    mail_expunge(vms->mailstream);
07290    ast_mutex_unlock(&vms->lock);
07291    return 0;
07292 }
07293 
07294 #else
07295 #ifndef IMAP_STORAGE
07296 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07297 {
07298    int count_msg, last_msg;
07299 
07300    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07301    
07302    /* Rename the member vmbox HERE so that we don't try to return before
07303     * we know what's going on.
07304     */
07305    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07306    
07307    /* Faster to make the directory than to check if it exists. */
07308    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07309 
07310    count_msg = count_messages(vmu, vms->curdir);
07311    if (count_msg < 0)
07312       return count_msg;
07313    else
07314       vms->lastmsg = count_msg - 1;
07315 
07316    /*
07317    The following test is needed in case sequencing gets messed up.
07318    There appears to be more than one way to mess up sequence, so
07319    we will not try to find all of the root causes--just fix it when
07320    detected.
07321    */
07322 
07323    if (vm_lock_path(vms->curdir)) {
07324       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07325       return -1;
07326    }
07327 
07328    last_msg = last_message_index(vmu, vms->curdir);
07329    ast_unlock_path(vms->curdir);
07330 
07331    if (last_msg < 0) 
07332       return last_msg;
07333 
07334    return 0;
07335 }
07336 #endif
07337 #endif
07338 
07339 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07340 {
07341    int x = 0;
07342 #ifndef IMAP_STORAGE
07343    int res = 0, nummsg;
07344    char fn2[PATH_MAX];
07345 #endif
07346 
07347    if (vms->lastmsg <= -1)
07348       goto done;
07349 
07350    vms->curmsg = -1; 
07351 #ifndef IMAP_STORAGE
07352    /* Get the deleted messages fixed */ 
07353    if (vm_lock_path(vms->curdir))
07354       return ERROR_LOCK_PATH;
07355 
07356    for (x = 0; x < vmu->maxmsg; x++) { 
07357       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
07358          /* Save this message.  It's not in INBOX or hasn't been heard */ 
07359          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
07360          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
07361             break;
07362          vms->curmsg++; 
07363          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
07364          if (strcmp(vms->fn, fn2)) { 
07365             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07366          } 
07367       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07368          /* Move to old folder before deleting */ 
07369          res = save_to_folder(vmu, vms, x, 1);
07370          if (res == ERROR_LOCK_PATH) {
07371             /* If save failed do not delete the message */
07372             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07373             vms->deleted[x] = 0;
07374             vms->heard[x] = 0;
07375             --x;
07376          }
07377       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07378          /* Move to deleted folder */ 
07379          res = save_to_folder(vmu, vms, x, 10);
07380          if (res == ERROR_LOCK_PATH) {
07381             /* If save failed do not delete the message */
07382             vms->deleted[x] = 0;
07383             vms->heard[x] = 0;
07384             --x;
07385          }
07386       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07387          /* If realtime storage enabled - we should explicitly delete this message,
07388          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07389          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07390          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07391             DELETE(vms->curdir, x, vms->fn, vmu);
07392       }
07393    } 
07394 
07395    /* Delete ALL remaining messages */
07396    nummsg = x - 1;
07397    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07398       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07399       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07400          DELETE(vms->curdir, x, vms->fn, vmu);
07401    }
07402    ast_unlock_path(vms->curdir);
07403 #else
07404    if (vms->deleted) {
07405       for (x=0;x < vmu->maxmsg;x++) { 
07406          if (vms->deleted[x]) { 
07407             ast_debug(3,"IMAP delete of %d\n",x);
07408             DELETE(vms->curdir, x, vms->fn, vmu);
07409          }
07410       }
07411    }
07412 #endif
07413 
07414 done:
07415    if (vms->deleted)
07416       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07417    if (vms->heard)
07418       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07419 
07420    return 0;
07421 }
07422 
07423 /* In Greek even though we CAN use a syntax like "friends messages"
07424  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07425  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07426  * syntax for the above three categories which is more elegant. 
07427  */
07428 
07429 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07430 {
07431    int cmd;
07432    char *buf;
07433 
07434    buf = alloca(strlen(box)+2); 
07435    strcpy(buf, box);
07436    strcat(buf,"s");
07437 
07438    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07439       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07440       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07441    } else {
07442       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07443       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07444    }
07445 }
07446 
07447 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07448 {
07449    int cmd;
07450 
07451    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07452       if (!strcasecmp(box, "vm-INBOX"))
07453          cmd = ast_play_and_wait(chan, "vm-new-e");
07454       else
07455          cmd = ast_play_and_wait(chan, "vm-old-e");
07456       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07457    } else {
07458       cmd = ast_play_and_wait(chan, "vm-messages");
07459       return cmd ? cmd : ast_play_and_wait(chan, box);
07460    }
07461 }
07462 
07463 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07464 {
07465    int cmd;
07466 
07467    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07468       cmd = ast_play_and_wait(chan, "vm-messages");
07469       return cmd ? cmd : ast_play_and_wait(chan, box);
07470    } else {
07471       cmd = ast_play_and_wait(chan, box);
07472       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07473    }
07474 }
07475 
07476 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07477 {
07478    int cmd;
07479 
07480    if (  !strncasecmp(chan->language, "it", 2) ||
07481         !strncasecmp(chan->language, "es", 2) ||
07482         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07483       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07484       return cmd ? cmd : ast_play_and_wait(chan, box);
07485    } else if (!strncasecmp(chan->language, "gr", 2)) {
07486       return vm_play_folder_name_gr(chan, box);
07487    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07488       return ast_play_and_wait(chan, box);
07489    } else if (!strncasecmp(chan->language, "pl", 2)) {
07490       return vm_play_folder_name_pl(chan, box);
07491    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07492       return vm_play_folder_name_ua(chan, box);
07493    } else {  /* Default English */
07494       cmd = ast_play_and_wait(chan, box);
07495       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07496    }
07497 }
07498 
07499 /* GREEK SYNTAX
07500    In greek the plural for old/new is
07501    different so we need the following files
07502    We also need vm-denExeteMynhmata because
07503    this syntax is different.
07504 
07505    -> vm-Olds.wav : "Palia"
07506    -> vm-INBOXs.wav : "Nea"
07507    -> vm-denExeteMynhmata : "den exete mynhmata"
07508 */
07509 
07510 
07511 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07512 {
07513    int res = 0;
07514 
07515    if (vms->newmessages) {
07516       res = ast_play_and_wait(chan, "vm-youhave");
07517       if (!res) 
07518          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07519       if (!res) {
07520          if ((vms->newmessages == 1)) {
07521             res = ast_play_and_wait(chan, "vm-INBOX");
07522             if (!res)
07523                res = ast_play_and_wait(chan, "vm-message");
07524          } else {
07525             res = ast_play_and_wait(chan, "vm-INBOXs");
07526             if (!res)
07527                res = ast_play_and_wait(chan, "vm-messages");
07528          }
07529       }
07530    } else if (vms->oldmessages){
07531       res = ast_play_and_wait(chan, "vm-youhave");
07532       if (!res)
07533          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07534       if ((vms->oldmessages == 1)){
07535          res = ast_play_and_wait(chan, "vm-Old");
07536          if (!res)
07537             res = ast_play_and_wait(chan, "vm-message");
07538       } else {
07539          res = ast_play_and_wait(chan, "vm-Olds");
07540          if (!res)
07541             res = ast_play_and_wait(chan, "vm-messages");
07542       }
07543    } else if (!vms->oldmessages && !vms->newmessages) 
07544       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07545    return res;
07546 }
07547 
07548 /* Version of vm_intro() designed to work for many languages.
07549  *
07550  * It is hoped that this function can prevent the proliferation of 
07551  * language-specific vm_intro() functions and in time replace the language-
07552  * specific functions which already exist.  An examination of the language-
07553  * specific functions revealed that they all corrected the same deficiencies
07554  * in vm_intro_en() (which was the default function). Namely:
07555  *
07556  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07557  *     wording of the voicemail greeting hides this problem.  For example,
07558  *     vm-INBOX contains only the word "new".  This means that both of these
07559  *     sequences produce valid utterances:
07560  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07561  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07562  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07563  *     in many languages) the first utterance becomes "you have 1 the new message".
07564  *  2) The function contains hardcoded rules for pluralizing the word "message".
07565  *     These rules are correct for English, but not for many other languages.
07566  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07567  *     required in many languages.
07568  *  4) The gender of the word for "message" is not specified. This is a problem
07569  *     because in many languages the gender of the number in phrases such
07570  *     as "you have one new message" must match the gender of the word
07571  *     meaning "message".
07572  *
07573  * Fixing these problems for each new language has meant duplication of effort.
07574  * This new function solves the problems in the following general ways:
07575  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07576  *     and vm-Old respectively for those languages where it makes sense.
07577  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07578  *     on vm-message.
07579  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07580  *     prefix on vm-new and vm-old (none for English).
07581  *  4) Pass the gender of the language's word for "message" as an agument to
07582  *     this function which is can in turn pass on to the functions which 
07583  *     say numbers and put endings on nounds and adjectives.
07584  *
07585  * All languages require these messages:
07586  *  vm-youhave    "You have..."
07587  *  vm-and     "and"
07588  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07589  *
07590  * To use it for English, you will need these additional sound files:
07591  *  vm-new     "new"
07592  *  vm-message    "message", singular
07593  *  vm-messages      "messages", plural
07594  *
07595  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07596  *
07597  *  vm-newn    "novoye" (singular, neuter)
07598  *  vm-newx    "novikh" (counting plural form, genative plural)
07599  *  vm-message    "sobsheniye" (singular form)
07600  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07601  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07602  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07603  *  digits/2n     "dva" (neuter singular)
07604  */
07605 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07606 {
07607    int res;
07608    int lastnum = 0;
07609 
07610    res = ast_play_and_wait(chan, "vm-youhave");
07611 
07612    if (!res && vms->newmessages) {
07613       lastnum = vms->newmessages;
07614 
07615       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07616          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07617       }
07618 
07619       if (!res && vms->oldmessages) {
07620          res = ast_play_and_wait(chan, "vm-and");
07621       }
07622    }
07623 
07624    if (!res && vms->oldmessages) {
07625       lastnum = vms->oldmessages;
07626 
07627       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07628          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07629       }
07630    }
07631 
07632    if (!res) {
07633       if (lastnum == 0) {
07634          res = ast_play_and_wait(chan, "vm-no");
07635       }
07636       if (!res) {
07637          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07638       }
07639    }
07640 
07641    return res;
07642 }
07643 
07644 /* Default Hebrew syntax */
07645 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07646 {
07647    int res = 0;
07648 
07649    /* Introduce messages they have */
07650    if (!res) {
07651       if ((vms->newmessages) || (vms->oldmessages)) {
07652          res = ast_play_and_wait(chan, "vm-youhave");
07653       }
07654       /*
07655        * The word "shtei" refers to the number 2 in hebrew when performing a count
07656        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07657        * an element, this is one of them.
07658        */
07659       if (vms->newmessages) {
07660          if (!res) {
07661             if (vms->newmessages == 1) {
07662                res = ast_play_and_wait(chan, "vm-INBOX1");
07663             } else {
07664                if (vms->newmessages == 2) {
07665                   res = ast_play_and_wait(chan, "vm-shtei");
07666                } else {
07667                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07668                }
07669                res = ast_play_and_wait(chan, "vm-INBOX");
07670             }
07671          }
07672          if (vms->oldmessages && !res) {
07673             res = ast_play_and_wait(chan, "vm-and");
07674             if (vms->oldmessages == 1) {
07675                res = ast_play_and_wait(chan, "vm-Old1");
07676             } else {
07677                if (vms->oldmessages == 2) {
07678                   res = ast_play_and_wait(chan, "vm-shtei");
07679                } else {
07680                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07681                }
07682                res = ast_play_and_wait(chan, "vm-Old");
07683             }
07684          }
07685       }
07686       if (!res && vms->oldmessages && !vms->newmessages) {
07687          if (!res) {
07688             if (vms->oldmessages == 1) {
07689                res = ast_play_and_wait(chan, "vm-Old1");
07690             } else {
07691                if (vms->oldmessages == 2) {
07692                   res = ast_play_and_wait(chan, "vm-shtei");
07693                } else {
07694                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07695                }
07696                res = ast_play_and_wait(chan, "vm-Old");
07697             }
07698          }
07699       }
07700       if (!res) {
07701          if (!vms->oldmessages && !vms->newmessages) {
07702             if (!res) {
07703                res = ast_play_and_wait(chan, "vm-nomessages");
07704             }
07705          }
07706       }
07707    }
07708    return res;
07709 }
07710    
07711 /* Default English syntax */
07712 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07713 {
07714    int res;
07715 
07716    /* Introduce messages they have */
07717    res = ast_play_and_wait(chan, "vm-youhave");
07718    if (!res) {
07719       if (vms->urgentmessages) {
07720          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07721          if (!res)
07722             res = ast_play_and_wait(chan, "vm-Urgent");
07723          if ((vms->oldmessages || vms->newmessages) && !res) {
07724             res = ast_play_and_wait(chan, "vm-and");
07725          } else if (!res) {
07726             if ((vms->urgentmessages == 1))
07727                res = ast_play_and_wait(chan, "vm-message");
07728             else
07729                res = ast_play_and_wait(chan, "vm-messages");
07730          }
07731       }
07732       if (vms->newmessages) {
07733          res = say_and_wait(chan, vms->newmessages, chan->language);
07734          if (!res)
07735             res = ast_play_and_wait(chan, "vm-INBOX");
07736          if (vms->oldmessages && !res)
07737             res = ast_play_and_wait(chan, "vm-and");
07738          else if (!res) {
07739             if ((vms->newmessages == 1))
07740                res = ast_play_and_wait(chan, "vm-message");
07741             else
07742                res = ast_play_and_wait(chan, "vm-messages");
07743          }
07744             
07745       }
07746       if (!res && vms->oldmessages) {
07747          res = say_and_wait(chan, vms->oldmessages, chan->language);
07748          if (!res)
07749             res = ast_play_and_wait(chan, "vm-Old");
07750          if (!res) {
07751             if (vms->oldmessages == 1)
07752                res = ast_play_and_wait(chan, "vm-message");
07753             else
07754                res = ast_play_and_wait(chan, "vm-messages");
07755          }
07756       }
07757       if (!res) {
07758          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07759             res = ast_play_and_wait(chan, "vm-no");
07760             if (!res)
07761                res = ast_play_and_wait(chan, "vm-messages");
07762          }
07763       }
07764    }
07765    return res;
07766 }
07767 
07768 /* ITALIAN syntax */
07769 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07770 {
07771    /* Introduce messages they have */
07772    int res;
07773    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07774       res = ast_play_and_wait(chan, "vm-no") ||
07775          ast_play_and_wait(chan, "vm-message");
07776    else
07777       res = ast_play_and_wait(chan, "vm-youhave");
07778    if (!res && vms->newmessages) {
07779       res = (vms->newmessages == 1) ?
07780          ast_play_and_wait(chan, "digits/un") ||
07781          ast_play_and_wait(chan, "vm-nuovo") ||
07782          ast_play_and_wait(chan, "vm-message") :
07783          /* 2 or more new messages */
07784          say_and_wait(chan, vms->newmessages, chan->language) ||
07785          ast_play_and_wait(chan, "vm-nuovi") ||
07786          ast_play_and_wait(chan, "vm-messages");
07787       if (!res && vms->oldmessages)
07788          res = ast_play_and_wait(chan, "vm-and");
07789    }
07790    if (!res && vms->oldmessages) {
07791       res = (vms->oldmessages == 1) ?
07792          ast_play_and_wait(chan, "digits/un") ||
07793          ast_play_and_wait(chan, "vm-vecchio") ||
07794          ast_play_and_wait(chan, "vm-message") :
07795          /* 2 or more old messages */
07796          say_and_wait(chan, vms->oldmessages, chan->language) ||
07797          ast_play_and_wait(chan, "vm-vecchi") ||
07798          ast_play_and_wait(chan, "vm-messages");
07799    }
07800    return res;
07801 }
07802 
07803 /* POLISH syntax */
07804 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07805 {
07806    /* Introduce messages they have */
07807    int res;
07808    div_t num;
07809 
07810    if (!vms->oldmessages && !vms->newmessages) {
07811       res = ast_play_and_wait(chan, "vm-no");
07812       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07813       return res;
07814    } else {
07815       res = ast_play_and_wait(chan, "vm-youhave");
07816    }
07817 
07818    if (vms->newmessages) {
07819       num = div(vms->newmessages, 10);
07820       if (vms->newmessages == 1) {
07821          res = ast_play_and_wait(chan, "digits/1-a");
07822          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07823          res = res ? res : ast_play_and_wait(chan, "vm-message");
07824       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07825          if (num.rem == 2) {
07826             if (!num.quot) {
07827                res = ast_play_and_wait(chan, "digits/2-ie");
07828             } else {
07829                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07830                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07831             }
07832          } else {
07833             res = say_and_wait(chan, vms->newmessages, chan->language);
07834          }
07835          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07836          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07837       } else {
07838          res = say_and_wait(chan, vms->newmessages, chan->language);
07839          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07840          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07841       }
07842       if (!res && vms->oldmessages)
07843          res = ast_play_and_wait(chan, "vm-and");
07844    }
07845    if (!res && vms->oldmessages) {
07846       num = div(vms->oldmessages, 10);
07847       if (vms->oldmessages == 1) {
07848          res = ast_play_and_wait(chan, "digits/1-a");
07849          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07850          res = res ? res : ast_play_and_wait(chan, "vm-message");
07851       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07852          if (num.rem == 2) {
07853             if (!num.quot) {
07854                res = ast_play_and_wait(chan, "digits/2-ie");
07855             } else {
07856                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07857                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07858             }
07859          } else {
07860             res = say_and_wait(chan, vms->oldmessages, chan->language);
07861          }
07862          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07863          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07864       } else {
07865          res = say_and_wait(chan, vms->oldmessages, chan->language);
07866          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07867          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07868       }
07869    }
07870 
07871    return res;
07872 }
07873 
07874 /* SWEDISH syntax */
07875 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07876 {
07877    /* Introduce messages they have */
07878    int res;
07879 
07880    res = ast_play_and_wait(chan, "vm-youhave");
07881    if (res)
07882       return res;
07883 
07884    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07885       res = ast_play_and_wait(chan, "vm-no");
07886       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07887       return res;
07888    }
07889 
07890    if (vms->newmessages) {
07891       if ((vms->newmessages == 1)) {
07892          res = ast_play_and_wait(chan, "digits/ett");
07893          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07894          res = res ? res : ast_play_and_wait(chan, "vm-message");
07895       } else {
07896          res = say_and_wait(chan, vms->newmessages, chan->language);
07897          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07898          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07899       }
07900       if (!res && vms->oldmessages)
07901          res = ast_play_and_wait(chan, "vm-and");
07902    }
07903    if (!res && vms->oldmessages) {
07904       if (vms->oldmessages == 1) {
07905          res = ast_play_and_wait(chan, "digits/ett");
07906          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07907          res = res ? res : ast_play_and_wait(chan, "vm-message");
07908       } else {
07909          res = say_and_wait(chan, vms->oldmessages, chan->language);
07910          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07911          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07912       }
07913    }
07914 
07915    return res;
07916 }
07917 
07918 /* NORWEGIAN syntax */
07919 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07920 {
07921    /* Introduce messages they have */
07922    int res;
07923 
07924    res = ast_play_and_wait(chan, "vm-youhave");
07925    if (res)
07926       return res;
07927 
07928    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07929       res = ast_play_and_wait(chan, "vm-no");
07930       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07931       return res;
07932    }
07933 
07934    if (vms->newmessages) {
07935       if ((vms->newmessages == 1)) {
07936          res = ast_play_and_wait(chan, "digits/1");
07937          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07938          res = res ? res : ast_play_and_wait(chan, "vm-message");
07939       } else {
07940          res = say_and_wait(chan, vms->newmessages, chan->language);
07941          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07942          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07943       }
07944       if (!res && vms->oldmessages)
07945          res = ast_play_and_wait(chan, "vm-and");
07946    }
07947    if (!res && vms->oldmessages) {
07948       if (vms->oldmessages == 1) {
07949          res = ast_play_and_wait(chan, "digits/1");
07950          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07951          res = res ? res : ast_play_and_wait(chan, "vm-message");
07952       } else {
07953          res = say_and_wait(chan, vms->oldmessages, chan->language);
07954          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07955          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07956       }
07957    }
07958 
07959    return res;
07960 }
07961 
07962 /* GERMAN syntax */
07963 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07964 {
07965    /* Introduce messages they have */
07966    int res;
07967    res = ast_play_and_wait(chan, "vm-youhave");
07968    if (!res) {
07969       if (vms->newmessages) {
07970          if ((vms->newmessages == 1))
07971             res = ast_play_and_wait(chan, "digits/1F");
07972          else
07973             res = say_and_wait(chan, vms->newmessages, chan->language);
07974          if (!res)
07975             res = ast_play_and_wait(chan, "vm-INBOX");
07976          if (vms->oldmessages && !res)
07977             res = ast_play_and_wait(chan, "vm-and");
07978          else if (!res) {
07979             if ((vms->newmessages == 1))
07980                res = ast_play_and_wait(chan, "vm-message");
07981             else
07982                res = ast_play_and_wait(chan, "vm-messages");
07983          }
07984             
07985       }
07986       if (!res && vms->oldmessages) {
07987          if (vms->oldmessages == 1)
07988             res = ast_play_and_wait(chan, "digits/1F");
07989          else
07990             res = say_and_wait(chan, vms->oldmessages, chan->language);
07991          if (!res)
07992             res = ast_play_and_wait(chan, "vm-Old");
07993          if (!res) {
07994             if (vms->oldmessages == 1)
07995                res = ast_play_and_wait(chan, "vm-message");
07996             else
07997                res = ast_play_and_wait(chan, "vm-messages");
07998          }
07999       }
08000       if (!res) {
08001          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08002             res = ast_play_and_wait(chan, "vm-no");
08003             if (!res)
08004                res = ast_play_and_wait(chan, "vm-messages");
08005          }
08006       }
08007    }
08008    return res;
08009 }
08010 
08011 /* SPANISH syntax */
08012 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
08013 {
08014    /* Introduce messages they have */
08015    int res;
08016    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08017       res = ast_play_and_wait(chan, "vm-youhaveno");
08018       if (!res)
08019          res = ast_play_and_wait(chan, "vm-messages");
08020    } else {
08021       res = ast_play_and_wait(chan, "vm-youhave");
08022    }
08023    if (!res) {
08024       if (vms->newmessages) {
08025          if (!res) {
08026             if ((vms->newmessages == 1)) {
08027                res = ast_play_and_wait(chan, "digits/1M");
08028                if (!res)
08029                   res = ast_play_and_wait(chan, "vm-message");
08030                if (!res)
08031                   res = ast_play_and_wait(chan, "vm-INBOXs");
08032             } else {
08033                res = say_and_wait(chan, vms->newmessages, chan->language);
08034                if (!res)
08035                   res = ast_play_and_wait(chan, "vm-messages");
08036                if (!res)
08037                   res = ast_play_and_wait(chan, "vm-INBOX");
08038             }
08039          }
08040          if (vms->oldmessages && !res)
08041             res = ast_play_and_wait(chan, "vm-and");
08042       }
08043       if (vms->oldmessages) {
08044          if (!res) {
08045             if (vms->oldmessages == 1) {
08046                res = ast_play_and_wait(chan, "digits/1M");
08047                if (!res)
08048                   res = ast_play_and_wait(chan, "vm-message");
08049                if (!res)
08050                   res = ast_play_and_wait(chan, "vm-Olds");
08051             } else {
08052                res = say_and_wait(chan, vms->oldmessages, chan->language);
08053                if (!res)
08054                   res = ast_play_and_wait(chan, "vm-messages");
08055                if (!res)
08056                   res = ast_play_and_wait(chan, "vm-Old");
08057             }
08058          }
08059       }
08060    }
08061 return res;
08062 }
08063 
08064 /* BRAZILIAN PORTUGUESE syntax */
08065 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
08066    /* Introduce messages they have */
08067    int res;
08068    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08069       res = ast_play_and_wait(chan, "vm-nomessages");
08070       return res;
08071    } else {
08072       res = ast_play_and_wait(chan, "vm-youhave");
08073    }
08074    if (vms->newmessages) {
08075       if (!res)
08076          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08077       if ((vms->newmessages == 1)) {
08078          if (!res)
08079             res = ast_play_and_wait(chan, "vm-message");
08080          if (!res)
08081             res = ast_play_and_wait(chan, "vm-INBOXs");
08082       } else {
08083          if (!res)
08084             res = ast_play_and_wait(chan, "vm-messages");
08085          if (!res)
08086             res = ast_play_and_wait(chan, "vm-INBOX");
08087       }
08088       if (vms->oldmessages && !res)
08089          res = ast_play_and_wait(chan, "vm-and");
08090    }
08091    if (vms->oldmessages) {
08092       if (!res)
08093          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08094       if (vms->oldmessages == 1) {
08095          if (!res)
08096             res = ast_play_and_wait(chan, "vm-message");
08097          if (!res)
08098             res = ast_play_and_wait(chan, "vm-Olds");
08099       } else {
08100          if (!res)
08101             res = ast_play_and_wait(chan, "vm-messages");
08102          if (!res)
08103             res = ast_play_and_wait(chan, "vm-Old");
08104       }
08105    }
08106    return res;
08107 }
08108 
08109 /* FRENCH syntax */
08110 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
08111 {
08112    /* Introduce messages they have */
08113    int res;
08114    res = ast_play_and_wait(chan, "vm-youhave");
08115    if (!res) {
08116       if (vms->newmessages) {
08117          res = say_and_wait(chan, vms->newmessages, chan->language);
08118          if (!res)
08119             res = ast_play_and_wait(chan, "vm-INBOX");
08120          if (vms->oldmessages && !res)
08121             res = ast_play_and_wait(chan, "vm-and");
08122          else if (!res) {
08123             if ((vms->newmessages == 1))
08124                res = ast_play_and_wait(chan, "vm-message");
08125             else
08126                res = ast_play_and_wait(chan, "vm-messages");
08127          }
08128             
08129       }
08130       if (!res && vms->oldmessages) {
08131          res = say_and_wait(chan, vms->oldmessages, chan->language);
08132          if (!res)
08133             res = ast_play_and_wait(chan, "vm-Old");
08134          if (!res) {
08135             if (vms->oldmessages == 1)
08136                res = ast_play_and_wait(chan, "vm-message");
08137             else
08138                res = ast_play_and_wait(chan, "vm-messages");
08139          }
08140       }
08141       if (!res) {
08142          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08143             res = ast_play_and_wait(chan, "vm-no");
08144             if (!res)
08145                res = ast_play_and_wait(chan, "vm-messages");
08146          }
08147       }
08148    }
08149    return res;
08150 }
08151 
08152 /* DUTCH syntax */
08153 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
08154 {
08155    /* Introduce messages they have */
08156    int res;
08157    res = ast_play_and_wait(chan, "vm-youhave");
08158    if (!res) {
08159       if (vms->newmessages) {
08160          res = say_and_wait(chan, vms->newmessages, chan->language);
08161          if (!res) {
08162             if (vms->newmessages == 1)
08163                res = ast_play_and_wait(chan, "vm-INBOXs");
08164             else
08165                res = ast_play_and_wait(chan, "vm-INBOX");
08166          }
08167          if (vms->oldmessages && !res)
08168             res = ast_play_and_wait(chan, "vm-and");
08169          else if (!res) {
08170             if ((vms->newmessages == 1))
08171                res = ast_play_and_wait(chan, "vm-message");
08172             else
08173                res = ast_play_and_wait(chan, "vm-messages");
08174          }
08175             
08176       }
08177       if (!res && vms->oldmessages) {
08178          res = say_and_wait(chan, vms->oldmessages, chan->language);
08179          if (!res) {
08180             if (vms->oldmessages == 1)
08181                res = ast_play_and_wait(chan, "vm-Olds");
08182             else
08183                res = ast_play_and_wait(chan, "vm-Old");
08184          }
08185          if (!res) {
08186             if (vms->oldmessages == 1)
08187                res = ast_play_and_wait(chan, "vm-message");
08188             else
08189                res = ast_play_and_wait(chan, "vm-messages");
08190          }
08191       }
08192       if (!res) {
08193          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08194             res = ast_play_and_wait(chan, "vm-no");
08195             if (!res)
08196                res = ast_play_and_wait(chan, "vm-messages");
08197          }
08198       }
08199    }
08200    return res;
08201 }
08202 
08203 /* PORTUGUESE syntax */
08204 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08205 {
08206    /* Introduce messages they have */
08207    int res;
08208    res = ast_play_and_wait(chan, "vm-youhave");
08209    if (!res) {
08210       if (vms->newmessages) {
08211          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08212          if (!res) {
08213             if ((vms->newmessages == 1)) {
08214                res = ast_play_and_wait(chan, "vm-message");
08215                if (!res)
08216                   res = ast_play_and_wait(chan, "vm-INBOXs");
08217             } else {
08218                res = ast_play_and_wait(chan, "vm-messages");
08219                if (!res)
08220                   res = ast_play_and_wait(chan, "vm-INBOX");
08221             }
08222          }
08223          if (vms->oldmessages && !res)
08224             res = ast_play_and_wait(chan, "vm-and");
08225       }
08226       if (!res && vms->oldmessages) {
08227          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08228          if (!res) {
08229             if (vms->oldmessages == 1) {
08230                res = ast_play_and_wait(chan, "vm-message");
08231                if (!res)
08232                   res = ast_play_and_wait(chan, "vm-Olds");
08233             } else {
08234                res = ast_play_and_wait(chan, "vm-messages");
08235                if (!res)
08236                   res = ast_play_and_wait(chan, "vm-Old");
08237             }
08238          }
08239       }
08240       if (!res) {
08241          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08242             res = ast_play_and_wait(chan, "vm-no");
08243             if (!res)
08244                res = ast_play_and_wait(chan, "vm-messages");
08245          }
08246       }
08247    }
08248    return res;
08249 }
08250 
08251 
08252 /* CZECH syntax */
08253 /* in czech there must be declension of word new and message
08254  * czech        : english        : czech      : english
08255  * --------------------------------------------------------
08256  * vm-youhave   : you have 
08257  * vm-novou     : one new        : vm-zpravu  : message
08258  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08259  * vm-novych    : 5-infinite new : vm-zprav   : messages
08260  * vm-starou   : one old
08261  * vm-stare     : 2-4 old 
08262  * vm-starych   : 5-infinite old
08263  * jednu        : one   - falling 4. 
08264  * vm-no        : no  ( no messages )
08265  */
08266 
08267 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08268 {
08269    int res;
08270    res = ast_play_and_wait(chan, "vm-youhave");
08271    if (!res) {
08272       if (vms->newmessages) {
08273          if (vms->newmessages == 1) {
08274             res = ast_play_and_wait(chan, "digits/jednu");
08275          } else {
08276             res = say_and_wait(chan, vms->newmessages, chan->language);
08277          }
08278          if (!res) {
08279             if ((vms->newmessages == 1))
08280                res = ast_play_and_wait(chan, "vm-novou");
08281             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08282                res = ast_play_and_wait(chan, "vm-nove");
08283             if (vms->newmessages > 4)
08284                res = ast_play_and_wait(chan, "vm-novych");
08285          }
08286          if (vms->oldmessages && !res)
08287             res = ast_play_and_wait(chan, "vm-and");
08288          else if (!res) {
08289             if ((vms->newmessages == 1))
08290                res = ast_play_and_wait(chan, "vm-zpravu");
08291             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08292                res = ast_play_and_wait(chan, "vm-zpravy");
08293             if (vms->newmessages > 4)
08294                res = ast_play_and_wait(chan, "vm-zprav");
08295          }
08296       }
08297       if (!res && vms->oldmessages) {
08298          res = say_and_wait(chan, vms->oldmessages, chan->language);
08299          if (!res) {
08300             if ((vms->oldmessages == 1))
08301                res = ast_play_and_wait(chan, "vm-starou");
08302             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08303                res = ast_play_and_wait(chan, "vm-stare");
08304             if (vms->oldmessages > 4)
08305                res = ast_play_and_wait(chan, "vm-starych");
08306          }
08307          if (!res) {
08308             if ((vms->oldmessages == 1))
08309                res = ast_play_and_wait(chan, "vm-zpravu");
08310             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08311                res = ast_play_and_wait(chan, "vm-zpravy");
08312             if (vms->oldmessages > 4)
08313                res = ast_play_and_wait(chan, "vm-zprav");
08314          }
08315       }
08316       if (!res) {
08317          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08318             res = ast_play_and_wait(chan, "vm-no");
08319             if (!res)
08320                res = ast_play_and_wait(chan, "vm-zpravy");
08321          }
08322       }
08323    }
08324    return res;
08325 }
08326 
08327 /* CHINESE (Taiwan) syntax */
08328 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08329 {
08330    int res;
08331    /* Introduce messages they have */
08332    res = ast_play_and_wait(chan, "vm-you");
08333 
08334    if (!res && vms->newmessages) {
08335       res = ast_play_and_wait(chan, "vm-have");
08336       if (!res)
08337          res = say_and_wait(chan, vms->newmessages, chan->language);
08338       if (!res)
08339          res = ast_play_and_wait(chan, "vm-tong");
08340       if (!res)
08341          res = ast_play_and_wait(chan, "vm-INBOX");
08342       if (vms->oldmessages && !res)
08343          res = ast_play_and_wait(chan, "vm-and");
08344       else if (!res) 
08345          res = ast_play_and_wait(chan, "vm-messages");
08346    }
08347    if (!res && vms->oldmessages) {
08348       res = ast_play_and_wait(chan, "vm-have");
08349       if (!res)
08350          res = say_and_wait(chan, vms->oldmessages, chan->language);
08351       if (!res)
08352          res = ast_play_and_wait(chan, "vm-tong");
08353       if (!res)
08354          res = ast_play_and_wait(chan, "vm-Old");
08355       if (!res)
08356          res = ast_play_and_wait(chan, "vm-messages");
08357    }
08358    if (!res && !vms->oldmessages && !vms->newmessages) {
08359       res = ast_play_and_wait(chan, "vm-haveno");
08360       if (!res)
08361          res = ast_play_and_wait(chan, "vm-messages");
08362    }
08363    return res;
08364 }
08365 
08366 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08367 {
08368    char prefile[256];
08369    
08370    /* Notify the user that the temp greeting is set and give them the option to remove it */
08371    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08372    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08373       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08374       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08375          ast_play_and_wait(chan, "vm-tempgreetactive");
08376       }
08377       DISPOSE(prefile, -1);
08378    }
08379 
08380    /* Play voicemail intro - syntax is different for different languages */
08381    if (0) {
08382    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08383       return vm_intro_cs(chan, vms);
08384    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08385       static int deprecation_warning = 0;
08386       if (deprecation_warning++ % 10 == 0) {
08387          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08388       }
08389       return vm_intro_cs(chan, vms);
08390    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08391       return vm_intro_de(chan, vms);
08392    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08393       return vm_intro_es(chan, vms);
08394    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08395       return vm_intro_fr(chan, vms);
08396    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08397       return vm_intro_gr(chan, vms);
08398    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08399       return vm_intro_he(chan, vms);
08400    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08401       return vm_intro_it(chan, vms);
08402    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08403       return vm_intro_nl(chan, vms);
08404    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08405       return vm_intro_no(chan, vms);
08406    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08407       return vm_intro_pl(chan, vms);
08408    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08409       return vm_intro_pt_BR(chan, vms);
08410    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08411       return vm_intro_pt(chan, vms);
08412    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08413       return vm_intro_multilang(chan, vms, "n");
08414    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08415       return vm_intro_se(chan, vms);
08416    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08417       return vm_intro_multilang(chan, vms, "n");
08418    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08419       return vm_intro_zh(chan, vms);
08420    } else {                                             /* Default to ENGLISH */
08421       return vm_intro_en(chan, vms);
08422    }
08423 }
08424 
08425 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08426 {
08427    int res = 0;
08428    /* Play instructions and wait for new command */
08429    while (!res) {
08430       if (vms->starting) {
08431          if (vms->lastmsg > -1) {
08432             if (skipadvanced)
08433                res = ast_play_and_wait(chan, "vm-onefor-full");
08434             else
08435                res = ast_play_and_wait(chan, "vm-onefor");
08436             if (!res)
08437                res = vm_play_folder_name(chan, vms->vmbox);
08438          }
08439          if (!res) {
08440             if (skipadvanced)
08441                res = ast_play_and_wait(chan, "vm-opts-full");
08442             else
08443                res = ast_play_and_wait(chan, "vm-opts");
08444          }
08445       } else {
08446          /* Added for additional help */
08447          if (skipadvanced) {
08448             res = ast_play_and_wait(chan, "vm-onefor-full");
08449             if (!res)
08450                res = vm_play_folder_name(chan, vms->vmbox);
08451             res = ast_play_and_wait(chan, "vm-opts-full");
08452          }
08453          /* Logic:
08454           * If the current message is not the first OR
08455           * if we're listening to the first new message and there are
08456           * also urgent messages, then prompt for navigation to the
08457           * previous message
08458           */
08459          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08460             res = ast_play_and_wait(chan, "vm-prev");
08461          }
08462          if (!res && !skipadvanced)
08463             res = ast_play_and_wait(chan, "vm-advopts");
08464          if (!res)
08465             res = ast_play_and_wait(chan, "vm-repeat");
08466          /* Logic:
08467           * If we're not listening to the last message OR
08468           * we're listening to the last urgent message and there are
08469           * also new non-urgent messages, then prompt for navigation
08470           * to the next message
08471           */
08472          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08473             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08474             res = ast_play_and_wait(chan, "vm-next");
08475          }
08476          if (!res) {
08477             if (!vms->deleted[vms->curmsg])
08478                res = ast_play_and_wait(chan, "vm-delete");
08479             else
08480                res = ast_play_and_wait(chan, "vm-undelete");
08481             if (!res)
08482                res = ast_play_and_wait(chan, "vm-toforward");
08483             if (!res)
08484                res = ast_play_and_wait(chan, "vm-savemessage");
08485          }
08486       }
08487       if (!res) {
08488          res = ast_play_and_wait(chan, "vm-helpexit");
08489       }
08490       if (!res)
08491          res = ast_waitfordigit(chan, 6000);
08492       if (!res) {
08493          vms->repeats++;
08494          if (vms->repeats > 2) {
08495             res = 't';
08496          }
08497       }
08498    }
08499    return res;
08500 }
08501 
08502 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08503 {
08504    int res = 0;
08505    /* Play instructions and wait for new command */
08506    while (!res) {
08507       if (vms->lastmsg > -1) {
08508          res = ast_play_and_wait(chan, "vm-listen");
08509          if (!res)
08510             res = vm_play_folder_name(chan, vms->vmbox);
08511          if (!res)
08512             res = ast_play_and_wait(chan, "press");
08513          if (!res)
08514             res = ast_play_and_wait(chan, "digits/1");
08515       }
08516       if (!res)
08517          res = ast_play_and_wait(chan, "vm-opts");
08518       if (!res) {
08519          vms->starting = 0;
08520          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08521       }
08522    }
08523    return res;
08524 }
08525 
08526 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08527 {
08528    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08529       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08530    } else {             /* Default to ENGLISH */
08531       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08532    }
08533 }
08534 
08535 
08536 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08537 {
08538    int cmd = 0;
08539    int duration = 0;
08540    int tries = 0;
08541    char newpassword[80] = "";
08542    char newpassword2[80] = "";
08543    char prefile[PATH_MAX] = "";
08544    unsigned char buf[256];
08545    int bytes=0;
08546 
08547    if (ast_adsi_available(chan)) {
08548       bytes += adsi_logo(buf + bytes);
08549       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08550       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08551       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08552       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08553       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08554    }
08555 
08556    /* First, have the user change their password 
08557       so they won't get here again */
08558    for (;;) {
08559       newpassword[1] = '\0';
08560       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08561       if (cmd == '#')
08562          newpassword[0] = '\0';
08563       if (cmd < 0 || cmd == 't' || cmd == '#')
08564          return cmd;
08565       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08566       if (cmd < 0 || cmd == 't' || cmd == '#')
08567          return cmd;
08568       cmd = check_password(vmu, newpassword); /* perform password validation */
08569       if (cmd != 0) {
08570          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08571          cmd = ast_play_and_wait(chan, vm_invalid_password);
08572       } else {
08573          newpassword2[1] = '\0';
08574          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08575          if (cmd == '#')
08576             newpassword2[0] = '\0';
08577          if (cmd < 0 || cmd == 't' || cmd == '#')
08578             return cmd;
08579          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08580          if (cmd < 0 || cmd == 't' || cmd == '#')
08581             return cmd;
08582          if (!strcmp(newpassword, newpassword2))
08583             break;
08584          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08585          cmd = ast_play_and_wait(chan, vm_mismatch);
08586       }
08587       if (++tries == 3)
08588          return -1;
08589       if (cmd != 0) {
08590          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08591       }
08592    }
08593    if (pwdchange & PWDCHANGE_INTERNAL)
08594       vm_change_password(vmu, newpassword);
08595    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08596       vm_change_password_shell(vmu, newpassword);
08597 
08598    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08599    cmd = ast_play_and_wait(chan, vm_passchanged);
08600 
08601    /* If forcename is set, have the user record their name */  
08602    if (ast_test_flag(vmu, VM_FORCENAME)) {
08603       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08604       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08605          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08606          if (cmd < 0 || cmd == 't' || cmd == '#')
08607             return cmd;
08608       }
08609    }
08610 
08611    /* If forcegreetings is set, have the user record their greetings */
08612    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08613       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08614       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08615          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08616          if (cmd < 0 || cmd == 't' || cmd == '#')
08617             return cmd;
08618       }
08619 
08620       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08621       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08622          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08623          if (cmd < 0 || cmd == 't' || cmd == '#')
08624             return cmd;
08625       }
08626    }
08627 
08628    return cmd;
08629 }
08630 
08631 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08632 {
08633    int cmd = 0;
08634    int retries = 0;
08635    int duration = 0;
08636    char newpassword[80] = "";
08637    char newpassword2[80] = "";
08638    char prefile[PATH_MAX] = "";
08639    unsigned char buf[256];
08640    int bytes=0;
08641 
08642    if (ast_adsi_available(chan)) {
08643       bytes += adsi_logo(buf + bytes);
08644       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08645       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08646       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08647       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08648       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08649    }
08650    while ((cmd >= 0) && (cmd != 't')) {
08651       if (cmd)
08652          retries = 0;
08653       switch (cmd) {
08654       case '1': /* Record your unavailable message */
08655          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08656          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08657          break;
08658       case '2':  /* Record your busy message */
08659          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08660          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08661          break;
08662       case '3': /* Record greeting */
08663          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08664          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08665          break;
08666       case '4':  /* manage the temporary greeting */
08667          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08668          break;
08669       case '5': /* change password */
08670          if (vmu->password[0] == '-') {
08671             cmd = ast_play_and_wait(chan, "vm-no");
08672             break;
08673          }
08674          newpassword[1] = '\0';
08675          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08676          if (cmd == '#')
08677             newpassword[0] = '\0';
08678          else {
08679             if (cmd < 0)
08680                break;
08681             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08682                break;
08683             }
08684          }
08685          cmd = check_password(vmu, newpassword); /* perform password validation */
08686          if (cmd != 0) {
08687             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08688             cmd = ast_play_and_wait(chan, vm_invalid_password);
08689             if (!cmd) {
08690                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08691             }
08692             break;
08693          }
08694          newpassword2[1] = '\0';
08695          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08696          if (cmd == '#')
08697             newpassword2[0] = '\0';
08698          else {
08699             if (cmd < 0)
08700                break;
08701 
08702             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08703                break;
08704             }
08705          }
08706          if (strcmp(newpassword, newpassword2)) {
08707             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08708             cmd = ast_play_and_wait(chan, vm_mismatch);
08709             if (!cmd) {
08710                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08711             }
08712             break;
08713          }
08714          if (pwdchange & PWDCHANGE_INTERNAL)
08715             vm_change_password(vmu, newpassword);
08716          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08717             vm_change_password_shell(vmu, newpassword);
08718 
08719          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08720          cmd = ast_play_and_wait(chan, vm_passchanged);
08721          break;
08722       case '*': 
08723          cmd = 't';
08724          break;
08725       default: 
08726          cmd = 0;
08727          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08728          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08729          if (ast_fileexists(prefile, NULL, NULL)) {
08730             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08731          }
08732          DISPOSE(prefile, -1);
08733          if (!cmd) {
08734             cmd = ast_play_and_wait(chan, "vm-options");
08735          }
08736          if (!cmd) {
08737             cmd = ast_waitfordigit(chan,6000);
08738          }
08739          if (!cmd) {
08740             retries++;
08741          }
08742          if (retries > 3) {
08743             cmd = 't';
08744          }
08745       }
08746    }
08747    if (cmd == 't')
08748       cmd = 0;
08749    return cmd;
08750 }
08751 
08752 /*!
08753  * \brief The handler for 'record a temporary greeting'. 
08754  * \param chan
08755  * \param vmu
08756  * \param vms
08757  * \param fmtc
08758  * \param record_gain
08759  *
08760  * This is option 4 from the mailbox options menu.
08761  * This function manages the following promptings:
08762  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08763  * 2: remove (delete) the temporary greeting.
08764  * *: return to the main menu.
08765  *
08766  * \return zero on success, -1 on error.
08767  */
08768 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08769 {
08770    int cmd = 0;
08771    int retries = 0;
08772    int duration = 0;
08773    char prefile[PATH_MAX] = "";
08774    unsigned char buf[256];
08775    int bytes = 0;
08776 
08777    if (ast_adsi_available(chan)) {
08778       bytes += adsi_logo(buf + bytes);
08779       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08780       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08781       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08782       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08783       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08784    }
08785 
08786    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08787    while ((cmd >= 0) && (cmd != 't')) {
08788       if (cmd)
08789          retries = 0;
08790       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08791       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08792          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08793          cmd = 't';  
08794       } else {
08795          switch (cmd) {
08796          case '1':
08797             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08798             break;
08799          case '2':
08800             DELETE(prefile, -1, prefile, vmu);
08801             ast_play_and_wait(chan, "vm-tempremoved");
08802             cmd = 't';  
08803             break;
08804          case '*': 
08805             cmd = 't';
08806             break;
08807          default:
08808             cmd = ast_play_and_wait(chan,
08809                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08810                   "vm-tempgreeting2" : "vm-tempgreeting");
08811             if (!cmd)
08812                cmd = ast_waitfordigit(chan,6000);
08813             if (!cmd)
08814                retries++;
08815             if (retries > 3)
08816                cmd = 't';
08817          }
08818       }
08819       DISPOSE(prefile, -1);
08820    }
08821    if (cmd == 't')
08822       cmd = 0;
08823    return cmd;
08824 }
08825 
08826 /*!
08827  * \brief Greek syntax for 'You have N messages' greeting.
08828  * \param chan
08829  * \param vms
08830  * \param vmu
08831  *
08832  * \return zero on success, -1 on error.
08833  */   
08834 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08835 {
08836    int cmd=0;
08837 
08838    if (vms->lastmsg > -1) {
08839       cmd = play_message(chan, vmu, vms);
08840    } else {
08841       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08842       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08843          if (!cmd) {
08844             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08845             cmd = ast_play_and_wait(chan, vms->fn);
08846          }
08847          if (!cmd)
08848             cmd = ast_play_and_wait(chan, "vm-messages");
08849       } else {
08850          if (!cmd)
08851             cmd = ast_play_and_wait(chan, "vm-messages");
08852          if (!cmd) {
08853             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08854             cmd = ast_play_and_wait(chan, vms->fn);
08855          }
08856       }
08857    } 
08858    return cmd;
08859 }
08860 
08861 /* Hebrew Syntax */
08862 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08863 {
08864    int cmd = 0;
08865 
08866    if (vms->lastmsg > -1) {
08867       cmd = play_message(chan, vmu, vms);
08868    } else {
08869       if (!strcasecmp(vms->fn, "INBOX")) {
08870          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08871       } else {
08872          cmd = ast_play_and_wait(chan, "vm-nomessages");
08873       }
08874    }
08875    return cmd;
08876 }
08877 
08878 /*! 
08879  * \brief Default English syntax for 'You have N messages' greeting.
08880  * \param chan
08881  * \param vms
08882  * \param vmu
08883  *
08884  * \return zero on success, -1 on error.
08885  */
08886 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08887 {
08888    int cmd=0;
08889 
08890    if (vms->lastmsg > -1) {
08891       cmd = play_message(chan, vmu, vms);
08892    } else {
08893       cmd = ast_play_and_wait(chan, "vm-youhave");
08894       if (!cmd) 
08895          cmd = ast_play_and_wait(chan, "vm-no");
08896       if (!cmd) {
08897          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08898          cmd = ast_play_and_wait(chan, vms->fn);
08899       }
08900       if (!cmd)
08901          cmd = ast_play_and_wait(chan, "vm-messages");
08902    }
08903    return cmd;
08904 }
08905 
08906 /*! 
08907  *\brief Italian syntax for 'You have N messages' greeting.
08908  * \param chan
08909  * \param vms
08910  * \param vmu
08911  *
08912  * \return zero on success, -1 on error.
08913  */
08914 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08915 {
08916    int cmd=0;
08917 
08918    if (vms->lastmsg > -1) {
08919       cmd = play_message(chan, vmu, vms);
08920    } else {
08921       cmd = ast_play_and_wait(chan, "vm-no");
08922       if (!cmd)
08923          cmd = ast_play_and_wait(chan, "vm-message");
08924       if (!cmd) {
08925          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08926          cmd = ast_play_and_wait(chan, vms->fn);
08927       }
08928    }
08929    return cmd;
08930 }
08931 
08932 /*! 
08933  * \brief Spanish syntax for 'You have N messages' greeting.
08934  * \param chan
08935  * \param vms
08936  * \param vmu
08937  *
08938  * \return zero on success, -1 on error.
08939  */
08940 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08941 {
08942    int cmd=0;
08943 
08944    if (vms->lastmsg > -1) {
08945       cmd = play_message(chan, vmu, vms);
08946    } else {
08947       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08948       if (!cmd)
08949          cmd = ast_play_and_wait(chan, "vm-messages");
08950       if (!cmd) {
08951          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08952          cmd = ast_play_and_wait(chan, vms->fn);
08953       }
08954    }
08955    return cmd;
08956 }
08957 
08958 /*! 
08959  * \brief Portuguese syntax for 'You have N messages' greeting.
08960  * \param chan
08961  * \param vms
08962  * \param vmu
08963  *
08964  * \return zero on success, -1 on error.
08965  */
08966 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08967 {
08968    int cmd=0;
08969 
08970    if (vms->lastmsg > -1) {
08971       cmd = play_message(chan, vmu, vms);
08972    } else {
08973       cmd = ast_play_and_wait(chan, "vm-no");
08974       if (!cmd) {
08975          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08976          cmd = ast_play_and_wait(chan, vms->fn);
08977       }
08978       if (!cmd)
08979          cmd = ast_play_and_wait(chan, "vm-messages");
08980    }
08981    return cmd;
08982 }
08983 
08984 /*! 
08985  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08986  * \param chan
08987  * \param vms
08988  * \param vmu
08989  *
08990  * \return zero on success, -1 on error.
08991  */
08992 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08993 {
08994    int cmd=0;
08995 
08996    if (vms->lastmsg > -1) {
08997       cmd = play_message(chan, vmu, vms);
08998    } else {
08999       cmd = ast_play_and_wait(chan, "vm-you");
09000       if (!cmd) 
09001          cmd = ast_play_and_wait(chan, "vm-haveno");
09002       if (!cmd)
09003          cmd = ast_play_and_wait(chan, "vm-messages");
09004       if (!cmd) {
09005          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09006          cmd = ast_play_and_wait(chan, vms->fn);
09007       }
09008    }
09009    return cmd;
09010 }
09011 
09012 /*!
09013  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09014  * \param chan The channel for the current user. We read the language property from this.
09015  * \param vms passed into the language-specific vm_browse_messages function.
09016  * \param vmu passed into the language-specific vm_browse_messages function.
09017  * 
09018  * The method to be invoked is determined by the value of language code property in the user's channel.
09019  * The default (when unable to match) is to use english.
09020  *
09021  * \return zero on success, -1 on error.
09022  */
09023 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09024 {
09025    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09026       return vm_browse_messages_es(chan, vms, vmu);
09027    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09028       return vm_browse_messages_gr(chan, vms, vmu);
09029    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09030       return vm_browse_messages_he(chan, vms, vmu);
09031    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09032       return vm_browse_messages_it(chan, vms, vmu);
09033    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09034       return vm_browse_messages_pt(chan, vms, vmu);
09035    } else if (!strncasecmp(chan->language, "zh", 2)) {
09036       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
09037    } else {                                             /* Default to English syntax */
09038       return vm_browse_messages_en(chan, vms, vmu);
09039    }
09040 }
09041 
09042 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09043          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09044          int skipuser, int max_logins, int silent)
09045 {
09046    int useadsi=0, valid=0, logretries=0;
09047    char password[AST_MAX_EXTENSION]="", *passptr;
09048    struct ast_vm_user vmus, *vmu = NULL;
09049 
09050    /* If ADSI is supported, setup login screen */
09051    adsi_begin(chan, &useadsi);
09052    if (!skipuser && useadsi)
09053       adsi_login(chan);
09054    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09055       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09056       return -1;
09057    }
09058    
09059    /* Authenticate them and get their mailbox/password */
09060    
09061    while (!valid && (logretries < max_logins)) {
09062       /* Prompt for, and read in the username */
09063       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09064          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09065          return -1;
09066       }
09067       if (ast_strlen_zero(mailbox)) {
09068          if (chan->cid.cid_num) {
09069             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
09070          } else {
09071             ast_verb(3,"Username not entered\n");  
09072             return -1;
09073          }
09074       }
09075       if (useadsi)
09076          adsi_password(chan);
09077 
09078       if (!ast_strlen_zero(prefix)) {
09079          char fullusername[80] = "";
09080          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09081          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09082          ast_copy_string(mailbox, fullusername, mailbox_size);
09083       }
09084 
09085       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
09086       vmu = find_user(&vmus, context, mailbox);
09087       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09088          /* saved password is blank, so don't bother asking */
09089          password[0] = '\0';
09090       } else {
09091          if (ast_streamfile(chan, vm_password, chan->language)) {
09092             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09093             return -1;
09094          }
09095          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09096             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09097             return -1;
09098          }
09099       }
09100 
09101       if (vmu) {
09102          passptr = vmu->password;
09103          if (passptr[0] == '-') passptr++;
09104       }
09105       if (vmu && !strcmp(passptr, password))
09106          valid++;
09107       else {
09108          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09109          if (!ast_strlen_zero(prefix))
09110             mailbox[0] = '\0';
09111       }
09112       logretries++;
09113       if (!valid) {
09114          if (skipuser || logretries >= max_logins) {
09115             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09116                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09117                return -1;
09118             }
09119          } else {
09120             if (useadsi)
09121                adsi_login(chan);
09122             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09123                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09124                return -1;
09125             }
09126          }
09127          if (ast_waitstream(chan, "")) /* Channel is hung up */
09128             return -1;
09129       }
09130    }
09131    if (!valid && (logretries >= max_logins)) {
09132       ast_stopstream(chan);
09133       ast_play_and_wait(chan, "vm-goodbye");
09134       return -1;
09135    }
09136    if (vmu && !skipuser) {
09137       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09138    }
09139    return 0;
09140 }
09141 
09142 static int vm_execmain(struct ast_channel *chan, void *data)
09143 {
09144    /* XXX This is, admittedly, some pretty horrendous code.  For some
09145       reason it just seemed a lot easier to do with GOTO's.  I feel
09146       like I'm back in my GWBASIC days. XXX */
09147    int res=-1;
09148    int cmd=0;
09149    int valid = 0;
09150    char prefixstr[80] ="";
09151    char ext_context[256]="";
09152    int box;
09153    int useadsi = 0;
09154    int skipuser = 0;
09155    struct vm_state vms;
09156    struct ast_vm_user *vmu = NULL, vmus;
09157    char *context=NULL;
09158    int silentexit = 0;
09159    struct ast_flags flags = { 0 };
09160    signed char record_gain = 0;
09161    int play_auto = 0;
09162    int play_folder = 0;
09163    int in_urgent = 0;
09164 #ifdef IMAP_STORAGE
09165    int deleted = 0;
09166 #endif
09167 
09168    /* Add the vm_state to the active list and keep it active */
09169    memset(&vms, 0, sizeof(vms));
09170 
09171    vms.lastmsg = -1;
09172 
09173    memset(&vmus, 0, sizeof(vmus));
09174 
09175    if (chan->_state != AST_STATE_UP) {
09176       ast_debug(1, "Before ast_answer\n");
09177       ast_answer(chan);
09178    }
09179 
09180    if (!ast_strlen_zero(data)) {
09181       char *opts[OPT_ARG_ARRAY_SIZE];
09182       char *parse;
09183       AST_DECLARE_APP_ARGS(args,
09184          AST_APP_ARG(argv0);
09185          AST_APP_ARG(argv1);
09186       );
09187 
09188       parse = ast_strdupa(data);
09189 
09190       AST_STANDARD_APP_ARGS(args, parse);
09191 
09192       if (args.argc == 2) {
09193          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09194             return -1;
09195          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09196             int gain;
09197             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09198                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09199                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09200                   return -1;
09201                } else {
09202                   record_gain = (signed char) gain;
09203                }
09204             } else {
09205                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09206             }
09207          }
09208          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09209             play_auto = 1;
09210             if (opts[OPT_ARG_PLAYFOLDER]) {
09211                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09212                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09213                }
09214             } else {
09215                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09216             }  
09217             if ( play_folder > 9 || play_folder < 0) {
09218                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09219                play_folder = 0;
09220             }
09221          }
09222       } else {
09223          /* old style options parsing */
09224          while (*(args.argv0)) {
09225             if (*(args.argv0) == 's')
09226                ast_set_flag(&flags, OPT_SILENT);
09227             else if (*(args.argv0) == 'p')
09228                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09229             else 
09230                break;
09231             (args.argv0)++;
09232          }
09233 
09234       }
09235 
09236       valid = ast_test_flag(&flags, OPT_SILENT);
09237 
09238       if ((context = strchr(args.argv0, '@')))
09239          *context++ = '\0';
09240 
09241       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09242          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09243       else
09244          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09245 
09246       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09247          skipuser++;
09248       else
09249          valid = 0;
09250    }
09251 
09252    if (!valid)
09253       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09254 
09255    ast_debug(1, "After vm_authenticate\n");
09256    if (!res) {
09257       valid = 1;
09258       if (!skipuser)
09259          vmu = &vmus;
09260    } else {
09261       res = 0;
09262    }
09263 
09264    /* If ADSI is supported, setup login screen */
09265    adsi_begin(chan, &useadsi);
09266 
09267    if (!valid) {
09268       goto out;
09269    }
09270 
09271 #ifdef IMAP_STORAGE
09272    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09273    pthread_setspecific(ts_vmstate.key, &vms);
09274 
09275    vms.interactive = 1;
09276    vms.updated = 1;
09277    if (vmu)
09278       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09279    vmstate_insert(&vms);
09280    init_vm_state(&vms);
09281 #endif
09282    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09283       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09284       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09285       return -1;
09286    }
09287    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09288       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09289       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09290       return -1;
09291    }
09292    
09293    /* Set language from config to override channel language */
09294    if (!ast_strlen_zero(vmu->language))
09295       ast_string_field_set(chan, language, vmu->language);
09296 
09297    /* Retrieve urgent, old and new message counts */
09298    ast_debug(1, "Before open_mailbox\n");
09299    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09300    if (res == ERROR_LOCK_PATH)
09301       goto out;
09302    vms.oldmessages = vms.lastmsg + 1;
09303    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09304    /* check INBOX */
09305    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09306    if (res == ERROR_LOCK_PATH)
09307       goto out;
09308    vms.newmessages = vms.lastmsg + 1;
09309    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09310    /* Start in Urgent */
09311    in_urgent = 1;
09312    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09313    if (res == ERROR_LOCK_PATH)
09314       goto out;
09315    vms.urgentmessages = vms.lastmsg + 1;
09316    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09317 
09318    /* Select proper mailbox FIRST!! */
09319    if (play_auto) {
09320       if (vms.urgentmessages) {
09321          in_urgent = 1;
09322          res = open_mailbox(&vms, vmu, 11);
09323       } else {
09324          in_urgent = 0;
09325          res = open_mailbox(&vms, vmu, play_folder);
09326       }
09327       if (res == ERROR_LOCK_PATH)
09328          goto out;
09329 
09330       /* If there are no new messages, inform the user and hangup */
09331       if (vms.lastmsg == -1) {
09332          in_urgent = 0;
09333          cmd = vm_browse_messages(chan, &vms, vmu);
09334          res = 0;
09335          goto out;
09336       }
09337    } else {
09338       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09339          /* If we only have old messages start here */
09340          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09341          in_urgent = 0;
09342          play_folder = 1;
09343          if (res == ERROR_LOCK_PATH)
09344             goto out;
09345       } else if (!vms.urgentmessages && vms.newmessages) {
09346          /* If we have new messages but none are urgent */
09347          in_urgent = 0;
09348          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09349          if (res == ERROR_LOCK_PATH)
09350             goto out;
09351       }
09352    }
09353 
09354    if (useadsi)
09355       adsi_status(chan, &vms);
09356    res = 0;
09357 
09358    /* Check to see if this is a new user */
09359    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09360       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09361       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09362          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09363       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09364       if ((cmd == 't') || (cmd == '#')) {
09365          /* Timeout */
09366          res = 0;
09367          goto out;
09368       } else if (cmd < 0) {
09369          /* Hangup */
09370          res = -1;
09371          goto out;
09372       }
09373    }
09374 #ifdef IMAP_STORAGE
09375       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09376       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09377          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09378          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09379       }
09380       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09381       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09382          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09383          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09384       }
09385 #endif
09386    if (play_auto) {
09387       cmd = '1';
09388    } else {
09389       cmd = vm_intro(chan, vmu, &vms);
09390    }
09391 
09392    vms.repeats = 0;
09393    vms.starting = 1;
09394    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09395       /* Run main menu */
09396       switch (cmd) {
09397       case '1': /* First message */
09398          vms.curmsg = 0;
09399          /* Fall through */
09400       case '5': /* Play current message */
09401          cmd = vm_browse_messages(chan, &vms, vmu);
09402          break;
09403       case '2': /* Change folders */
09404          if (useadsi)
09405             adsi_folders(chan, 0, "Change to folder...");
09406          cmd = get_folder2(chan, "vm-changeto", 0);
09407          if (cmd == '#') {
09408             cmd = 0;
09409          } else if (cmd > 0) {
09410             cmd = cmd - '0';
09411             res = close_mailbox(&vms, vmu);
09412             if (res == ERROR_LOCK_PATH)
09413                goto out;
09414             /* If folder is not urgent, set in_urgent to zero! */
09415             if (cmd != 11) in_urgent = 0;
09416             res = open_mailbox(&vms, vmu, cmd);
09417             if (res == ERROR_LOCK_PATH)
09418                goto out;
09419             play_folder = cmd;
09420             cmd = 0;
09421          }
09422          if (useadsi)
09423             adsi_status2(chan, &vms);
09424             
09425          if (!cmd)
09426             cmd = vm_play_folder_name(chan, vms.vmbox);
09427 
09428          vms.starting = 1;
09429          break;
09430       case '3': /* Advanced options */
09431          cmd = 0;
09432          vms.repeats = 0;
09433          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09434             switch (cmd) {
09435             case '1': /* Reply */
09436                if (vms.lastmsg > -1 && !vms.starting) {
09437                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09438                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09439                      res = cmd;
09440                      goto out;
09441                   }
09442                } else
09443                   cmd = ast_play_and_wait(chan, "vm-sorry");
09444                cmd = 't';
09445                break;
09446             case '2': /* Callback */
09447                if (!vms.starting)
09448                   ast_verb(3, "Callback Requested\n");
09449                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09450                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09451                   if (cmd == 9) {
09452                      silentexit = 1;
09453                      goto out;
09454                   } else if (cmd == ERROR_LOCK_PATH) {
09455                      res = cmd;
09456                      goto out;
09457                   }
09458                } else 
09459                   cmd = ast_play_and_wait(chan, "vm-sorry");
09460                cmd = 't';
09461                break;
09462             case '3': /* Envelope */
09463                if (vms.lastmsg > -1 && !vms.starting) {
09464                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09465                   if (cmd == ERROR_LOCK_PATH) {
09466                      res = cmd;
09467                      goto out;
09468                   }
09469                } else
09470                   cmd = ast_play_and_wait(chan, "vm-sorry");
09471                cmd = 't';
09472                break;
09473             case '4': /* Dialout */
09474                if (!ast_strlen_zero(vmu->dialout)) {
09475                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09476                   if (cmd == 9) {
09477                      silentexit = 1;
09478                      goto out;
09479                   }
09480                } else 
09481                   cmd = ast_play_and_wait(chan, "vm-sorry");
09482                cmd = 't';
09483                break;
09484 
09485             case '5': /* Leave VoiceMail */
09486                if (ast_test_flag(vmu, VM_SVMAIL)) {
09487                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09488                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09489                      res = cmd;
09490                      goto out;
09491                   }
09492                } else
09493                   cmd = ast_play_and_wait(chan,"vm-sorry");
09494                cmd='t';
09495                break;
09496                
09497             case '*': /* Return to main menu */
09498                cmd = 't';
09499                break;
09500 
09501             default:
09502                cmd = 0;
09503                if (!vms.starting) {
09504                   cmd = ast_play_and_wait(chan, "vm-toreply");
09505                }
09506                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09507                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09508                }
09509                if (!cmd && !vms.starting) {
09510                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09511                }
09512                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09513                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09514                }
09515                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09516                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09517                if (!cmd)
09518                   cmd = ast_play_and_wait(chan, "vm-starmain");
09519                if (!cmd)
09520                   cmd = ast_waitfordigit(chan,6000);
09521                if (!cmd)
09522                   vms.repeats++;
09523                if (vms.repeats > 3)
09524                   cmd = 't';
09525             }
09526          }
09527          if (cmd == 't') {
09528             cmd = 0;
09529             vms.repeats = 0;
09530          }
09531          break;
09532       case '4': /* Go to the previous message */
09533          if (vms.curmsg > 0) {
09534             vms.curmsg--;
09535             cmd = play_message(chan, vmu, &vms);
09536          } else {
09537             /* Check if we were listening to new
09538                messages.  If so, go to Urgent messages
09539                instead of saying "no more messages"
09540             */
09541             if (in_urgent == 0 && vms.urgentmessages > 0) {
09542                /* Check for Urgent messages */
09543                in_urgent = 1;
09544                res = close_mailbox(&vms, vmu);
09545                if (res == ERROR_LOCK_PATH)
09546                   goto out;
09547                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09548                if (res == ERROR_LOCK_PATH)
09549                   goto out;
09550                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09551                vms.curmsg = vms.lastmsg;
09552                if (vms.lastmsg < 0)
09553                   cmd = ast_play_and_wait(chan, "vm-nomore");
09554             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09555                vms.curmsg = vms.lastmsg;
09556                cmd = play_message(chan, vmu, &vms);
09557             } else {
09558                cmd = ast_play_and_wait(chan, "vm-nomore");
09559             }
09560          }
09561          break;
09562       case '6': /* Go to the next message */
09563          if (vms.curmsg < vms.lastmsg) {
09564             vms.curmsg++;
09565             cmd = play_message(chan, vmu, &vms);
09566          } else {
09567             if (in_urgent && vms.newmessages > 0) {
09568                /* Check if we were listening to urgent
09569                 * messages.  If so, go to regular new messages
09570                 * instead of saying "no more messages"
09571                 */
09572                in_urgent = 0;
09573                res = close_mailbox(&vms, vmu);
09574                if (res == ERROR_LOCK_PATH)
09575                   goto out;
09576                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09577                if (res == ERROR_LOCK_PATH)
09578                   goto out;
09579                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09580                vms.curmsg = -1;
09581                if (vms.lastmsg < 0) {
09582                   cmd = ast_play_and_wait(chan, "vm-nomore");
09583                }
09584             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09585                vms.curmsg = 0;
09586                cmd = play_message(chan, vmu, &vms);
09587             } else {
09588                cmd = ast_play_and_wait(chan, "vm-nomore");
09589             }
09590          }
09591          break;
09592       case '7': /* Delete the current message */
09593          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09594             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09595             if (useadsi)
09596                adsi_delete(chan, &vms);
09597             if (vms.deleted[vms.curmsg]) {
09598                if (play_folder == 0) {
09599                   if (in_urgent) {
09600                      vms.urgentmessages--;
09601                   } else {
09602                      vms.newmessages--;
09603                   }
09604                }
09605                else if (play_folder == 1)
09606                   vms.oldmessages--;
09607                cmd = ast_play_and_wait(chan, "vm-deleted");
09608             } else {
09609                if (play_folder == 0) {
09610                   if (in_urgent) {
09611                      vms.urgentmessages++;
09612                   } else {
09613                      vms.newmessages++;
09614                   }
09615                }
09616                else if (play_folder == 1)
09617                   vms.oldmessages++;
09618                cmd = ast_play_and_wait(chan, "vm-undeleted");
09619             }
09620             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09621                if (vms.curmsg < vms.lastmsg) {
09622                   vms.curmsg++;
09623                   cmd = play_message(chan, vmu, &vms);
09624                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09625                   vms.curmsg = 0;
09626                   cmd = play_message(chan, vmu, &vms);
09627                } else {
09628                   /* Check if we were listening to urgent
09629                      messages.  If so, go to regular new messages
09630                      instead of saying "no more messages"
09631                   */
09632                   if (in_urgent == 1) {
09633                      /* Check for new messages */
09634                      in_urgent = 0;
09635                      res = close_mailbox(&vms, vmu);
09636                      if (res == ERROR_LOCK_PATH)
09637                         goto out;
09638                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09639                      if (res == ERROR_LOCK_PATH)
09640                         goto out;
09641                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09642                      vms.curmsg = -1;
09643                      if (vms.lastmsg < 0)
09644                         cmd = ast_play_and_wait(chan, "vm-nomore");
09645                   } else {
09646                      cmd = ast_play_and_wait(chan, "vm-nomore");
09647                   }
09648                }
09649             }
09650          } else /* Delete not valid if we haven't selected a message */
09651             cmd = 0;
09652 #ifdef IMAP_STORAGE
09653          deleted = 1;
09654 #endif
09655          break;
09656    
09657       case '8': /* Forward the current messgae */
09658          if (vms.lastmsg > -1) {
09659             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09660             if (cmd == ERROR_LOCK_PATH) {
09661                res = cmd;
09662                goto out;
09663             }
09664          } else {
09665             /* Check if we were listening to urgent
09666                messages.  If so, go to regular new messages
09667                instead of saying "no more messages"
09668             */
09669             if (in_urgent == 1 && vms.newmessages > 0) {
09670                /* Check for new messages */
09671                in_urgent = 0;
09672                res = close_mailbox(&vms, vmu);
09673                if (res == ERROR_LOCK_PATH)
09674                   goto out;
09675                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09676                if (res == ERROR_LOCK_PATH)
09677                   goto out;
09678                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09679                vms.curmsg = -1;
09680                if (vms.lastmsg < 0)
09681                   cmd = ast_play_and_wait(chan, "vm-nomore");
09682             } else {
09683                cmd = ast_play_and_wait(chan, "vm-nomore");
09684             }
09685          }
09686          break;
09687       case '9': /* Save message to folder */
09688          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09689             /* No message selected */
09690             cmd = 0;
09691             break;
09692          }
09693          if (useadsi)
09694             adsi_folders(chan, 1, "Save to folder...");
09695          cmd = get_folder2(chan, "vm-savefolder", 1);
09696          box = 0; /* Shut up compiler */
09697          if (cmd == '#') {
09698             cmd = 0;
09699             break;
09700          } else if (cmd > 0) {
09701             box = cmd = cmd - '0';
09702             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09703             if (cmd == ERROR_LOCK_PATH) {
09704                res = cmd;
09705                goto out;
09706 #ifndef IMAP_STORAGE
09707             } else if (!cmd) {
09708                vms.deleted[vms.curmsg] = 1;
09709 #endif
09710             } else {
09711                vms.deleted[vms.curmsg] = 0;
09712                vms.heard[vms.curmsg] = 0;
09713             }
09714          }
09715          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09716          if (useadsi)
09717             adsi_message(chan, &vms);
09718          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09719          if (!cmd) {
09720             cmd = ast_play_and_wait(chan, "vm-message");
09721             if (!cmd) 
09722                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09723             if (!cmd)
09724                cmd = ast_play_and_wait(chan, "vm-savedto");
09725             if (!cmd)
09726                cmd = vm_play_folder_name(chan, vms.fn);
09727          } else {
09728             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09729          }
09730          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09731             if (vms.curmsg < vms.lastmsg) {
09732                vms.curmsg++;
09733                cmd = play_message(chan, vmu, &vms);
09734             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09735                vms.curmsg = 0;
09736                cmd = play_message(chan, vmu, &vms);
09737             } else {
09738                /* Check if we were listening to urgent
09739                   messages.  If so, go to regular new messages
09740                   instead of saying "no more messages"
09741                */
09742                if (in_urgent == 1 && vms.newmessages > 0) {
09743                   /* Check for new messages */
09744                   in_urgent = 0;
09745                   res = close_mailbox(&vms, vmu);
09746                   if (res == ERROR_LOCK_PATH)
09747                      goto out;
09748                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09749                   if (res == ERROR_LOCK_PATH)
09750                      goto out;
09751                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09752                   vms.curmsg = -1;
09753                   if (vms.lastmsg < 0)
09754                      cmd = ast_play_and_wait(chan, "vm-nomore");
09755                } else {
09756                   cmd = ast_play_and_wait(chan, "vm-nomore");
09757                }
09758             }
09759          }
09760          break;
09761       case '*': /* Help */
09762          if (!vms.starting) {
09763             cmd = ast_play_and_wait(chan, "vm-onefor");
09764             if (!strncasecmp(chan->language, "he", 2)) {
09765                cmd = ast_play_and_wait(chan, "vm-for");
09766             }
09767             if (!cmd)
09768                cmd = vm_play_folder_name(chan, vms.vmbox);
09769             if (!cmd)
09770                cmd = ast_play_and_wait(chan, "vm-opts");
09771             if (!cmd)
09772                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09773          } else
09774             cmd = 0;
09775          break;
09776       case '0': /* Mailbox options */
09777          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09778          if (useadsi)
09779             adsi_status(chan, &vms);
09780          break;
09781       default: /* Nothing */
09782          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09783          break;
09784       }
09785    }
09786    if ((cmd == 't') || (cmd == '#')) {
09787       /* Timeout */
09788       res = 0;
09789    } else {
09790       /* Hangup */
09791       res = -1;
09792    }
09793 
09794 out:
09795    if (res > -1) {
09796       ast_stopstream(chan);
09797       adsi_goodbye(chan);
09798       if (valid && res != OPERATOR_EXIT) {
09799          if (silentexit)
09800             res = ast_play_and_wait(chan, "vm-dialout");
09801          else 
09802             res = ast_play_and_wait(chan, "vm-goodbye");
09803       }
09804       if ((valid && res > 0) || res == OPERATOR_EXIT) {
09805          res = 0;
09806       }
09807       if (useadsi)
09808          ast_adsi_unload_session(chan);
09809    }
09810    if (vmu)
09811       close_mailbox(&vms, vmu);
09812    if (valid) {
09813       int new = 0, old = 0, urgent = 0;
09814       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09815       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09816       /* Urgent flag not passwd to externnotify here */
09817       run_externnotify(vmu->context, vmu->mailbox, NULL);
09818       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09819       queue_mwi_event(ext_context, urgent, new, old);
09820    }
09821 #ifdef IMAP_STORAGE
09822    /* expunge message - use UID Expunge if supported on IMAP server*/
09823    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09824    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09825       ast_mutex_lock(&vms.lock);
09826 #ifdef HAVE_IMAP_TK2006
09827       if (LEVELUIDPLUS (vms.mailstream)) {
09828          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09829       } else 
09830 #endif
09831          mail_expunge(vms.mailstream);
09832       ast_mutex_unlock(&vms.lock);
09833    }
09834    /*  before we delete the state, we should copy pertinent info
09835     *  back to the persistent model */
09836    if (vmu) {
09837       vmstate_delete(&vms);
09838    }
09839 #endif
09840    if (vmu)
09841       free_user(vmu);
09842    if (vms.deleted)
09843       ast_free(vms.deleted);
09844    if (vms.heard)
09845       ast_free(vms.heard);
09846 
09847 #ifdef IMAP_STORAGE
09848    pthread_setspecific(ts_vmstate.key, NULL);
09849 #endif
09850    return res;
09851 }
09852 
09853 static int vm_exec(struct ast_channel *chan, void *data)
09854 {
09855    int res = 0;
09856    char *tmp;
09857    struct leave_vm_options leave_options;
09858    struct ast_flags flags = { 0 };
09859    char *opts[OPT_ARG_ARRAY_SIZE];
09860    AST_DECLARE_APP_ARGS(args,
09861       AST_APP_ARG(argv0);
09862       AST_APP_ARG(argv1);
09863    );
09864    
09865    memset(&leave_options, 0, sizeof(leave_options));
09866 
09867    if (chan->_state != AST_STATE_UP)
09868       ast_answer(chan);
09869 
09870    if (!ast_strlen_zero(data)) {
09871       tmp = ast_strdupa(data);
09872       AST_STANDARD_APP_ARGS(args, tmp);
09873       if (args.argc == 2) {
09874          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09875             return -1;
09876          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09877          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09878             int gain;
09879 
09880             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09881                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09882                return -1;
09883             } else {
09884                leave_options.record_gain = (signed char) gain;
09885             }
09886          }
09887          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09888             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09889                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09890          }
09891       }
09892    } else {
09893       char temp[256];
09894       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09895       if (res < 0)
09896          return res;
09897       if (ast_strlen_zero(temp))
09898          return 0;
09899       args.argv0 = ast_strdupa(temp);
09900    }
09901 
09902    res = leave_voicemail(chan, args.argv0, &leave_options);
09903 
09904    if (res == ERROR_LOCK_PATH) {
09905       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09906       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09907       res = 0;
09908    }
09909 
09910    return res;
09911 }
09912 
09913 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09914 {
09915    struct ast_vm_user *vmu;
09916 
09917    AST_LIST_TRAVERSE(&users, vmu, list) {
09918       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09919          if (strcasecmp(vmu->context, context)) {
09920             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09921                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09922                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09923                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09924          }
09925          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09926          return NULL;
09927       }
09928       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09929          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09930          return NULL;
09931       }
09932    }
09933    
09934    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09935       return NULL;
09936    
09937    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09938    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09939 
09940    AST_LIST_INSERT_TAIL(&users, vmu, list);
09941    
09942    return vmu;
09943 }
09944 
09945 static int append_mailbox(const char *context, const char *box, const char *data)
09946 {
09947    /* Assumes lock is already held */
09948    char *tmp;
09949    char *stringp;
09950    char *s;
09951    struct ast_vm_user *vmu;
09952    char *mailbox_full;
09953    int new = 0, old = 0, urgent = 0;
09954 
09955    tmp = ast_strdupa(data);
09956 
09957    if (!(vmu = find_or_create(context, box)))
09958       return -1;
09959    
09960    populate_defaults(vmu);
09961 
09962    stringp = tmp;
09963    if ((s = strsep(&stringp, ","))) 
09964       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09965    if (stringp && (s = strsep(&stringp, ","))) 
09966       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09967    if (stringp && (s = strsep(&stringp, ","))) 
09968       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09969    if (stringp && (s = strsep(&stringp, ","))) 
09970       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09971    if (stringp && (s = strsep(&stringp, ","))) 
09972       apply_options(vmu, s);
09973 
09974    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09975    strcpy(mailbox_full, box);
09976    strcat(mailbox_full, "@");
09977    strcat(mailbox_full, context);
09978 
09979    inboxcount2(mailbox_full, &urgent, &new, &old);
09980    queue_mwi_event(mailbox_full, urgent, new, old);
09981 
09982    return 0;
09983 }
09984 
09985 static int vm_box_exists(struct ast_channel *chan, void *data) 
09986 {
09987    struct ast_vm_user svm;
09988    char *context, *box;
09989    AST_DECLARE_APP_ARGS(args,
09990       AST_APP_ARG(mbox);
09991       AST_APP_ARG(options);
09992    );
09993    static int dep_warning = 0;
09994 
09995    if (ast_strlen_zero(data)) {
09996       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09997       return -1;
09998    }
09999 
10000    if (!dep_warning) {
10001       dep_warning = 1;
10002       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
10003    }
10004 
10005    box = ast_strdupa(data);
10006 
10007    AST_STANDARD_APP_ARGS(args, box);
10008 
10009    if (args.options) {
10010    }
10011 
10012    if ((context = strchr(args.mbox, '@'))) {
10013       *context = '\0';
10014       context++;
10015    }
10016 
10017    if (find_user(&svm, context, args.mbox)) {
10018       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10019    } else
10020       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10021 
10022    return 0;
10023 }
10024 
10025 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10026 {
10027    struct ast_vm_user svm;
10028    AST_DECLARE_APP_ARGS(arg,
10029       AST_APP_ARG(mbox);
10030       AST_APP_ARG(context);
10031    );
10032 
10033    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10034 
10035    if (ast_strlen_zero(arg.mbox)) {
10036       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10037       return -1;
10038    }
10039 
10040    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10041    return 0;
10042 }
10043 
10044 static struct ast_custom_function mailbox_exists_acf = {
10045    .name = "MAILBOX_EXISTS",
10046    .read = acf_mailbox_exists,
10047 };
10048 
10049 static int vmauthenticate(struct ast_channel *chan, void *data)
10050 {
10051    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
10052    struct ast_vm_user vmus;
10053    char *options = NULL;
10054    int silent = 0, skipuser = 0;
10055    int res = -1;
10056    
10057    if (s) {
10058       s = ast_strdupa(s);
10059       user = strsep(&s, ",");
10060       options = strsep(&s, ",");
10061       if (user) {
10062          s = user;
10063          user = strsep(&s, "@");
10064          context = strsep(&s, "");
10065          if (!ast_strlen_zero(user))
10066             skipuser++;
10067          ast_copy_string(mailbox, user, sizeof(mailbox));
10068       }
10069    }
10070 
10071    if (options) {
10072       silent = (strchr(options, 's')) != NULL;
10073    }
10074 
10075    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10076       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10077       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10078       ast_play_and_wait(chan, "auth-thankyou");
10079       res = 0;
10080    }
10081 
10082    return res;
10083 }
10084 
10085 static char *show_users_realtime(int fd, const char *context)
10086 {
10087    struct ast_config *cfg;
10088    const char *cat = NULL;
10089 
10090    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10091       "context", context, SENTINEL))) {
10092       return CLI_FAILURE;
10093    }
10094 
10095    ast_cli(fd,
10096       "\n"
10097       "=============================================================\n"
10098       "=== Configured Voicemail Users ==============================\n"
10099       "=============================================================\n"
10100       "===\n");
10101 
10102    while ((cat = ast_category_browse(cfg, cat))) {
10103       struct ast_variable *var = NULL;
10104       ast_cli(fd,
10105          "=== Mailbox ...\n"
10106          "===\n");
10107       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10108          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10109       ast_cli(fd,
10110          "===\n"
10111          "=== ---------------------------------------------------------\n"
10112          "===\n");
10113    }
10114 
10115    ast_cli(fd,
10116       "=============================================================\n"
10117       "\n");
10118 
10119    ast_config_destroy(cfg);
10120 
10121    return CLI_SUCCESS;
10122 }
10123 
10124 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10125 {
10126    int which = 0;
10127    int wordlen;
10128    struct ast_vm_user *vmu;
10129    const char *context = "";
10130 
10131    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10132    if (pos > 4)
10133       return NULL;
10134    if (pos == 3)
10135       return (state == 0) ? ast_strdup("for") : NULL;
10136    wordlen = strlen(word);
10137    AST_LIST_TRAVERSE(&users, vmu, list) {
10138       if (!strncasecmp(word, vmu->context, wordlen)) {
10139          if (context && strcmp(context, vmu->context) && ++which > state)
10140             return ast_strdup(vmu->context);
10141          /* ignore repeated contexts ? */
10142          context = vmu->context;
10143       }
10144    }
10145    return NULL;
10146 }
10147 
10148 /*! \brief Show a list of voicemail users in the CLI */
10149 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10150 {
10151    struct ast_vm_user *vmu;
10152 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10153    const char *context = NULL;
10154    int users_counter = 0;
10155 
10156    switch (cmd) {
10157    case CLI_INIT:
10158       e->command = "voicemail show users";
10159       e->usage =
10160          "Usage: voicemail show users [for <context>]\n"
10161          "       Lists all mailboxes currently set up\n";
10162       return NULL;
10163    case CLI_GENERATE:
10164       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10165    }  
10166 
10167    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10168       return CLI_SHOWUSAGE;
10169    if (a->argc == 5) {
10170       if (strcmp(a->argv[3],"for"))
10171          return CLI_SHOWUSAGE;
10172       context = a->argv[4];
10173    }
10174 
10175    if (ast_check_realtime("voicemail")) {
10176       if (!context) {
10177          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10178          return CLI_SHOWUSAGE;
10179       }
10180       return show_users_realtime(a->fd, context);
10181    }
10182 
10183    AST_LIST_LOCK(&users);
10184    if (AST_LIST_EMPTY(&users)) {
10185       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10186       AST_LIST_UNLOCK(&users);
10187       return CLI_FAILURE;
10188    }
10189    if (a->argc == 3)
10190       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10191    else {
10192       int count = 0;
10193       AST_LIST_TRAVERSE(&users, vmu, list) {
10194          if (!strcmp(context, vmu->context))
10195             count++;
10196       }
10197       if (count) {
10198          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10199       } else {
10200          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10201          AST_LIST_UNLOCK(&users);
10202          return CLI_FAILURE;
10203       }
10204    }
10205    AST_LIST_TRAVERSE(&users, vmu, list) {
10206       int newmsgs = 0, oldmsgs = 0;
10207       char count[12], tmp[256] = "";
10208 
10209       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10210          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10211          inboxcount(tmp, &newmsgs, &oldmsgs);
10212          snprintf(count, sizeof(count), "%d", newmsgs);
10213          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10214          users_counter++;
10215       }
10216    }
10217    AST_LIST_UNLOCK(&users);
10218    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10219    return CLI_SUCCESS;
10220 }
10221 
10222 /*! \brief Show a list of voicemail zones in the CLI */
10223 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10224 {
10225    struct vm_zone *zone;
10226 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10227    char *res = CLI_SUCCESS;
10228 
10229    switch (cmd) {
10230    case CLI_INIT:
10231       e->command = "voicemail show zones";
10232       e->usage =
10233          "Usage: voicemail show zones\n"
10234          "       Lists zone message formats\n";
10235       return NULL;
10236    case CLI_GENERATE:
10237       return NULL;
10238    }
10239 
10240    if (a->argc != 3)
10241       return CLI_SHOWUSAGE;
10242 
10243    AST_LIST_LOCK(&zones);
10244    if (!AST_LIST_EMPTY(&zones)) {
10245       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10246       AST_LIST_TRAVERSE(&zones, zone, list) {
10247          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10248       }
10249    } else {
10250       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10251       res = CLI_FAILURE;
10252    }
10253    AST_LIST_UNLOCK(&zones);
10254 
10255    return res;
10256 }
10257 
10258 /*! \brief Reload voicemail configuration from the CLI */
10259 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10260 {
10261    switch (cmd) {
10262    case CLI_INIT:
10263       e->command = "voicemail reload";
10264       e->usage =
10265          "Usage: voicemail reload\n"
10266          "       Reload voicemail configuration\n";
10267       return NULL;
10268    case CLI_GENERATE:
10269       return NULL;
10270    }
10271 
10272    if (a->argc != 2)
10273       return CLI_SHOWUSAGE;
10274 
10275    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10276    load_config(1);
10277    
10278    return CLI_SUCCESS;
10279 }
10280 
10281 static struct ast_cli_entry cli_voicemail[] = {
10282    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10283    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10284    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10285 };
10286 
10287 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10288 {
10289    int new = 0, old = 0, urgent = 0;
10290 
10291    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10292 
10293    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10294       mwi_sub->old_urgent = urgent;
10295       mwi_sub->old_new = new;
10296       mwi_sub->old_old = old;
10297       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10298    }
10299 }
10300 
10301 static void poll_subscribed_mailboxes(void)
10302 {
10303    struct mwi_sub *mwi_sub;
10304 
10305    AST_RWLIST_RDLOCK(&mwi_subs);
10306    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10307       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10308          poll_subscribed_mailbox(mwi_sub);
10309       }
10310    }
10311    AST_RWLIST_UNLOCK(&mwi_subs);
10312 }
10313 
10314 static void *mb_poll_thread(void *data)
10315 {
10316    while (poll_thread_run) {
10317       struct timespec ts = { 0, };
10318       struct timeval wait;
10319 
10320       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10321       ts.tv_sec = wait.tv_sec;
10322       ts.tv_nsec = wait.tv_usec * 1000;
10323 
10324       ast_mutex_lock(&poll_lock);
10325       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10326       ast_mutex_unlock(&poll_lock);
10327 
10328       if (!poll_thread_run)
10329          break;
10330 
10331       poll_subscribed_mailboxes();
10332    }
10333 
10334    return NULL;
10335 }
10336 
10337 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10338 {
10339    ast_free(mwi_sub);
10340 }
10341 
10342 static int handle_unsubscribe(void *datap)
10343 {
10344    struct mwi_sub *mwi_sub;
10345    uint32_t *uniqueid = datap;
10346    
10347    AST_RWLIST_WRLOCK(&mwi_subs);
10348    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10349       if (mwi_sub->uniqueid == *uniqueid) {
10350          AST_LIST_REMOVE_CURRENT(entry);
10351          break;
10352       }
10353    }
10354    AST_RWLIST_TRAVERSE_SAFE_END
10355    AST_RWLIST_UNLOCK(&mwi_subs);
10356 
10357    if (mwi_sub)
10358       mwi_sub_destroy(mwi_sub);
10359 
10360    ast_free(uniqueid);  
10361    return 0;
10362 }
10363 
10364 static int handle_subscribe(void *datap)
10365 {
10366    unsigned int len;
10367    struct mwi_sub *mwi_sub;
10368    struct mwi_sub_task *p = datap;
10369 
10370    len = sizeof(*mwi_sub);
10371    if (!ast_strlen_zero(p->mailbox))
10372       len += strlen(p->mailbox);
10373 
10374    if (!ast_strlen_zero(p->context))
10375       len += strlen(p->context) + 1; /* Allow for seperator */
10376 
10377    if (!(mwi_sub = ast_calloc(1, len)))
10378       return -1;
10379 
10380    mwi_sub->uniqueid = p->uniqueid;
10381    if (!ast_strlen_zero(p->mailbox))
10382       strcpy(mwi_sub->mailbox, p->mailbox);
10383 
10384    if (!ast_strlen_zero(p->context)) {
10385       strcat(mwi_sub->mailbox, "@");
10386       strcat(mwi_sub->mailbox, p->context);
10387    }
10388 
10389    AST_RWLIST_WRLOCK(&mwi_subs);
10390    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10391    AST_RWLIST_UNLOCK(&mwi_subs);
10392    ast_free((void *) p->mailbox);
10393    ast_free((void *) p->context);
10394    ast_free(p);
10395    poll_subscribed_mailbox(mwi_sub);
10396    return 0;
10397 }
10398 
10399 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10400 {
10401    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10402    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10403       return;
10404 
10405    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10406       return;
10407 
10408    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10409    *uniqueid = u;
10410    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10411       ast_free(uniqueid);
10412    }
10413 }
10414 
10415 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10416 {
10417    struct mwi_sub_task *mwist;
10418    
10419    if (ast_event_get_type(event) != AST_EVENT_SUB)
10420       return;
10421 
10422    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10423       return;
10424 
10425    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10426       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10427       return;
10428    }
10429    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10430    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10431    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10432    
10433    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10434       ast_free(mwist);
10435    }
10436 }
10437 
10438 static void start_poll_thread(void)
10439 {
10440    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10441       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10442       AST_EVENT_IE_END);
10443 
10444    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10445       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10446       AST_EVENT_IE_END);
10447 
10448    if (mwi_sub_sub)
10449       ast_event_report_subs(mwi_sub_sub);
10450 
10451    poll_thread_run = 1;
10452 
10453    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10454 }
10455 
10456 static void stop_poll_thread(void)
10457 {
10458    poll_thread_run = 0;
10459 
10460    if (mwi_sub_sub) {
10461       ast_event_unsubscribe(mwi_sub_sub);
10462       mwi_sub_sub = NULL;
10463    }
10464 
10465    if (mwi_unsub_sub) {
10466       ast_event_unsubscribe(mwi_unsub_sub);
10467       mwi_unsub_sub = NULL;
10468    }
10469 
10470    ast_mutex_lock(&poll_lock);
10471    ast_cond_signal(&poll_cond);
10472    ast_mutex_unlock(&poll_lock);
10473 
10474    pthread_join(poll_thread, NULL);
10475 
10476    poll_thread = AST_PTHREADT_NULL;
10477 }
10478 
10479 /*! \brief Manager list voicemail users command */
10480 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10481 {
10482    struct ast_vm_user *vmu = NULL;
10483    const char *id = astman_get_header(m, "ActionID");
10484    char actionid[128] = "";
10485 
10486    if (!ast_strlen_zero(id))
10487       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10488 
10489    AST_LIST_LOCK(&users);
10490 
10491    if (AST_LIST_EMPTY(&users)) {
10492       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10493       AST_LIST_UNLOCK(&users);
10494       return RESULT_SUCCESS;
10495    }
10496    
10497    astman_send_ack(s, m, "Voicemail user list will follow");
10498    
10499    AST_LIST_TRAVERSE(&users, vmu, list) {
10500       char dirname[256];
10501 
10502 #ifdef IMAP_STORAGE
10503       int new, old;
10504       inboxcount(vmu->mailbox, &new, &old);
10505 #endif
10506       
10507       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10508       astman_append(s,
10509          "%s"
10510          "Event: VoicemailUserEntry\r\n"
10511          "VMContext: %s\r\n"
10512          "VoiceMailbox: %s\r\n"
10513          "Fullname: %s\r\n"
10514          "Email: %s\r\n"
10515          "Pager: %s\r\n"
10516          "ServerEmail: %s\r\n"
10517          "MailCommand: %s\r\n"
10518          "Language: %s\r\n"
10519          "TimeZone: %s\r\n"
10520          "Callback: %s\r\n"
10521          "Dialout: %s\r\n"
10522          "UniqueID: %s\r\n"
10523          "ExitContext: %s\r\n"
10524          "SayDurationMinimum: %d\r\n"
10525          "SayEnvelope: %s\r\n"
10526          "SayCID: %s\r\n"
10527          "AttachMessage: %s\r\n"
10528          "AttachmentFormat: %s\r\n"
10529          "DeleteMessage: %s\r\n"
10530          "VolumeGain: %.2f\r\n"
10531          "CanReview: %s\r\n"
10532          "CallOperator: %s\r\n"
10533          "MaxMessageCount: %d\r\n"
10534          "MaxMessageLength: %d\r\n"
10535          "NewMessageCount: %d\r\n"
10536 #ifdef IMAP_STORAGE
10537          "OldMessageCount: %d\r\n"
10538          "IMAPUser: %s\r\n"
10539 #endif
10540          "\r\n",
10541          actionid,
10542          vmu->context,
10543          vmu->mailbox,
10544          vmu->fullname,
10545          vmu->email,
10546          vmu->pager,
10547          vmu->serveremail,
10548          vmu->mailcmd,
10549          vmu->language,
10550          vmu->zonetag,
10551          vmu->callback,
10552          vmu->dialout,
10553          vmu->uniqueid,
10554          vmu->exit,
10555          vmu->saydurationm,
10556          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10557          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10558          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10559          vmu->attachfmt,
10560          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10561          vmu->volgain,
10562          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10563          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10564          vmu->maxmsg,
10565          vmu->maxsecs,
10566 #ifdef IMAP_STORAGE
10567          new, old, vmu->imapuser
10568 #else
10569          count_messages(vmu, dirname)
10570 #endif
10571          );
10572    }     
10573    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10574 
10575    AST_LIST_UNLOCK(&users);
10576 
10577    return RESULT_SUCCESS;
10578 }
10579 
10580 /*! \brief Free the users structure. */
10581 static void free_vm_users(void) 
10582 {
10583    struct ast_vm_user *current;
10584    AST_LIST_LOCK(&users);
10585    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10586       ast_set_flag(current, VM_ALLOCED);
10587       free_user(current);
10588    }
10589    AST_LIST_UNLOCK(&users);
10590 }
10591 
10592 /*! \brief Free the zones structure. */
10593 static void free_vm_zones(void)
10594 {
10595    struct vm_zone *zcur;
10596    AST_LIST_LOCK(&zones);
10597    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10598       free_zone(zcur);
10599    AST_LIST_UNLOCK(&zones);
10600 }
10601 
10602 static const char *substitute_escapes(const char *value)
10603 {
10604    char *current;
10605 
10606    /* Add 16 for fudge factor */
10607    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10608 
10609    ast_str_reset(str);
10610    
10611    /* Substitute strings \r, \n, and \t into the appropriate characters */
10612    for (current = (char *) value; *current; current++) {
10613       if (*current == '\\') {
10614          current++;
10615          if (!*current) {
10616             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10617             break;
10618          }
10619          switch (*current) {
10620          case 'r':
10621             ast_str_append(&str, 0, "\r");
10622             break;
10623          case 'n':
10624 #ifdef IMAP_STORAGE
10625             if (!str->used || str->str[str->used - 1] != '\r') {
10626                ast_str_append(&str, 0, "\r");
10627             }
10628 #endif
10629             ast_str_append(&str, 0, "\n");
10630             break;
10631          case 't':
10632             ast_str_append(&str, 0, "\t");
10633             break;
10634          default:
10635             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10636             break;
10637          }
10638       } else {
10639          ast_str_append(&str, 0, "%c", *current);
10640       }
10641    }
10642 
10643    return ast_str_buffer(str);
10644 }
10645 
10646 static int load_config(int reload)
10647 {
10648    struct ast_vm_user *current;
10649    struct ast_config *cfg, *ucfg;
10650    char *cat;
10651    struct ast_variable *var;
10652    const char *val;
10653    char *q, *stringp, *tmp;
10654    int x;
10655    int tmpadsi[4];
10656    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10657 
10658    ast_unload_realtime("voicemail");
10659    ast_unload_realtime("voicemail_data");
10660 
10661    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10662       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10663          return 0;
10664       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10665          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10666          ucfg = NULL;
10667       }
10668       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10669       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10670          ast_config_destroy(ucfg);
10671          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10672          return 0;
10673       }
10674    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10675       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10676       return 0;
10677    } else {
10678       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10679       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10680          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10681          ucfg = NULL;
10682       }
10683    }
10684 #ifdef IMAP_STORAGE
10685    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10686 #endif
10687    /* set audio control prompts */
10688    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10689    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10690    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10691    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10692    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10693 
10694    /* Free all the users structure */  
10695    free_vm_users();
10696 
10697    /* Free all the zones structure */
10698    free_vm_zones();
10699 
10700    AST_LIST_LOCK(&users);  
10701 
10702    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10703    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10704 
10705    if (cfg) {
10706       /* General settings */
10707 
10708       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10709          val = "default";
10710       ast_copy_string(userscontext, val, sizeof(userscontext));
10711       /* Attach voice message to mail message ? */
10712       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10713          val = "yes";
10714       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10715 
10716       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10717          val = "no";
10718       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10719 
10720       volgain = 0.0;
10721       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10722          sscanf(val, "%30lf", &volgain);
10723 
10724 #ifdef ODBC_STORAGE
10725       strcpy(odbc_database, "asterisk");
10726       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10727          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10728       }
10729       strcpy(odbc_table, "voicemessages");
10730       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10731          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10732       }
10733 #endif      
10734       /* Mail command */
10735       strcpy(mailcmd, SENDMAIL);
10736       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10737          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10738 
10739       maxsilence = 0;
10740       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10741          maxsilence = atoi(val);
10742          if (maxsilence > 0)
10743             maxsilence *= 1000;
10744       }
10745       
10746       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10747          maxmsg = MAXMSG;
10748       } else {
10749          maxmsg = atoi(val);
10750          if (maxmsg <= 0) {
10751             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10752             maxmsg = MAXMSG;
10753          } else if (maxmsg > MAXMSGLIMIT) {
10754             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10755             maxmsg = MAXMSGLIMIT;
10756          }
10757       }
10758 
10759       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10760          maxdeletedmsg = 0;
10761       } else {
10762          if (sscanf(val, "%30d", &x) == 1)
10763             maxdeletedmsg = x;
10764          else if (ast_true(val))
10765             maxdeletedmsg = MAXMSG;
10766          else
10767             maxdeletedmsg = 0;
10768 
10769          if (maxdeletedmsg < 0) {
10770             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10771             maxdeletedmsg = MAXMSG;
10772          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10773             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10774             maxdeletedmsg = MAXMSGLIMIT;
10775          }
10776       }
10777 
10778       /* Load date format config for voicemail mail */
10779       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10780          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10781       }
10782 
10783       /* External password changing command */
10784       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10785          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10786          pwdchange = PWDCHANGE_EXTERNAL;
10787       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10788          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10789          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10790       }
10791  
10792       /* External password validation command */
10793       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10794          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10795          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10796       }
10797 
10798 #ifdef IMAP_STORAGE
10799       /* IMAP server address */
10800       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10801          ast_copy_string(imapserver, val, sizeof(imapserver));
10802       } else {
10803          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10804       }
10805       /* IMAP server port */
10806       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10807          ast_copy_string(imapport, val, sizeof(imapport));
10808       } else {
10809          ast_copy_string(imapport,"143", sizeof(imapport));
10810       }
10811       /* IMAP server flags */
10812       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10813          ast_copy_string(imapflags, val, sizeof(imapflags));
10814       }
10815       /* IMAP server master username */
10816       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10817          ast_copy_string(authuser, val, sizeof(authuser));
10818       }
10819       /* IMAP server master password */
10820       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10821          ast_copy_string(authpassword, val, sizeof(authpassword));
10822       }
10823       /* Expunge on exit */
10824       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10825          if (ast_false(val))
10826             expungeonhangup = 0;
10827          else
10828             expungeonhangup = 1;
10829       } else {
10830          expungeonhangup = 1;
10831       }
10832       /* IMAP voicemail folder */
10833       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10834          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10835       } else {
10836          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10837       }
10838       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10839          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10840       }
10841       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10842          imapgreetings = ast_true(val);
10843       } else {
10844          imapgreetings = 0;
10845       }
10846       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10847          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10848       } else {
10849          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10850       }
10851 
10852       /* There is some very unorthodox casting done here. This is due
10853        * to the way c-client handles the argument passed in. It expects a 
10854        * void pointer and casts the pointer directly to a long without
10855        * first dereferencing it. */
10856       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10857          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10858       } else {
10859          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10860       }
10861 
10862       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10863          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10864       } else {
10865          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10866       }
10867 
10868       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10869          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10870       } else {
10871          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10872       }
10873 
10874       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10875          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10876       } else {
10877          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10878       }
10879 
10880       /* Increment configuration version */
10881       imapversion++;
10882 #endif
10883       /* External voicemail notify application */
10884       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10885          ast_copy_string(externnotify, val, sizeof(externnotify));
10886          ast_debug(1, "found externnotify: %s\n", externnotify);
10887       } else {
10888          externnotify[0] = '\0';
10889       }
10890 
10891       /* SMDI voicemail notification */
10892       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10893          ast_debug(1, "Enabled SMDI voicemail notification\n");
10894          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10895             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10896          } else {
10897             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10898             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10899          }
10900          if (!smdi_iface) {
10901             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10902          } 
10903       }
10904 
10905       /* Silence treshold */
10906       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10907       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10908          silencethreshold = atoi(val);
10909       
10910       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10911          val = ASTERISK_USERNAME;
10912       ast_copy_string(serveremail, val, sizeof(serveremail));
10913       
10914       vmmaxsecs = 0;
10915       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10916          if (sscanf(val, "%30d", &x) == 1) {
10917             vmmaxsecs = x;
10918          } else {
10919             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10920          }
10921       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10922          static int maxmessage_deprecate = 0;
10923          if (maxmessage_deprecate == 0) {
10924             maxmessage_deprecate = 1;
10925             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10926          }
10927          if (sscanf(val, "%30d", &x) == 1) {
10928             vmmaxsecs = x;
10929          } else {
10930             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10931          }
10932       }
10933 
10934       vmminsecs = 0;
10935       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10936          if (sscanf(val, "%30d", &x) == 1) {
10937             vmminsecs = x;
10938             if (maxsilence / 1000 >= vmminsecs) {
10939                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10940             }
10941          } else {
10942             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10943          }
10944       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10945          static int maxmessage_deprecate = 0;
10946          if (maxmessage_deprecate == 0) {
10947             maxmessage_deprecate = 1;
10948             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10949          }
10950          if (sscanf(val, "%30d", &x) == 1) {
10951             vmminsecs = x;
10952             if (maxsilence / 1000 >= vmminsecs) {
10953                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10954             }
10955          } else {
10956             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10957          }
10958       }
10959 
10960       val = ast_variable_retrieve(cfg, "general", "format");
10961       if (!val) {
10962          val = "wav";   
10963       } else {
10964          tmp = ast_strdupa(val);
10965          val = ast_format_str_reduce(tmp);
10966          if (!val) {
10967             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10968             val = "wav";
10969          }
10970       }
10971       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10972 
10973       skipms = 3000;
10974       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10975          if (sscanf(val, "%30d", &x) == 1) {
10976             maxgreet = x;
10977          } else {
10978             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10979          }
10980       }
10981 
10982       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10983          if (sscanf(val, "%30d", &x) == 1) {
10984             skipms = x;
10985          } else {
10986             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10987          }
10988       }
10989 
10990       maxlogins = 3;
10991       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10992          if (sscanf(val, "%30d", &x) == 1) {
10993             maxlogins = x;
10994          } else {
10995             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10996          }
10997       }
10998 
10999       minpassword = MINPASSWORD;
11000       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11001          if (sscanf(val, "%30d", &x) == 1) {
11002             minpassword = x;
11003          } else {
11004             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11005          }
11006       }
11007 
11008       /* Force new user to record name ? */
11009       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11010          val = "no";
11011       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11012 
11013       /* Force new user to record greetings ? */
11014       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11015          val = "no";
11016       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11017 
11018       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11019          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11020          stringp = ast_strdupa(val);
11021          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11022             if (!ast_strlen_zero(stringp)) {
11023                q = strsep(&stringp, ",");
11024                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11025                   q++;
11026                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11027                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11028             } else {
11029                cidinternalcontexts[x][0] = '\0';
11030             }
11031          }
11032       }
11033       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11034          ast_debug(1,"VM Review Option disabled globally\n");
11035          val = "no";
11036       }
11037       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11038 
11039       /* Temporary greeting reminder */
11040       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11041          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11042          val = "no";
11043       } else {
11044          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11045       }
11046       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11047       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11048          ast_debug(1, "VM next message wrap disabled globally\n");
11049          val = "no";
11050       }
11051       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11052 
11053       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11054          ast_debug(1,"VM Operator break disabled globally\n");
11055          val = "no";
11056       }
11057       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11058 
11059       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11060          ast_debug(1,"VM CID Info before msg disabled globally\n");
11061          val = "no";
11062       } 
11063       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11064 
11065       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
11066          ast_debug(1,"Send Voicemail msg disabled globally\n");
11067          val = "no";
11068       }
11069       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11070    
11071       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11072          ast_debug(1,"ENVELOPE before msg enabled globally\n");
11073          val = "yes";
11074       }
11075       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11076 
11077       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11078          ast_debug(1,"Move Heard enabled globally\n");
11079          val = "yes";
11080       }
11081       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11082 
11083       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11084          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11085          val = "no";
11086       }
11087       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11088 
11089       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11090          ast_debug(1,"Duration info before msg enabled globally\n");
11091          val = "yes";
11092       }
11093       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11094 
11095       saydurationminfo = 2;
11096       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11097          if (sscanf(val, "%30d", &x) == 1) {
11098             saydurationminfo = x;
11099          } else {
11100             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11101          }
11102       }
11103 
11104       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11105          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
11106          val = "no";
11107       }
11108       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11109 
11110       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11111          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11112          ast_debug(1, "found dialout context: %s\n", dialcontext);
11113       } else {
11114          dialcontext[0] = '\0';  
11115       }
11116       
11117       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11118          ast_copy_string(callcontext, val, sizeof(callcontext));
11119          ast_debug(1, "found callback context: %s\n", callcontext);
11120       } else {
11121          callcontext[0] = '\0';
11122       }
11123 
11124       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11125          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11126          ast_debug(1, "found operator context: %s\n", exitcontext);
11127       } else {
11128          exitcontext[0] = '\0';
11129       }
11130       
11131       /* load password sounds configuration */
11132       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11133          ast_copy_string(vm_password, val, sizeof(vm_password));
11134       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11135          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11136       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11137          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11138       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11139          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11140       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11141          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11142       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11143          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11144       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11145          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11146       }
11147       /* load configurable audio prompts */
11148       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11149          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11150       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11151          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11152       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11153          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11154       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11155          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11156       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11157          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11158 
11159       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11160          val = "no";
11161       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11162 
11163       poll_freq = DEFAULT_POLL_FREQ;
11164       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11165          if (sscanf(val, "%30u", &poll_freq) != 1) {
11166             poll_freq = DEFAULT_POLL_FREQ;
11167             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11168          }
11169       }
11170 
11171       poll_mailboxes = 0;
11172       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11173          poll_mailboxes = ast_true(val);
11174 
11175       if (ucfg) { 
11176          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11177             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11178                continue;
11179             if ((current = find_or_create(userscontext, cat))) {
11180                populate_defaults(current);
11181                apply_options_full(current, ast_variable_browse(ucfg, cat));
11182                ast_copy_string(current->context, userscontext, sizeof(current->context));
11183             }
11184          }
11185          ast_config_destroy(ucfg);
11186       }
11187       cat = ast_category_browse(cfg, NULL);
11188       while (cat) {
11189          if (strcasecmp(cat, "general")) {
11190             var = ast_variable_browse(cfg, cat);
11191             if (strcasecmp(cat, "zonemessages")) {
11192                /* Process mailboxes in this context */
11193                while (var) {
11194                   append_mailbox(cat, var->name, var->value);
11195                   var = var->next;
11196                }
11197             } else {
11198                /* Timezones in this context */
11199                while (var) {
11200                   struct vm_zone *z;
11201                   if ((z = ast_malloc(sizeof(*z)))) {
11202                      char *msg_format, *tzone;
11203                      msg_format = ast_strdupa(var->value);
11204                      tzone = strsep(&msg_format, "|");
11205                      if (msg_format) {
11206                         ast_copy_string(z->name, var->name, sizeof(z->name));
11207                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11208                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11209                         AST_LIST_LOCK(&zones);
11210                         AST_LIST_INSERT_HEAD(&zones, z, list);
11211                         AST_LIST_UNLOCK(&zones);
11212                      } else {
11213                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11214                         ast_free(z);
11215                      }
11216                   } else {
11217                      AST_LIST_UNLOCK(&users);
11218                      ast_config_destroy(cfg);
11219                      return -1;
11220                   }
11221                   var = var->next;
11222                }
11223             }
11224          }
11225          cat = ast_category_browse(cfg, cat);
11226       }
11227       memset(fromstring, 0, sizeof(fromstring));
11228       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11229       strcpy(charset, "ISO-8859-1");
11230       if (emailbody) {
11231          ast_free(emailbody);
11232          emailbody = NULL;
11233       }
11234       if (emailsubject) {
11235          ast_free(emailsubject);
11236          emailsubject = NULL;
11237       }
11238       if (pagerbody) {
11239          ast_free(pagerbody);
11240          pagerbody = NULL;
11241       }
11242       if (pagersubject) {
11243          ast_free(pagersubject);
11244          pagersubject = NULL;
11245       }
11246       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11247          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11248       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11249          ast_copy_string(fromstring, val, sizeof(fromstring));
11250       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11251          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11252       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11253          ast_copy_string(charset, val, sizeof(charset));
11254       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11255          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11256          for (x = 0; x < 4; x++) {
11257             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11258          }
11259       }
11260       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11261          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11262          for (x = 0; x < 4; x++) {
11263             memcpy(&adsisec[x], &tmpadsi[x], 1);
11264          }
11265       }
11266       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11267          if (atoi(val)) {
11268             adsiver = atoi(val);
11269          }
11270       }
11271       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11272          ast_copy_string(zonetag, val, sizeof(zonetag));
11273       }
11274       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11275          emailsubject = ast_strdup(val);
11276       }
11277       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11278          emailbody = ast_strdup(substitute_escapes(val));
11279       }
11280       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11281          pagersubject = ast_strdup(val);
11282       }
11283       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11284          pagerbody = ast_strdup(substitute_escapes(val));
11285       }
11286       AST_LIST_UNLOCK(&users);
11287       ast_config_destroy(cfg);
11288 
11289       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11290          start_poll_thread();
11291       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11292          stop_poll_thread();;
11293 
11294       return 0;
11295    } else {
11296       AST_LIST_UNLOCK(&users);
11297       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11298       if (ucfg)
11299          ast_config_destroy(ucfg);
11300       return 0;
11301    }
11302 }
11303 
11304 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11305 {
11306    int res = -1;
11307    char dir[PATH_MAX];
11308    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11309    ast_debug(2, "About to try retrieving name file %s\n", dir);
11310    RETRIEVE(dir, -1, mailbox, context);
11311    if (ast_fileexists(dir, NULL, NULL)) {
11312       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11313    }
11314    DISPOSE(dir, -1);
11315    return res;
11316 }
11317 
11318 static int reload(void)
11319 {
11320    return load_config(1);
11321 }
11322 
11323 static int unload_module(void)
11324 {
11325    int res;
11326 
11327    res = ast_unregister_application(app);
11328    res |= ast_unregister_application(app2);
11329    res |= ast_unregister_application(app3);
11330    res |= ast_unregister_application(app4);
11331    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11332    res |= ast_manager_unregister("VoicemailUsersList");
11333    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11334    ast_uninstall_vm_functions();
11335    ao2_ref(inprocess_container, -1);
11336 
11337    if (poll_thread != AST_PTHREADT_NULL)
11338       stop_poll_thread();
11339 
11340    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11341    ast_unload_realtime("voicemail");
11342    ast_unload_realtime("voicemail_data");
11343 
11344    free_vm_users();
11345    free_vm_zones();
11346    return res;
11347 }
11348 
11349 static int load_module(void)
11350 {
11351    int res;
11352    my_umask = umask(0);
11353    umask(my_umask);
11354 
11355    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11356       return AST_MODULE_LOAD_DECLINE;
11357    }
11358 
11359    /* compute the location of the voicemail spool directory */
11360    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11361 
11362    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11363       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11364    }
11365 
11366    if ((res = load_config(0)))
11367       return res;
11368 
11369    res = ast_register_application_xml(app, vm_exec);
11370    res |= ast_register_application_xml(app2, vm_execmain);
11371    res |= ast_register_application_xml(app3, vm_box_exists);
11372    res |= ast_register_application_xml(app4, vmauthenticate);
11373    res |= ast_custom_function_register(&mailbox_exists_acf);
11374    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11375    if (res)
11376       return res;
11377 
11378    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11379 
11380    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11381    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11382    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11383 
11384    return res;
11385 }
11386 
11387 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11388 {
11389    int cmd = 0;
11390    char destination[80] = "";
11391    int retries = 0;
11392 
11393    if (!num) {
11394       ast_verb(3, "Destination number will be entered manually\n");
11395       while (retries < 3 && cmd != 't') {
11396          destination[1] = '\0';
11397          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11398          if (!cmd)
11399             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11400          if (!cmd)
11401             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11402          if (!cmd) {
11403             cmd = ast_waitfordigit(chan, 6000);
11404             if (cmd)
11405                destination[0] = cmd;
11406          }
11407          if (!cmd) {
11408             retries++;
11409          } else {
11410 
11411             if (cmd < 0)
11412                return 0;
11413             if (cmd == '*') {
11414                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11415                return 0;
11416             }
11417             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11418                retries++;
11419             else
11420                cmd = 't';
11421          }
11422       }
11423       if (retries >= 3) {
11424          return 0;
11425       }
11426       
11427    } else {
11428       if (option_verbose > 2)
11429          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11430       ast_copy_string(destination, num, sizeof(destination));
11431    }
11432 
11433    if (!ast_strlen_zero(destination)) {
11434       if (destination[strlen(destination) -1 ] == '*')
11435          return 0; 
11436       if (option_verbose > 2)
11437          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11438       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11439       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11440       chan->priority = 0;
11441       return 9;
11442    }
11443    return 0;
11444 }
11445 
11446 /*!
11447  * \brief The advanced options within a message.
11448  * \param chan
11449  * \param vmu 
11450  * \param vms
11451  * \param msg
11452  * \param option
11453  * \param record_gain
11454  *
11455  * Provides handling for the play message envelope, call the person back, or reply to message. 
11456  *
11457  * \return zero on success, -1 on error.
11458  */
11459 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)
11460 {
11461    int res = 0;
11462    char filename[PATH_MAX];
11463    struct ast_config *msg_cfg = NULL;
11464    const char *origtime, *context;
11465    char *name, *num;
11466    int retries = 0;
11467    char *cid;
11468    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11469 
11470    vms->starting = 0; 
11471 
11472    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11473 
11474    /* Retrieve info from VM attribute file */
11475    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11476    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11477    msg_cfg = ast_config_load(filename, config_flags);
11478    DISPOSE(vms->curdir, vms->curmsg);
11479    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11480       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11481       return 0;
11482    }
11483 
11484    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11485       ast_config_destroy(msg_cfg);
11486       return 0;
11487    }
11488 
11489    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11490 
11491    context = ast_variable_retrieve(msg_cfg, "message", "context");
11492    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11493       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11494    switch (option) {
11495    case 3: /* Play message envelope */
11496       if (!res)
11497          res = play_message_datetime(chan, vmu, origtime, filename);
11498       if (!res)
11499          res = play_message_callerid(chan, vms, cid, context, 0);
11500 
11501       res = 't';
11502       break;
11503 
11504    case 2:  /* Call back */
11505 
11506       if (ast_strlen_zero(cid))
11507          break;
11508 
11509       ast_callerid_parse(cid, &name, &num);
11510       while ((res > -1) && (res != 't')) {
11511          switch (res) {
11512          case '1':
11513             if (num) {
11514                /* Dial the CID number */
11515                res = dialout(chan, vmu, num, vmu->callback);
11516                if (res) {
11517                   ast_config_destroy(msg_cfg);
11518                   return 9;
11519                }
11520             } else {
11521                res = '2';
11522             }
11523             break;
11524 
11525          case '2':
11526             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11527             if (!ast_strlen_zero(vmu->dialout)) {
11528                res = dialout(chan, vmu, NULL, vmu->dialout);
11529                if (res) {
11530                   ast_config_destroy(msg_cfg);
11531                   return 9;
11532                }
11533             } else {
11534                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11535                res = ast_play_and_wait(chan, "vm-sorry");
11536             }
11537             ast_config_destroy(msg_cfg);
11538             return res;
11539          case '*':
11540             res = 't';
11541             break;
11542          case '3':
11543          case '4':
11544          case '5':
11545          case '6':
11546          case '7':
11547          case '8':
11548          case '9':
11549          case '0':
11550 
11551             res = ast_play_and_wait(chan, "vm-sorry");
11552             retries++;
11553             break;
11554          default:
11555             if (num) {
11556                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11557                res = ast_play_and_wait(chan, "vm-num-i-have");
11558                if (!res)
11559                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11560                if (!res)
11561                   res = ast_play_and_wait(chan, "vm-tocallnum");
11562                /* Only prompt for a caller-specified number if there is a dialout context specified */
11563                if (!ast_strlen_zero(vmu->dialout)) {
11564                   if (!res)
11565                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11566                }
11567             } else {
11568                res = ast_play_and_wait(chan, "vm-nonumber");
11569                if (!ast_strlen_zero(vmu->dialout)) {
11570                   if (!res)
11571                      res = ast_play_and_wait(chan, "vm-toenternumber");
11572                }
11573             }
11574             if (!res)
11575                res = ast_play_and_wait(chan, "vm-star-cancel");
11576             if (!res)
11577                res = ast_waitfordigit(chan, 6000);
11578             if (!res) {
11579                retries++;
11580                if (retries > 3)
11581                   res = 't';
11582             }
11583             break; 
11584             
11585          }
11586          if (res == 't')
11587             res = 0;
11588          else if (res == '*')
11589             res = -1;
11590       }
11591       break;
11592       
11593    case 1:  /* Reply */
11594       /* Send reply directly to sender */
11595       if (ast_strlen_zero(cid))
11596          break;
11597 
11598       ast_callerid_parse(cid, &name, &num);
11599       if (!num) {
11600          ast_verb(3, "No CID number available, no reply sent\n");
11601          if (!res)
11602             res = ast_play_and_wait(chan, "vm-nonumber");
11603          ast_config_destroy(msg_cfg);
11604          return res;
11605       } else {
11606          struct ast_vm_user vmu2;
11607          if (find_user(&vmu2, vmu->context, num)) {
11608             struct leave_vm_options leave_options;
11609             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11610             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11611 
11612             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11613             
11614             memset(&leave_options, 0, sizeof(leave_options));
11615             leave_options.record_gain = record_gain;
11616             res = leave_voicemail(chan, mailbox, &leave_options);
11617             if (!res)
11618                res = 't';
11619             ast_config_destroy(msg_cfg);
11620             return res;
11621          } else {
11622             /* Sender has no mailbox, can't reply */
11623             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11624             ast_play_and_wait(chan, "vm-nobox");
11625             res = 't';
11626             ast_config_destroy(msg_cfg);
11627             return res;
11628          }
11629       } 
11630       res = 0;
11631 
11632       break;
11633    }
11634 
11635 #ifndef IMAP_STORAGE
11636    ast_config_destroy(msg_cfg);
11637 
11638    if (!res) {
11639       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11640       vms->heard[msg] = 1;
11641       res = wait_file(chan, vms, vms->fn);
11642    }
11643 #endif
11644    return res;
11645 }
11646 
11647 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11648          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11649          signed char record_gain, struct vm_state *vms, char *flag)
11650 {
11651    /* Record message & let caller review or re-record it, or set options if applicable */
11652    int res = 0;
11653    int cmd = 0;
11654    int max_attempts = 3;
11655    int attempts = 0;
11656    int recorded = 0;
11657    int msg_exists = 0;
11658    signed char zero_gain = 0;
11659    char tempfile[PATH_MAX];
11660    char *acceptdtmf = "#";
11661    char *canceldtmf = "";
11662 
11663    /* Note that urgent and private are for flagging messages as such in the future */
11664 
11665    /* barf if no pointer passed to store duration in */
11666    if (duration == NULL) {
11667       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11668       return -1;
11669    }
11670 
11671    if (!outsidecaller)
11672       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11673    else
11674       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11675 
11676    cmd = '3';  /* Want to start by recording */
11677 
11678    while ((cmd >= 0) && (cmd != 't')) {
11679       switch (cmd) {
11680       case '1':
11681          if (!msg_exists) {
11682             /* In this case, 1 is to record a message */
11683             cmd = '3';
11684             break;
11685          } else {
11686             /* Otherwise 1 is to save the existing message */
11687             ast_verb(3, "Saving message as is\n");
11688             if (!outsidecaller) 
11689                ast_filerename(tempfile, recordfile, NULL);
11690             ast_stream_and_wait(chan, "vm-msgsaved", "");
11691             if (!outsidecaller) {
11692                /* Saves to IMAP server only if imapgreeting=yes */
11693                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11694                DISPOSE(recordfile, -1);
11695             }
11696             cmd = 't';
11697             return res;
11698          }
11699       case '2':
11700          /* Review */
11701          ast_verb(3, "Reviewing the message\n");
11702          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11703          break;
11704       case '3':
11705          msg_exists = 0;
11706          /* Record */
11707          if (recorded == 1) 
11708             ast_verb(3, "Re-recording the message\n");
11709          else  
11710             ast_verb(3, "Recording the message\n");
11711          
11712          if (recorded && outsidecaller) {
11713             cmd = ast_play_and_wait(chan, INTRO);
11714             cmd = ast_play_and_wait(chan, "beep");
11715          }
11716          recorded = 1;
11717          /* 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 */
11718          if (record_gain)
11719             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11720          if (ast_test_flag(vmu, VM_OPERATOR))
11721             canceldtmf = "0";
11722          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11723          if (record_gain)
11724             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11725          if (cmd == -1) {
11726             /* User has hung up, no options to give */
11727             if (!outsidecaller) {
11728                /* user was recording a greeting and they hung up, so let's delete the recording. */
11729                ast_filedelete(tempfile, NULL);
11730             }     
11731             return cmd;
11732          }
11733          if (cmd == '0') {
11734             break;
11735          } else if (cmd == '*') {
11736             break;
11737 #if 0
11738          } else if (vmu->review && (*duration < 5)) {
11739             /* Message is too short */
11740             ast_verb(3, "Message too short\n");
11741             cmd = ast_play_and_wait(chan, "vm-tooshort");
11742             cmd = ast_filedelete(tempfile, NULL);
11743             break;
11744          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11745             /* Message is all silence */
11746             ast_verb(3, "Nothing recorded\n");
11747             cmd = ast_filedelete(tempfile, NULL);
11748             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11749             if (!cmd)
11750                cmd = ast_play_and_wait(chan, "vm-speakup");
11751             break;
11752 #endif
11753          } else {
11754             /* If all is well, a message exists */
11755             msg_exists = 1;
11756             cmd = 0;
11757          }
11758          break;
11759       case '4':
11760          if (outsidecaller) {  /* only mark vm messages */
11761             /* Mark Urgent */
11762             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11763                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11764                res = ast_play_and_wait(chan, "vm-marked-urgent");
11765                strcpy(flag, "Urgent");
11766             } else if (flag) {
11767                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11768                res = ast_play_and_wait(chan, "vm-urgent-removed");
11769                strcpy(flag, "");
11770             } else {
11771                ast_play_and_wait(chan, "vm-sorry");
11772             }
11773             cmd = 0;
11774          } else {
11775             cmd = ast_play_and_wait(chan, "vm-sorry");
11776          }
11777          break;
11778       case '5':
11779       case '6':
11780       case '7':
11781       case '8':
11782       case '9':
11783       case '*':
11784       case '#':
11785          cmd = ast_play_and_wait(chan, "vm-sorry");
11786          break;
11787 #if 0 
11788 /*  XXX Commented out for the moment because of the dangers of deleting
11789     a message while recording (can put the message numbers out of sync) */
11790       case '*':
11791          /* Cancel recording, delete message, offer to take another message*/
11792          cmd = ast_play_and_wait(chan, "vm-deleted");
11793          cmd = ast_filedelete(tempfile, NULL);
11794          if (outsidecaller) {
11795             res = vm_exec(chan, NULL);
11796             return res;
11797          }
11798          else
11799             return 1;
11800 #endif
11801       case '0':
11802          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11803             cmd = ast_play_and_wait(chan, "vm-sorry");
11804             break;
11805          }
11806          if (msg_exists || recorded) {
11807             cmd = ast_play_and_wait(chan, "vm-saveoper");
11808             if (!cmd)
11809                cmd = ast_waitfordigit(chan, 3000);
11810             if (cmd == '1') {
11811                ast_play_and_wait(chan, "vm-msgsaved");
11812                cmd = '0';
11813             } else if (cmd == '4') {
11814                if (flag) {
11815                   ast_play_and_wait(chan, "vm-marked-urgent");
11816                   strcpy(flag, "Urgent");
11817                }
11818                ast_play_and_wait(chan, "vm-msgsaved");
11819                cmd = '0';
11820             } else {
11821                ast_play_and_wait(chan, "vm-deleted");
11822                DELETE(recordfile, -1, recordfile, vmu);
11823                cmd = '0';
11824             }
11825          }
11826          return cmd;
11827       default:
11828          /* If the caller is an ouside caller, and the review option is enabled,
11829             allow them to review the message, but let the owner of the box review
11830             their OGM's */
11831          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11832             return cmd;
11833          if (msg_exists) {
11834             cmd = ast_play_and_wait(chan, "vm-review");
11835             if (!cmd && outsidecaller) {
11836                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11837                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11838                } else if (flag) {
11839                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11840                }
11841             }
11842          } else {
11843             cmd = ast_play_and_wait(chan, "vm-torerecord");
11844             if (!cmd)
11845                cmd = ast_waitfordigit(chan, 600);
11846          }
11847          
11848          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11849             cmd = ast_play_and_wait(chan, "vm-reachoper");
11850             if (!cmd)
11851                cmd = ast_waitfordigit(chan, 600);
11852          }
11853 #if 0
11854          if (!cmd)
11855             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11856 #endif
11857          if (!cmd)
11858             cmd = ast_waitfordigit(chan, 6000);
11859          if (!cmd) {
11860             attempts++;
11861          }
11862          if (attempts > max_attempts) {
11863             cmd = 't';
11864          }
11865       }
11866    }
11867    if (cmd == 't')
11868       cmd = 0;
11869    else if (outsidecaller) /* won't play if time out occurs */
11870       ast_play_and_wait(chan, "vm-goodbye");
11871    return cmd;
11872 }
11873 
11874 /* This is a workaround so that menuselect displays a proper description
11875  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11876  */
11877 
11878 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11879       .load = load_module,
11880       .unload = unload_module,
11881       .reload = reload,
11882       );