Fri Nov 12 11:45:36 2010

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: 250977 $")
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 #define eol "\r\n"
00409 
00410 #define MAX_DATETIME_FORMAT   512
00411 #define MAX_NUM_CID_CONTEXTS 10
00412 
00413 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00414 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00415 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00416 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00417 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00418 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00419 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00420 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00421 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00422 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00423 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00424 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00425 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00426 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00427 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00428 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00429 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00430 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00431 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00432 #define ERROR_LOCK_PATH  -100
00433 
00434 
00435 enum {
00436    NEW_FOLDER,
00437    OLD_FOLDER,
00438    WORK_FOLDER,
00439    FAMILY_FOLDER,
00440    FRIENDS_FOLDER,
00441    GREETINGS_FOLDER
00442 } vm_box;
00443 
00444 enum {
00445    OPT_SILENT =           (1 << 0),
00446    OPT_BUSY_GREETING =    (1 << 1),
00447    OPT_UNAVAIL_GREETING = (1 << 2),
00448    OPT_RECORDGAIN =       (1 << 3),
00449    OPT_PREPEND_MAILBOX =  (1 << 4),
00450    OPT_AUTOPLAY =         (1 << 6),
00451    OPT_DTMFEXIT =         (1 << 7),
00452    OPT_MESSAGE_Urgent =   (1 << 8),
00453    OPT_MESSAGE_PRIORITY = (1 << 9)
00454 } vm_option_flags;
00455 
00456 enum {
00457    OPT_ARG_RECORDGAIN = 0,
00458    OPT_ARG_PLAYFOLDER = 1,
00459    OPT_ARG_DTMFEXIT   = 2,
00460    /* This *must* be the last value in this enum! */
00461    OPT_ARG_ARRAY_SIZE = 3,
00462 } vm_option_args;
00463 
00464 AST_APP_OPTIONS(vm_app_options, {
00465    AST_APP_OPTION('s', OPT_SILENT),
00466    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00467    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00468    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00469    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00470    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00471    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00472    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00473    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00474 });
00475 
00476 static int load_config(int reload);
00477 
00478 /*! \page vmlang Voicemail Language Syntaxes Supported
00479 
00480    \par Syntaxes supported, not really language codes.
00481    \arg \b en    - English
00482    \arg \b de    - German
00483    \arg \b es    - Spanish
00484    \arg \b fr    - French
00485    \arg \b it    - Italian
00486    \arg \b nl    - Dutch
00487    \arg \b pt    - Portuguese
00488    \arg \b pt_BR - Portuguese (Brazil)
00489    \arg \b gr    - Greek
00490    \arg \b no    - Norwegian
00491    \arg \b se    - Swedish
00492    \arg \b tw    - Chinese (Taiwan)
00493    \arg \b ua - Ukrainian
00494 
00495 German requires the following additional soundfile:
00496 \arg \b 1F  einE (feminine)
00497 
00498 Spanish requires the following additional soundfile:
00499 \arg \b 1M      un (masculine)
00500 
00501 Dutch, Portuguese & Spanish require the following additional soundfiles:
00502 \arg \b vm-INBOXs singular of 'new'
00503 \arg \b vm-Olds      singular of 'old/heard/read'
00504 
00505 NB these are plural:
00506 \arg \b vm-INBOX  nieuwe (nl)
00507 \arg \b vm-Old    oude (nl)
00508 
00509 Polish uses:
00510 \arg \b vm-new-a  'new', feminine singular accusative
00511 \arg \b vm-new-e  'new', feminine plural accusative
00512 \arg \b vm-new-ych   'new', feminine plural genitive
00513 \arg \b vm-old-a  'old', feminine singular accusative
00514 \arg \b vm-old-e  'old', feminine plural accusative
00515 \arg \b vm-old-ych   'old', feminine plural genitive
00516 \arg \b digits/1-a   'one', not always same as 'digits/1'
00517 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00518 
00519 Swedish uses:
00520 \arg \b vm-nytt      singular of 'new'
00521 \arg \b vm-nya    plural of 'new'
00522 \arg \b vm-gammalt   singular of 'old'
00523 \arg \b vm-gamla  plural of 'old'
00524 \arg \b digits/ett   'one', not always same as 'digits/1'
00525 
00526 Norwegian uses:
00527 \arg \b vm-ny     singular of 'new'
00528 \arg \b vm-nye    plural of 'new'
00529 \arg \b vm-gammel singular of 'old'
00530 \arg \b vm-gamle  plural of 'old'
00531 
00532 Dutch also uses:
00533 \arg \b nl-om     'at'?
00534 
00535 Spanish also uses:
00536 \arg \b vm-youhaveno
00537 
00538 Italian requires the following additional soundfile:
00539 
00540 For vm_intro_it:
00541 \arg \b vm-nuovo  new
00542 \arg \b vm-nuovi  new plural
00543 \arg \b vm-vecchio   old
00544 \arg \b vm-vecchi old plural
00545 
00546 Chinese (Taiwan) requires the following additional soundfile:
00547 \arg \b vm-tong      A class-word for call (tong1)
00548 \arg \b vm-ri     A class-word for day (ri4)
00549 \arg \b vm-you    You (ni3)
00550 \arg \b vm-haveno   Have no (mei2 you3)
00551 \arg \b vm-have     Have (you3)
00552 \arg \b vm-listen   To listen (yao4 ting1)
00553 
00554 
00555 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00556 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00557 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00558 
00559 */
00560 
00561 struct baseio {
00562    int iocp;
00563    int iolen;
00564    int linelength;
00565    int ateof;
00566    unsigned char iobuf[BASEMAXINLINE];
00567 };
00568 
00569 /*! Structure for linked list of users 
00570  * Use ast_vm_user_destroy() to free one of these structures. */
00571 struct ast_vm_user {
00572    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00573    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00574    char password[80];               /*!< Secret pin code, numbers only */
00575    char fullname[80];               /*!< Full name, for directory app */
00576    char email[80];                  /*!< E-mail address */
00577    char *emailsubject;              /*!< E-mail subject */
00578    char *emailbody;                 /*!< E-mail body */
00579    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00580    char serveremail[80];            /*!< From: Mail address */
00581    char mailcmd[160];               /*!< Configurable mail command */
00582    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00583    char zonetag[80];                /*!< Time zone */
00584    char callback[80];
00585    char dialout[80];
00586    char uniqueid[80];               /*!< Unique integer identifier */
00587    char exit[80];
00588    char attachfmt[20];              /*!< Attachment format */
00589    unsigned int flags;              /*!< VM_ flags */ 
00590    int saydurationm;
00591    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00592    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00593    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00594 #ifdef IMAP_STORAGE
00595    char imapuser[80];               /*!< IMAP server login */
00596    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00597    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00598    int imapversion;                 /*!< If configuration changes, use the new values */
00599 #endif
00600    double volgain;                  /*!< Volume gain for voicemails sent via email */
00601    AST_LIST_ENTRY(ast_vm_user) list;
00602 };
00603 
00604 /*! Voicemail time zones */
00605 struct vm_zone {
00606    AST_LIST_ENTRY(vm_zone) list;
00607    char name[80];
00608    char timezone[80];
00609    char msg_format[512];
00610 };
00611 
00612 #define VMSTATE_MAX_MSG_ARRAY 256
00613 
00614 /*! Voicemail mailbox state */
00615 struct vm_state {
00616    char curbox[80];
00617    char username[80];
00618    char context[80];
00619    char curdir[PATH_MAX];
00620    char vmbox[PATH_MAX];
00621    char fn[PATH_MAX];
00622    char intro[PATH_MAX];
00623    int *deleted;
00624    int *heard;
00625    int curmsg;
00626    int lastmsg;
00627    int newmessages;
00628    int oldmessages;
00629    int urgentmessages;
00630    int starting;
00631    int repeats;
00632 #ifdef IMAP_STORAGE
00633    ast_mutex_t lock;
00634    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00635    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00636    MAILSTREAM *mailstream;
00637    int vmArrayIndex;
00638    char imapuser[80];                   /*!< IMAP server login */
00639    int imapversion;
00640    int interactive;
00641    char introfn[PATH_MAX];              /*!< Name of prepended file */
00642    unsigned int quota_limit;
00643    unsigned int quota_usage;
00644    struct vm_state *persist_vms;
00645 #endif
00646 };
00647 
00648 #ifdef ODBC_STORAGE
00649 static char odbc_database[80];
00650 static char odbc_table[80];
00651 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00652 #define DISPOSE(a,b) remove_file(a,b)
00653 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00654 #define EXISTS(a,b,c,d) (message_exists(a,b))
00655 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00656 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00657 #define DELETE(a,b,c,d) (delete_file(a,b))
00658 #else
00659 #ifdef IMAP_STORAGE
00660 #define DISPOSE(a,b) (imap_remove_file(a,b))
00661 #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))
00662 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00663 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00664 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00665 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00666 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00667 #else
00668 #define RETRIEVE(a,b,c,d)
00669 #define DISPOSE(a,b)
00670 #define STORE(a,b,c,d,e,f,g,h,i,j)
00671 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00672 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00673 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00674 #define DELETE(a,b,c,d) (vm_delete(c))
00675 #endif
00676 #endif
00677 
00678 static char VM_SPOOL_DIR[PATH_MAX];
00679 
00680 static char ext_pass_cmd[128];
00681 static char ext_pass_check_cmd[128];
00682 
00683 static int my_umask;
00684 
00685 #define PWDCHANGE_INTERNAL (1 << 1)
00686 #define PWDCHANGE_EXTERNAL (1 << 2)
00687 static int pwdchange = PWDCHANGE_INTERNAL;
00688 
00689 #ifdef ODBC_STORAGE
00690 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00691 #else
00692 # ifdef IMAP_STORAGE
00693 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00694 # else
00695 # define tdesc "Comedian Mail (Voicemail System)"
00696 # endif
00697 #endif
00698 
00699 static char userscontext[AST_MAX_EXTENSION] = "default";
00700 
00701 static char *addesc = "Comedian Mail";
00702 
00703 /* Leave a message */
00704 static char *app = "VoiceMail";
00705 
00706 /* Check mail, control, etc */
00707 static char *app2 = "VoiceMailMain";
00708 
00709 static char *app3 = "MailboxExists";
00710 static char *app4 = "VMAuthenticate";
00711 
00712 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00713 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00714 static char zonetag[80];
00715 static int maxsilence;
00716 static int maxmsg;
00717 static int maxdeletedmsg;
00718 static int silencethreshold = 128;
00719 static char serveremail[80];
00720 static char mailcmd[160];  /* Configurable mail cmd */
00721 static char externnotify[160]; 
00722 static struct ast_smdi_interface *smdi_iface = NULL;
00723 static char vmfmts[80];
00724 static double volgain;
00725 static int vmminsecs;
00726 static int vmmaxsecs;
00727 static int maxgreet;
00728 static int skipms;
00729 static int maxlogins;
00730 static int minpassword;
00731 
00732 /*! Poll mailboxes for changes since there is something external to
00733  *  app_voicemail that may change them. */
00734 static unsigned int poll_mailboxes;
00735 
00736 /*! Polling frequency */
00737 static unsigned int poll_freq;
00738 /*! By default, poll every 30 seconds */
00739 #define DEFAULT_POLL_FREQ 30
00740 
00741 AST_MUTEX_DEFINE_STATIC(poll_lock);
00742 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00743 static pthread_t poll_thread = AST_PTHREADT_NULL;
00744 static unsigned char poll_thread_run;
00745 
00746 /*! Subscription to ... MWI event subscriptions */
00747 static struct ast_event_sub *mwi_sub_sub;
00748 /*! Subscription to ... MWI event un-subscriptions */
00749 static struct ast_event_sub *mwi_unsub_sub;
00750 
00751 /*!
00752  * \brief An MWI subscription
00753  *
00754  * This is so we can keep track of which mailboxes are subscribed to.
00755  * This way, we know which mailboxes to poll when the pollmailboxes
00756  * option is being used.
00757  */
00758 struct mwi_sub {
00759    AST_RWLIST_ENTRY(mwi_sub) entry;
00760    int old_urgent;
00761    int old_new;
00762    int old_old;
00763    uint32_t uniqueid;
00764    char mailbox[1];
00765 };
00766 
00767 struct mwi_sub_task {
00768    const char *mailbox;
00769    const char *context;
00770    uint32_t uniqueid;
00771 };
00772 
00773 static struct ast_taskprocessor *mwi_subscription_tps;
00774 
00775 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00776 
00777 /* custom audio control prompts for voicemail playback */
00778 static char listen_control_forward_key[12];
00779 static char listen_control_reverse_key[12];
00780 static char listen_control_pause_key[12];
00781 static char listen_control_restart_key[12];
00782 static char listen_control_stop_key[12];
00783 
00784 /* custom password sounds */
00785 static char vm_password[80] = "vm-password";
00786 static char vm_newpassword[80] = "vm-newpassword";
00787 static char vm_passchanged[80] = "vm-passchanged";
00788 static char vm_reenterpassword[80] = "vm-reenterpassword";
00789 static char vm_mismatch[80] = "vm-mismatch";
00790 static char vm_invalid_password[80] = "vm-invalid-password";
00791 static char vm_pls_try_again[80] = "vm-pls-try-again";
00792 
00793 static struct ast_flags globalflags = {0};
00794 
00795 static int saydurationminfo;
00796 
00797 static char dialcontext[AST_MAX_CONTEXT] = "";
00798 static char callcontext[AST_MAX_CONTEXT] = "";
00799 static char exitcontext[AST_MAX_CONTEXT] = "";
00800 
00801 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00802 
00803 
00804 static char *emailbody = NULL;
00805 static char *emailsubject = NULL;
00806 static char *pagerbody = NULL;
00807 static char *pagersubject = NULL;
00808 static char fromstring[100];
00809 static char pagerfromstring[100];
00810 static char charset[32] = "ISO-8859-1";
00811 
00812 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00813 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00814 static int adsiver = 1;
00815 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00816 
00817 /* Forward declarations - generic */
00818 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00819 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);
00820 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00821 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00822          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00823          signed char record_gain, struct vm_state *vms, char *flag);
00824 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00825 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00826 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);
00827 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);
00828 static void apply_options(struct ast_vm_user *vmu, const char *options);
00829 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);
00830 static int is_valid_dtmf(const char *key);
00831 
00832 struct ao2_container *inprocess_container;
00833 
00834 struct inprocess {
00835    int count;
00836    char *context;
00837    char mailbox[0];
00838 };
00839 
00840 static int inprocess_hash_fn(const void *obj, const int flags)
00841 {
00842    const struct inprocess *i = obj;
00843    return atoi(i->mailbox);
00844 }
00845 
00846 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00847 {
00848    struct inprocess *i = obj, *j = arg;
00849    if (!strcmp(i->mailbox, j->mailbox)) {
00850       return 0;
00851    }
00852    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00853 }
00854 
00855 static int inprocess_count(const char *context, const char *mailbox, int delta)
00856 {
00857    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00858    arg->context = arg->mailbox + strlen(mailbox) + 1;
00859    strcpy(arg->mailbox, mailbox); /* SAFE */
00860    strcpy(arg->context, context); /* SAFE */
00861    ao2_lock(inprocess_container);
00862    if ((i = ao2_find(inprocess_container, arg, 0))) {
00863       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00864       ao2_unlock(inprocess_container);
00865       ao2_ref(i, -1);
00866       return ret;
00867    }
00868    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00869       ao2_unlock(inprocess_container);
00870       return 0;
00871    }
00872    i->context = i->mailbox + strlen(mailbox) + 1;
00873    strcpy(i->mailbox, mailbox); /* SAFE */
00874    strcpy(i->context, context); /* SAFE */
00875    i->count = delta;
00876    ao2_link(inprocess_container, i);
00877    ao2_unlock(inprocess_container);
00878    ao2_ref(i, -1);
00879    return 0;
00880 }
00881 
00882 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00883 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00884 #endif
00885 
00886 /*!
00887  * \brief Strips control and non 7-bit clean characters from input string.
00888  *
00889  * \note To map control and none 7-bit characters to a 7-bit clean characters
00890  *  please use ast_str_encode_mine().
00891  */
00892 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00893 {
00894    char *bufptr = buf;
00895    for (; *input; input++) {
00896       if (*input < 32) {
00897          continue;
00898       }
00899       *bufptr++ = *input;
00900       if (bufptr == buf + buflen - 1) {
00901          break;
00902       }
00903    }
00904    *bufptr = '\0';
00905    return buf;
00906 }
00907 
00908 
00909 /*!
00910  * \brief Sets default voicemail system options to a voicemail user.
00911  *
00912  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00913  * - all the globalflags
00914  * - the saydurationminfo
00915  * - the callcontext
00916  * - the dialcontext
00917  * - the exitcontext
00918  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00919  * - volume gain.
00920  */
00921 static void populate_defaults(struct ast_vm_user *vmu)
00922 {
00923    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00924    if (saydurationminfo)
00925       vmu->saydurationm = saydurationminfo;
00926    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00927    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00928    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00929    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00930    if (vmmaxsecs)
00931       vmu->maxsecs = vmmaxsecs;
00932    if (maxmsg)
00933       vmu->maxmsg = maxmsg;
00934    if (maxdeletedmsg)
00935       vmu->maxdeletedmsg = maxdeletedmsg;
00936    vmu->volgain = volgain;
00937    vmu->emailsubject = NULL;
00938    vmu->emailbody = NULL;
00939 }
00940 
00941 /*!
00942  * \brief Sets a a specific property value.
00943  * \param vmu The voicemail user object to work with.
00944  * \param var The name of the property to be set.
00945  * \param value The value to be set to the property.
00946  * 
00947  * The property name must be one of the understood properties. See the source for details.
00948  */
00949 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00950 {
00951    int x;
00952    if (!strcasecmp(var, "attach")) {
00953       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00954    } else if (!strcasecmp(var, "attachfmt")) {
00955       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00956    } else if (!strcasecmp(var, "serveremail")) {
00957       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00958    } else if (!strcasecmp(var, "language")) {
00959       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00960    } else if (!strcasecmp(var, "tz")) {
00961       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00962 #ifdef IMAP_STORAGE
00963    } else if (!strcasecmp(var, "imapuser")) {
00964       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00965       vmu->imapversion = imapversion;
00966    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00967       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00968       vmu->imapversion = imapversion;
00969    } else if (!strcasecmp(var, "imapvmshareid")) {
00970       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00971       vmu->imapversion = imapversion;
00972 #endif
00973    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00974       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00975    } else if (!strcasecmp(var, "saycid")){
00976       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00977    } else if (!strcasecmp(var,"sendvoicemail")){
00978       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00979    } else if (!strcasecmp(var, "review")){
00980       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00981    } else if (!strcasecmp(var, "tempgreetwarn")){
00982       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00983    } else if (!strcasecmp(var, "messagewrap")){
00984       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00985    } else if (!strcasecmp(var, "operator")) {
00986       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00987    } else if (!strcasecmp(var, "envelope")){
00988       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00989    } else if (!strcasecmp(var, "moveheard")){
00990       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00991    } else if (!strcasecmp(var, "sayduration")){
00992       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00993    } else if (!strcasecmp(var, "saydurationm")){
00994       if (sscanf(value, "%30d", &x) == 1) {
00995          vmu->saydurationm = x;
00996       } else {
00997          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
00998       }
00999    } else if (!strcasecmp(var, "forcename")){
01000       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01001    } else if (!strcasecmp(var, "forcegreetings")){
01002       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01003    } else if (!strcasecmp(var, "callback")) {
01004       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01005    } else if (!strcasecmp(var, "dialout")) {
01006       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01007    } else if (!strcasecmp(var, "exitcontext")) {
01008       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01009    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01010       vmu->maxsecs = atoi(value);
01011       if (vmu->maxsecs <= 0) {
01012          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01013          vmu->maxsecs = vmmaxsecs;
01014       } else {
01015          vmu->maxsecs = atoi(value);
01016       }
01017       if (!strcasecmp(var, "maxmessage"))
01018          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01019    } else if (!strcasecmp(var, "maxmsg")) {
01020       vmu->maxmsg = atoi(value);
01021       if (vmu->maxmsg <= 0) {
01022          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01023          vmu->maxmsg = MAXMSG;
01024       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01025          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01026          vmu->maxmsg = MAXMSGLIMIT;
01027       }
01028    } else if (!strcasecmp(var, "backupdeleted")) {
01029       if (sscanf(value, "%30d", &x) == 1)
01030          vmu->maxdeletedmsg = x;
01031       else if (ast_true(value))
01032          vmu->maxdeletedmsg = MAXMSG;
01033       else
01034          vmu->maxdeletedmsg = 0;
01035 
01036       if (vmu->maxdeletedmsg < 0) {
01037          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01038          vmu->maxdeletedmsg = MAXMSG;
01039       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01040          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01041          vmu->maxdeletedmsg = MAXMSGLIMIT;
01042       }
01043    } else if (!strcasecmp(var, "volgain")) {
01044       sscanf(value, "%30lf", &vmu->volgain);
01045    } else if (!strcasecmp(var, "options")) {
01046       apply_options(vmu, value);
01047    }
01048 }
01049 
01050 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01051 {
01052    int fds[2], pid = 0;
01053 
01054    memset(buf, 0, len);
01055 
01056    if (pipe(fds)) {
01057       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01058    } else {
01059       /* good to go*/
01060       pid = ast_safe_fork(0);
01061 
01062       if (pid < 0) {
01063          /* ok maybe not */
01064          close(fds[0]);
01065          close(fds[1]);
01066          snprintf(buf, len, "FAILURE: Fork failed");
01067       } else if (pid) {
01068          /* parent */
01069          close(fds[1]);
01070          if (read(fds[0], buf, len) < 0) {
01071             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01072          }
01073          close(fds[0]);
01074       } else {
01075          /*  child */
01076          AST_DECLARE_APP_ARGS(arg,
01077             AST_APP_ARG(v)[20];
01078          );
01079          char *mycmd = ast_strdupa(command);
01080 
01081          close(fds[0]);
01082          dup2(fds[1], STDOUT_FILENO);
01083          close(fds[1]);
01084          ast_close_fds_above_n(STDOUT_FILENO);
01085 
01086          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01087 
01088          execv(arg.v[0], arg.v); 
01089          printf("FAILURE: %s", strerror(errno));
01090          _exit(0);
01091       }
01092    }
01093    return buf;
01094 }
01095 
01096 /*!
01097  * \brief Check that password meets minimum required length
01098  * \param vmu The voicemail user to change the password for.
01099  * \param password The password string to check
01100  *
01101  * \return zero on ok, 1 on not ok.
01102  */
01103 static int check_password(struct ast_vm_user *vmu, char *password)
01104 {
01105    /* check minimum length */
01106    if (strlen(password) < minpassword)
01107       return 1;
01108    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01109       char cmd[255], buf[255];
01110 
01111       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01112 
01113       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01114       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01115          ast_debug(5, "Result: %s\n", buf);
01116          if (!strncasecmp(buf, "VALID", 5)) {
01117             ast_debug(3, "Passed password check: '%s'\n", buf);
01118             return 0;
01119          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01120             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01121             return 0;
01122          } else {
01123             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01124             return 1;
01125          }
01126       }
01127    }
01128    return 0;
01129 }
01130 
01131 /*! 
01132  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01133  * \param vmu The voicemail user to change the password for.
01134  * \param password The new value to be set to the password for this user.
01135  * 
01136  * This only works if there is a realtime engine configured.
01137  * This is called from the (top level) vm_change_password.
01138  *
01139  * \return zero on success, -1 on error.
01140  */
01141 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01142 {
01143    int res = -1;
01144    if (!strcmp(vmu->password, password)) {
01145       /* No change (but an update would return 0 rows updated, so we opt out here) */
01146       return 0;
01147    }
01148 
01149    if (strlen(password) > 10) {
01150       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01151    }
01152    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01153       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01154       res = 0;
01155    }
01156    return res;
01157 }
01158 
01159 /*!
01160  * \brief Destructively Parse options and apply.
01161  */
01162 static void apply_options(struct ast_vm_user *vmu, const char *options)
01163 {  
01164    char *stringp;
01165    char *s;
01166    char *var, *value;
01167    stringp = ast_strdupa(options);
01168    while ((s = strsep(&stringp, "|"))) {
01169       value = s;
01170       if ((var = strsep(&value, "=")) && value) {
01171          apply_option(vmu, var, value);
01172       }
01173    }  
01174 }
01175 
01176 /*!
01177  * \brief Loads the options specific to a voicemail user.
01178  * 
01179  * This is called when a vm_user structure is being set up, such as from load_options.
01180  */
01181 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01182 {
01183    for (; var; var = var->next) {
01184       if (!strcasecmp(var->name, "vmsecret")) {
01185          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01186       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01187          if (ast_strlen_zero(retval->password))
01188             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01189       } else if (!strcasecmp(var->name, "uniqueid")) {
01190          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01191       } else if (!strcasecmp(var->name, "pager")) {
01192          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01193       } else if (!strcasecmp(var->name, "email")) {
01194          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01195       } else if (!strcasecmp(var->name, "fullname")) {
01196          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01197       } else if (!strcasecmp(var->name, "context")) {
01198          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01199       } else if (!strcasecmp(var->name, "emailsubject")) {
01200          retval->emailsubject = ast_strdup(var->value);
01201       } else if (!strcasecmp(var->name, "emailbody")) {
01202          retval->emailbody = ast_strdup(var->value);
01203 #ifdef IMAP_STORAGE
01204       } else if (!strcasecmp(var->name, "imapuser")) {
01205          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01206          retval->imapversion = imapversion;
01207       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01208          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01209          retval->imapversion = imapversion;
01210       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01211          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01212          retval->imapversion = imapversion;
01213 #endif
01214       } else
01215          apply_option(retval, var->name, var->value);
01216    }
01217 }
01218 
01219 /*!
01220  * \brief Determines if a DTMF key entered is valid.
01221  * \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.
01222  *
01223  * Tests the character entered against the set of valid DTMF characters. 
01224  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01225  */
01226 static int is_valid_dtmf(const char *key)
01227 {
01228    int i;
01229    char *local_key = ast_strdupa(key);
01230 
01231    for (i = 0; i < strlen(key); ++i) {
01232       if (!strchr(VALID_DTMF, *local_key)) {
01233          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01234          return 0;
01235       }
01236       local_key++;
01237    }
01238    return 1;
01239 }
01240 
01241 /*!
01242  * \brief Finds a voicemail user from the realtime engine.
01243  * \param ivm
01244  * \param context
01245  * \param mailbox
01246  *
01247  * 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.
01248  *
01249  * \return The ast_vm_user structure for the user that was found.
01250  */
01251 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01252 {
01253    struct ast_variable *var;
01254    struct ast_vm_user *retval;
01255 
01256    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01257       if (!ivm)
01258          ast_set_flag(retval, VM_ALLOCED);   
01259       else
01260          memset(retval, 0, sizeof(*retval));
01261       if (mailbox) 
01262          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01263       populate_defaults(retval);
01264       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01265          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01266       else
01267          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01268       if (var) {
01269          apply_options_full(retval, var);
01270          ast_variables_destroy(var);
01271       } else { 
01272          if (!ivm) 
01273             ast_free(retval);
01274          retval = NULL;
01275       }  
01276    } 
01277    return retval;
01278 }
01279 
01280 /*!
01281  * \brief Finds a voicemail user from the users file or the realtime engine.
01282  * \param ivm
01283  * \param context
01284  * \param mailbox
01285  * 
01286  * \return The ast_vm_user structure for the user that was found.
01287  */
01288 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01289 {
01290    /* This function could be made to generate one from a database, too */
01291    struct ast_vm_user *vmu=NULL, *cur;
01292    AST_LIST_LOCK(&users);
01293 
01294    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01295       context = "default";
01296 
01297    AST_LIST_TRAVERSE(&users, cur, list) {
01298 #ifdef IMAP_STORAGE
01299       if (cur->imapversion != imapversion) {
01300          continue;
01301       }
01302 #endif
01303       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01304          break;
01305       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01306          break;
01307    }
01308    if (cur) {
01309       /* Make a copy, so that on a reload, we have no race */
01310       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01311          memcpy(vmu, cur, sizeof(*vmu));
01312          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01313          AST_LIST_NEXT(vmu, list) = NULL;
01314       }
01315    } else
01316       vmu = find_user_realtime(ivm, context, mailbox);
01317    AST_LIST_UNLOCK(&users);
01318    return vmu;
01319 }
01320 
01321 /*!
01322  * \brief Resets a user password to a specified password.
01323  * \param context
01324  * \param mailbox
01325  * \param newpass
01326  *
01327  * This does the actual change password work, called by the vm_change_password() function.
01328  *
01329  * \return zero on success, -1 on error.
01330  */
01331 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01332 {
01333    /* This function could be made to generate one from a database, too */
01334    struct ast_vm_user *cur;
01335    int res = -1;
01336    AST_LIST_LOCK(&users);
01337    AST_LIST_TRAVERSE(&users, cur, list) {
01338       if ((!context || !strcasecmp(context, cur->context)) &&
01339          (!strcasecmp(mailbox, cur->mailbox)))
01340             break;
01341    }
01342    if (cur) {
01343       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01344       res = 0;
01345    }
01346    AST_LIST_UNLOCK(&users);
01347    return res;
01348 }
01349 
01350 /*! 
01351  * \brief The handler for the change password option.
01352  * \param vmu The voicemail user to work with.
01353  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01354  * 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.
01355  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01356  */
01357 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01358 {
01359    struct ast_config   *cfg=NULL;
01360    struct ast_variable *var=NULL;
01361    struct ast_category *cat=NULL;
01362    char *category=NULL, *value=NULL, *new=NULL;
01363    const char *tmp=NULL;
01364    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01365    if (!change_password_realtime(vmu, newpassword))
01366       return;
01367 
01368    /* check voicemail.conf */
01369    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01370       while ((category = ast_category_browse(cfg, category))) {
01371          if (!strcasecmp(category, vmu->context)) {
01372             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01373                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01374                break;
01375             }
01376             value = strstr(tmp,",");
01377             if (!value) {
01378                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01379                break;
01380             }
01381             new = alloca((strlen(value)+strlen(newpassword)+1));
01382             sprintf(new,"%s%s", newpassword, value);
01383             if (!(cat = ast_category_get(cfg, category))) {
01384                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01385                break;
01386             }
01387             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01388          }
01389       }
01390       /* save the results */
01391       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01392       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01393       ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01394    }
01395    category = NULL;
01396    var = NULL;
01397    /* check users.conf and update the password stored for the mailbox*/
01398    /* if no vmsecret entry exists create one. */
01399    if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01400       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01401       while ((category = ast_category_browse(cfg, category))) {
01402          ast_debug(4, "users.conf: %s\n", category);
01403          if (!strcasecmp(category, vmu->mailbox)) {
01404             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01405                ast_debug(3, "looks like we need to make vmsecret!\n");
01406                var = ast_variable_new("vmsecret", newpassword, "");
01407             } 
01408             new = alloca(strlen(newpassword)+1);
01409             sprintf(new, "%s", newpassword);
01410             if (!(cat = ast_category_get(cfg, category))) {
01411                ast_debug(4, "failed to get category!\n");
01412                break;
01413             }
01414             if (!var)      
01415                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01416             else
01417                ast_variable_append(cat, var);
01418          }
01419       }
01420       /* save the results and clean things up */
01421       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01422       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01423       ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01424    }
01425 }
01426 
01427 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01428 {
01429    char buf[255];
01430    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01431    if (!ast_safe_system(buf)) {
01432       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01433       /* Reset the password in memory, too */
01434       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01435    }
01436 }
01437 
01438 /*! 
01439  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01440  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01441  * \param len The length of the path string that was written out.
01442  * 
01443  * The path is constructed as 
01444  *    VM_SPOOL_DIRcontext/ext/folder
01445  *
01446  * \return zero on success, -1 on error.
01447  */
01448 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01449 {
01450    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01451 }
01452 
01453 /*! 
01454  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01455  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01456  * \param len The length of the path string that was written out.
01457  * 
01458  * The path is constructed as 
01459  *    VM_SPOOL_DIRcontext/ext/folder
01460  *
01461  * \return zero on success, -1 on error.
01462  */
01463 static int make_file(char *dest, const int len, const char *dir, const int num)
01464 {
01465    return snprintf(dest, len, "%s/msg%04d", dir, num);
01466 }
01467 
01468 /* same as mkstemp, but return a FILE * */
01469 static FILE *vm_mkftemp(char *template)
01470 {
01471    FILE *p = NULL;
01472    int pfd = mkstemp(template);
01473    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01474    if (pfd > -1) {
01475       p = fdopen(pfd, "w+");
01476       if (!p) {
01477          close(pfd);
01478          pfd = -1;
01479       }
01480    }
01481    return p;
01482 }
01483 
01484 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01485  * \param dest    String. base directory.
01486  * \param len     Length of dest.
01487  * \param context String. Ignored if is null or empty string.
01488  * \param ext     String. Ignored if is null or empty string.
01489  * \param folder  String. Ignored if is null or empty string. 
01490  * \return -1 on failure, 0 on success.
01491  */
01492 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01493 {
01494    mode_t   mode = VOICEMAIL_DIR_MODE;
01495    int res;
01496 
01497    make_dir(dest, len, context, ext, folder);
01498    if ((res = ast_mkdir(dest, mode))) {
01499       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01500       return -1;
01501    }
01502    return 0;
01503 }
01504 
01505 static const char *mbox(int id)
01506 {
01507    static const char *msgs[] = {
01508 #ifdef IMAP_STORAGE
01509       imapfolder,
01510 #else
01511       "INBOX",
01512 #endif
01513       "Old",
01514       "Work",
01515       "Family",
01516       "Friends",
01517       "Cust1",
01518       "Cust2",
01519       "Cust3",
01520       "Cust4",
01521       "Cust5",
01522       "Deleted",
01523       "Urgent"
01524    };
01525    return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
01526 }
01527 
01528 static void free_user(struct ast_vm_user *vmu)
01529 {
01530    if (ast_test_flag(vmu, VM_ALLOCED)) {
01531       if (vmu->emailbody != NULL) {
01532          ast_free(vmu->emailbody);
01533          vmu->emailbody = NULL;
01534       }
01535       if (vmu->emailsubject != NULL) {
01536          ast_free(vmu->emailsubject);
01537          vmu->emailsubject = NULL;
01538       }
01539       ast_free(vmu);
01540    }
01541 }
01542 
01543 /* All IMAP-specific functions should go in this block. This
01544  * keeps them from being spread out all over the code */
01545 #ifdef IMAP_STORAGE
01546 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01547 {
01548    char arg[10];
01549    struct vm_state *vms;
01550    unsigned long messageNum;
01551 
01552    /* If greetings aren't stored in IMAP, just delete the file */
01553    if (msgnum < 0 && !imapgreetings) {
01554       ast_filedelete(file, NULL);
01555       return;
01556    }
01557 
01558    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01559       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);
01560       return;
01561    }
01562 
01563    /* find real message number based on msgnum */
01564    /* this may be an index into vms->msgArray based on the msgnum. */
01565    messageNum = vms->msgArray[msgnum];
01566    if (messageNum == 0) {
01567       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01568       return;
01569    }
01570    if (option_debug > 2)
01571       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01572    /* delete message */
01573    snprintf (arg, sizeof(arg), "%lu",messageNum);
01574    ast_mutex_lock(&vms->lock);
01575    mail_setflag (vms->mailstream,arg,"\\DELETED");
01576    mail_expunge(vms->mailstream);
01577    ast_mutex_unlock(&vms->lock);
01578 }
01579 
01580 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01581 {
01582    struct vm_state *vms_p;
01583    char *file, *filename;
01584    char *attachment;
01585    int ret = 0, i;
01586    BODY *body;
01587 
01588    /* This function is only used for retrieval of IMAP greetings
01589     * regular messages are not retrieved this way, nor are greetings
01590     * if they are stored locally*/
01591    if (msgnum > -1 || !imapgreetings) {
01592       return 0;
01593    } else {
01594       file = strrchr(ast_strdupa(dir), '/');
01595       if (file)
01596          *file++ = '\0';
01597       else {
01598          ast_debug (1, "Failed to procure file name from directory passed.\n");
01599          return -1;
01600       }
01601    }
01602 
01603    /* check if someone is accessing this box right now... */
01604    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01605       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01606       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01607       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01608       * that's all we need to do.
01609       */
01610       if (!(vms_p = create_vm_state_from_user(vmu))) {
01611          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01612          return -1;
01613       }
01614    }
01615    
01616    /* Greetings will never have a prepended message */
01617    *vms_p->introfn = '\0';
01618 
01619    ast_mutex_lock(&vms_p->lock);
01620    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01621    if (!vms_p->mailstream) {
01622       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01623       ast_mutex_unlock(&vms_p->lock);
01624       return -1;
01625    }
01626 
01627    /*XXX Yuck, this could probably be done a lot better */
01628    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01629       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01630       /* We have the body, now we extract the file name of the first attachment. */
01631       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01632          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01633       } else {
01634          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01635          ast_mutex_unlock(&vms_p->lock);
01636          return -1;
01637       }
01638       filename = strsep(&attachment, ".");
01639       if (!strcmp(filename, file)) {
01640          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01641          vms_p->msgArray[vms_p->curmsg] = i + 1;
01642          save_body(body, vms_p, "2", attachment, 0);
01643          ast_mutex_unlock(&vms_p->lock);
01644          return 0;
01645       }
01646    }
01647    ast_mutex_unlock(&vms_p->lock);
01648 
01649    return -1;
01650 }
01651 
01652 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01653 {
01654    BODY *body;
01655    char *header_content;
01656    char *attachedfilefmt;
01657    char buf[80];
01658    struct vm_state *vms;
01659    char text_file[PATH_MAX];
01660    FILE *text_file_ptr;
01661    int res = 0;
01662    struct ast_vm_user *vmu;
01663 
01664    if (!(vmu = find_user(NULL, context, mailbox))) {
01665       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01666       return -1;
01667    }
01668    
01669    if (msgnum < 0) {
01670       if (imapgreetings) {
01671          res = imap_retrieve_greeting(dir, msgnum, vmu);
01672          goto exit;
01673       } else {
01674          res = 0;
01675          goto exit;
01676       }
01677    }
01678 
01679    /* Before anything can happen, we need a vm_state so that we can
01680     * actually access the imap server through the vms->mailstream
01681     */
01682    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01683       /* This should not happen. If it does, then I guess we'd
01684        * need to create the vm_state, extract which mailbox to
01685        * open, and then set up the msgArray so that the correct
01686        * IMAP message could be accessed. If I have seen correctly
01687        * though, the vms should be obtainable from the vmstates list
01688        * and should have its msgArray properly set up.
01689        */
01690       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01691       res = -1;
01692       goto exit;
01693    }
01694    
01695    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01696    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01697 
01698    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01699    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01700       res = 0;
01701       goto exit;
01702    }
01703 
01704    if (option_debug > 2)
01705       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01706    if (vms->msgArray[msgnum] == 0) {
01707       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01708       res = -1;
01709       goto exit;
01710    }
01711 
01712    /* This will only work for new messages... */
01713    ast_mutex_lock(&vms->lock);
01714    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01715    ast_mutex_unlock(&vms->lock);
01716    /* empty string means no valid header */
01717    if (ast_strlen_zero(header_content)) {
01718       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01719       res = -1;
01720       goto exit;
01721    }
01722 
01723    ast_mutex_lock(&vms->lock);
01724    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01725    ast_mutex_unlock(&vms->lock);
01726 
01727    /* We have the body, now we extract the file name of the first attachment. */
01728    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01729       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01730    } else {
01731       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01732       res = -1;
01733       goto exit;
01734    }
01735    
01736    /* Find the format of the attached file */
01737 
01738    strsep(&attachedfilefmt, ".");
01739    if (!attachedfilefmt) {
01740       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01741       res = -1;
01742       goto exit;
01743    }
01744    
01745    save_body(body, vms, "2", attachedfilefmt, 0);
01746    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01747       *vms->introfn = '\0';
01748    }
01749 
01750    /* Get info from headers!! */
01751    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01752 
01753    if (!(text_file_ptr = fopen(text_file, "w"))) {
01754       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01755    }
01756 
01757    fprintf(text_file_ptr, "%s\n", "[message]");
01758 
01759    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01760    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01761    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01762    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01763    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01764    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01765    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01766    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01767    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01768    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01769    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01770    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01771    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01772    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01773    fclose(text_file_ptr);
01774 
01775 exit:
01776    free_user(vmu);
01777    return res;
01778 }
01779 
01780 static int folder_int(const char *folder)
01781 {
01782    /*assume a NULL folder means INBOX*/
01783    if (!folder)
01784       return 0;
01785 #ifdef IMAP_STORAGE
01786    if (!strcasecmp(folder, imapfolder))
01787 #else
01788    if (!strcasecmp(folder, "INBOX"))
01789 #endif
01790       return 0;
01791    else if (!strcasecmp(folder, "Old"))
01792       return 1;
01793    else if (!strcasecmp(folder, "Work"))
01794       return 2;
01795    else if (!strcasecmp(folder, "Family"))
01796       return 3;
01797    else if (!strcasecmp(folder, "Friends"))
01798       return 4;
01799    else if (!strcasecmp(folder, "Cust1"))
01800       return 5;
01801    else if (!strcasecmp(folder, "Cust2"))
01802       return 6;
01803    else if (!strcasecmp(folder, "Cust3"))
01804       return 7;
01805    else if (!strcasecmp(folder, "Cust4"))
01806       return 8;
01807    else if (!strcasecmp(folder, "Cust5"))
01808       return 9;
01809    else /*assume they meant INBOX if folder is not found otherwise*/
01810       return 0;
01811 }
01812 
01813 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01814 {
01815    SEARCHPGM *pgm;
01816    SEARCHHEADER *hdr;
01817 
01818    struct ast_vm_user *vmu, vmus;
01819    struct vm_state *vms_p;
01820    int ret = 0;
01821    int fold = folder_int(folder);
01822    int urgent = 0;
01823    
01824    /* If URGENT, then look at INBOX */
01825    if (fold == 11) {
01826       fold = NEW_FOLDER;
01827       urgent = 1;
01828    }
01829 
01830    if (ast_strlen_zero(mailbox))
01831       return 0;
01832 
01833    /* We have to get the user before we can open the stream! */
01834    vmu = find_user(&vmus, context, mailbox);
01835    if (!vmu) {
01836       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01837       return -1;
01838    } else {
01839       /* No IMAP account available */
01840       if (vmu->imapuser[0] == '\0') {
01841          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01842          return -1;
01843       }
01844    }
01845    
01846    /* No IMAP account available */
01847    if (vmu->imapuser[0] == '\0') {
01848       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01849       free_user(vmu);
01850       return -1;
01851    }
01852 
01853    /* check if someone is accessing this box right now... */
01854    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01855    if (!vms_p) {
01856       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01857    }
01858    if (vms_p) {
01859       ast_debug(3, "Returning before search - user is logged in\n");
01860       if (fold == 0) { /* INBOX */
01861          return vms_p->newmessages;
01862       }
01863       if (fold == 1) { /* Old messages */
01864          return vms_p->oldmessages;
01865       }
01866       if (fold == 11) {/*Urgent messages*/
01867          return vms_p->urgentmessages;
01868       }
01869    }
01870 
01871    /* add one if not there... */
01872    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01873    if (!vms_p) {
01874       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01875    }
01876 
01877    if (!vms_p) {
01878       vms_p = create_vm_state_from_user(vmu);
01879    }
01880    ret = init_mailstream(vms_p, fold);
01881    if (!vms_p->mailstream) {
01882       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01883       return -1;
01884    }
01885    if (ret == 0) {
01886       ast_mutex_lock(&vms_p->lock);
01887       pgm = mail_newsearchpgm ();
01888       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01889       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01890       pgm->header = hdr;
01891       if (fold != 1) {
01892          pgm->unseen = 1;
01893          pgm->seen = 0;
01894       }
01895       /* In the special case where fold is 1 (old messages) we have to do things a bit
01896        * differently. Old messages are stored in the INBOX but are marked as "seen"
01897        */
01898       else {
01899          pgm->unseen = 0;
01900          pgm->seen = 1;
01901       }
01902       /* look for urgent messages */
01903       if (urgent) {
01904          pgm->flagged = 1;
01905          pgm->unflagged = 0;
01906       }
01907       pgm->undeleted = 1;
01908       pgm->deleted = 0;
01909 
01910       vms_p->vmArrayIndex = 0;
01911       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01912       if (fold == 0 && urgent == 0)
01913          vms_p->newmessages = vms_p->vmArrayIndex;
01914       if (fold == 1)
01915          vms_p->oldmessages = vms_p->vmArrayIndex;
01916       if (fold == 0 && urgent == 1)
01917          vms_p->urgentmessages = vms_p->vmArrayIndex;
01918       /*Freeing the searchpgm also frees the searchhdr*/
01919       mail_free_searchpgm(&pgm);
01920       ast_mutex_unlock(&vms_p->lock);
01921       vms_p->updated = 0;
01922       return vms_p->vmArrayIndex;
01923    } else {
01924       ast_mutex_lock(&vms_p->lock);
01925       mail_ping(vms_p->mailstream);
01926       ast_mutex_unlock(&vms_p->lock);
01927    }
01928    return 0;
01929 }
01930 
01931 /*!
01932  * \brief Gets the number of messages that exist in a mailbox folder.
01933  * \param context
01934  * \param mailbox
01935  * \param folder
01936  * 
01937  * This method is used when IMAP backend is used.
01938  * \return The number of messages in this mailbox folder (zero or more).
01939  */
01940 static int messagecount(const char *context, const char *mailbox, const char *folder)
01941 {
01942    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
01943       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
01944    } else {
01945       return __messagecount(context, mailbox, folder);
01946    }
01947 }
01948 
01949 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)
01950 {
01951    char *myserveremail = serveremail;
01952    char fn[PATH_MAX];
01953    char introfn[PATH_MAX];
01954    char mailbox[256];
01955    char *stringp;
01956    FILE *p=NULL;
01957    char tmp[80] = "/tmp/astmail-XXXXXX";
01958    long len;
01959    void *buf;
01960    int tempcopy = 0;
01961    STRING str;
01962    int ret; /* for better error checking */
01963    char *imap_flags = NIL;
01964 
01965     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
01966     if (msgnum < 0 && !imapgreetings) {
01967         return 0;
01968     }
01969 
01970    /* Set urgent flag for IMAP message */
01971    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
01972       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
01973       imap_flags="\\FLAGGED";
01974    }
01975    
01976    /* Attach only the first format */
01977    fmt = ast_strdupa(fmt);
01978    stringp = fmt;
01979    strsep(&stringp, "|");
01980 
01981    if (!ast_strlen_zero(vmu->serveremail))
01982       myserveremail = vmu->serveremail;
01983 
01984    if (msgnum > -1)
01985       make_file(fn, sizeof(fn), dir, msgnum);
01986    else
01987       ast_copy_string (fn, dir, sizeof(fn));
01988 
01989    snprintf(introfn, sizeof(introfn), "%sintro", fn);
01990    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
01991       *introfn = '\0';
01992    }
01993    
01994    if (ast_strlen_zero(vmu->email)) {
01995       /* We need the vmu->email to be set when we call make_email_file, but
01996        * if we keep it set, a duplicate e-mail will be created. So at the end
01997        * of this function, we will revert back to an empty string if tempcopy
01998        * is 1.
01999        */
02000       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02001       tempcopy = 1;
02002    }
02003 
02004    if (!strcmp(fmt, "wav49"))
02005       fmt = "WAV";
02006    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02007 
02008    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02009       command hangs. */
02010    if (!(p = vm_mkftemp(tmp))) {
02011       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02012       if (tempcopy)
02013          *(vmu->email) = '\0';
02014       return -1;
02015    }
02016 
02017    if (msgnum < 0 && imapgreetings) {
02018       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02019          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02020          return -1;
02021       }
02022       imap_delete_old_greeting(fn, vms);
02023    }
02024 
02025    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);
02026    /* read mail file to memory */
02027    len = ftell(p);
02028    rewind(p);
02029    if (!(buf = ast_malloc(len + 1))) {
02030       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02031       fclose(p);
02032       if (tempcopy)
02033          *(vmu->email) = '\0';
02034       return -1;
02035    }
02036    if (fread(buf, len, 1, p) < len) {
02037       if (ferror(p)) {
02038          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02039          return -1;
02040       }
02041    }
02042    ((char *)buf)[len] = '\0';
02043    INIT(&str, mail_string, buf, len);
02044    ret = init_mailstream(vms, NEW_FOLDER);
02045    if (ret == 0) {
02046       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02047       ast_mutex_lock(&vms->lock);
02048       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02049          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02050       ast_mutex_unlock(&vms->lock);
02051       fclose(p);
02052       unlink(tmp);
02053       ast_free(buf);
02054    } else {
02055       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
02056       fclose(p);
02057       unlink(tmp);
02058       ast_free(buf);
02059       return -1;
02060    }
02061    ast_debug(3, "%s stored\n", fn);
02062    
02063    if (tempcopy)
02064       *(vmu->email) = '\0';
02065    
02066    return 0;
02067 
02068 }
02069 
02070 /*!
02071  * \brief Gets the number of messages that exist in the inbox folder.
02072  * \param mailbox_context
02073  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02074  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02075  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02076  * 
02077  * This method is used when IMAP backend is used.
02078  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02079  *
02080  * \return zero on success, -1 on error.
02081  */
02082 
02083 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02084 {
02085    char tmp[PATH_MAX] = "";
02086    char *mailboxnc;
02087    char *context;
02088    char *mb;
02089    char *cur;
02090    if (newmsgs)
02091       *newmsgs = 0;
02092    if (oldmsgs)
02093       *oldmsgs = 0;
02094    if (urgentmsgs)
02095       *urgentmsgs = 0;
02096 
02097    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
02098    /* If no mailbox, return immediately */
02099    if (ast_strlen_zero(mailbox_context))
02100       return 0;
02101    
02102    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02103    context = strchr(tmp, '@');
02104    if (strchr(mailbox_context, ',')) {
02105       int tmpnew, tmpold, tmpurgent;
02106       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02107       mb = tmp;
02108       while ((cur = strsep(&mb, ", "))) {
02109          if (!ast_strlen_zero(cur)) {
02110             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02111                return -1;
02112             else {
02113                if (newmsgs)
02114                   *newmsgs += tmpnew; 
02115                if (oldmsgs)
02116                   *oldmsgs += tmpold;
02117                if (urgentmsgs)
02118                   *urgentmsgs += tmpurgent;
02119             }
02120          }
02121       }
02122       return 0;
02123    }
02124    if (context) {
02125       *context = '\0';
02126       mailboxnc = tmp;
02127       context++;
02128    } else {
02129       context = "default";
02130       mailboxnc = (char *)mailbox_context;
02131    }
02132    if (newmsgs) {
02133       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02134          return -1;
02135       }
02136    }
02137    if (oldmsgs) {
02138       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02139          return -1;
02140       }
02141    }
02142    if (urgentmsgs) {
02143       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02144          return -1;
02145       }
02146    }
02147    return 0;
02148 }
02149 
02150 /** 
02151  * \brief Determines if the given folder has messages.
02152  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02153  * \param folder the folder to look in
02154  *
02155  * This function is used when the mailbox is stored in an IMAP back end.
02156  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02157  * \return 1 if the folder has one or more messages. zero otherwise.
02158  */
02159 
02160 static int has_voicemail(const char *mailbox, const char *folder)
02161 {
02162    char tmp[256], *tmp2, *box, *context;
02163    ast_copy_string(tmp, mailbox, sizeof(tmp));
02164    tmp2 = tmp;
02165    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02166       while ((box = strsep(&tmp2, ",&"))) {
02167          if (!ast_strlen_zero(box)) {
02168             if (has_voicemail(box, folder)) {
02169                return 1;
02170             }
02171          }
02172       }
02173    }
02174    if ((context = strchr(tmp, '@'))) {
02175       *context++ = '\0';
02176    } else {
02177       context = "default";
02178    }
02179    return __messagecount(context, tmp, folder) ? 1 : 0;
02180 }
02181 
02182 /*!
02183  * \brief Copies a message from one mailbox to another.
02184  * \param chan
02185  * \param vmu
02186  * \param imbox
02187  * \param msgnum
02188  * \param duration
02189  * \param recip
02190  * \param fmt
02191  * \param dir
02192  *
02193  * This works with IMAP storage based mailboxes.
02194  *
02195  * \return zero on success, -1 on error.
02196  */
02197 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)
02198 {
02199    struct vm_state *sendvms = NULL, *destvms = NULL;
02200    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02201    if (msgnum >= recip->maxmsg) {
02202       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02203       return -1;
02204    }
02205    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02206       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02207       return -1;
02208    }
02209    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02210       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02211       return -1;
02212    }
02213    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02214    ast_mutex_lock(&sendvms->lock);
02215    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02216       ast_mutex_unlock(&sendvms->lock);
02217       return 0;
02218    }
02219    ast_mutex_unlock(&sendvms->lock);
02220    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02221    return -1;
02222 }
02223 
02224 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02225 {
02226    char tmp[256], *t = tmp;
02227    size_t left = sizeof(tmp);
02228    
02229    if (box == OLD_FOLDER) {
02230       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02231    } else {
02232       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02233    }
02234 
02235    if (box == NEW_FOLDER) {
02236       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02237    } else {
02238       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02239    }
02240 
02241    /* Build up server information */
02242    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02243 
02244    /* Add authentication user if present */
02245    if (!ast_strlen_zero(authuser))
02246       ast_build_string(&t, &left, "/authuser=%s", authuser);
02247 
02248    /* Add flags if present */
02249    if (!ast_strlen_zero(imapflags))
02250       ast_build_string(&t, &left, "/%s", imapflags);
02251 
02252    /* End with username */
02253 #if 1
02254    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02255 #else
02256    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02257 #endif
02258    if (box == NEW_FOLDER || box == OLD_FOLDER)
02259       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02260    else if (box == GREETINGS_FOLDER)
02261       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02262    else {   /* Other folders such as Friends, Family, etc... */
02263       if (!ast_strlen_zero(imapparentfolder)) {
02264          /* imapparentfolder would typically be set to INBOX */
02265          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02266       } else {
02267          snprintf(spec, len, "%s%s", tmp, mbox(box));
02268       }
02269    }
02270 }
02271 
02272 static int init_mailstream(struct vm_state *vms, int box)
02273 {
02274    MAILSTREAM *stream = NIL;
02275    long debug;
02276    char tmp[256];
02277    
02278    if (!vms) {
02279       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02280       return -1;
02281    }
02282    if (option_debug > 2)
02283       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02284    if (vms->mailstream == NIL || !vms->mailstream) {
02285       if (option_debug)
02286          ast_log (LOG_DEBUG,"mailstream not set.\n");
02287    } else {
02288       stream = vms->mailstream;
02289    }
02290    /* debug = T;  user wants protocol telemetry? */
02291    debug = NIL;  /* NO protocol telemetry? */
02292 
02293    if (delimiter == '\0') {      /* did not probe the server yet */
02294       char *cp;
02295 #ifdef USE_SYSTEM_IMAP
02296 #include <imap/linkage.c>
02297 #elif defined(USE_SYSTEM_CCLIENT)
02298 #include <c-client/linkage.c>
02299 #else
02300 #include "linkage.c"
02301 #endif
02302       /* Connect to INBOX first to get folders delimiter */
02303       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02304       ast_mutex_lock(&vms->lock);
02305       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02306       ast_mutex_unlock(&vms->lock);
02307       if (stream == NIL) {
02308          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02309          return -1;
02310       }
02311       get_mailbox_delimiter(stream);
02312       /* update delimiter in imapfolder */
02313       for (cp = imapfolder; *cp; cp++)
02314          if (*cp == '/')
02315             *cp = delimiter;
02316    }
02317    /* Now connect to the target folder */
02318    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02319    if (option_debug > 2)
02320       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02321    ast_mutex_lock(&vms->lock);
02322    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02323    ast_mutex_unlock(&vms->lock);
02324    if (vms->mailstream == NIL) {
02325       return -1;
02326    } else {
02327       return 0;
02328    }
02329 }
02330 
02331 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02332 {
02333    SEARCHPGM *pgm;
02334    SEARCHHEADER *hdr;
02335    int ret, urgent = 0;
02336 
02337    /* If Urgent, then look at INBOX */
02338    if (box == 11) {
02339       box = NEW_FOLDER;
02340       urgent = 1;
02341    }
02342 
02343    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02344    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02345    vms->imapversion = vmu->imapversion;
02346 
02347    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02348       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02349       return -1;
02350    }
02351    
02352    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02353    
02354    /* Check Quota */
02355    if  (box == 0)  {
02356       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02357       check_quota(vms,(char *)mbox(box));
02358    }
02359 
02360    ast_mutex_lock(&vms->lock);
02361    pgm = mail_newsearchpgm();
02362 
02363    /* Check IMAP folder for Asterisk messages only... */
02364    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02365    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02366    pgm->header = hdr;
02367    pgm->deleted = 0;
02368    pgm->undeleted = 1;
02369 
02370    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02371    if (box == NEW_FOLDER && urgent == 1) {
02372       pgm->unseen = 1;
02373       pgm->seen = 0;
02374       pgm->flagged = 1;
02375       pgm->unflagged = 0;
02376    } else if (box == NEW_FOLDER && urgent == 0) {
02377       pgm->unseen = 1;
02378       pgm->seen = 0;
02379       pgm->flagged = 0;
02380       pgm->unflagged = 1;
02381    } else if (box == OLD_FOLDER) {
02382       pgm->seen = 1;
02383       pgm->unseen = 0;
02384    }
02385 
02386    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02387 
02388    vms->vmArrayIndex = 0;
02389    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02390    vms->lastmsg = vms->vmArrayIndex - 1;
02391    mail_free_searchpgm(&pgm);
02392 
02393    ast_mutex_unlock(&vms->lock);
02394    return 0;
02395 }
02396 
02397 static void write_file(char *filename, char *buffer, unsigned long len)
02398 {
02399    FILE *output;
02400 
02401    output = fopen (filename, "w");
02402    if (fwrite(buffer, len, 1, output) != 1) {
02403       if (ferror(output)) {
02404          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02405       }
02406    }
02407    fclose (output);
02408 }
02409 
02410 static void update_messages_by_imapuser(const char *user, unsigned long number)
02411 {
02412    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02413 
02414    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02415       return;
02416    }
02417 
02418    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02419    vms->msgArray[vms->vmArrayIndex++] = number;
02420 }
02421 
02422 void mm_searched(MAILSTREAM *stream, unsigned long number)
02423 {
02424    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02425 
02426    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02427       return;
02428 
02429    update_messages_by_imapuser(user, number);
02430 }
02431 
02432 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02433 {
02434    struct ast_variable *var;
02435    struct ast_vm_user *vmu;
02436 
02437    vmu = ast_calloc(1, sizeof *vmu);
02438    if (!vmu)
02439       return NULL;
02440    ast_set_flag(vmu, VM_ALLOCED);
02441    populate_defaults(vmu);
02442 
02443    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02444    if (var) {
02445       apply_options_full(vmu, var);
02446       ast_variables_destroy(var);
02447       return vmu;
02448    } else {
02449       ast_free(vmu);
02450       return NULL;
02451    }
02452 }
02453 
02454 /* Interfaces to C-client */
02455 
02456 void mm_exists(MAILSTREAM * stream, unsigned long number)
02457 {
02458    /* mail_ping will callback here if new mail! */
02459    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02460    if (number == 0) return;
02461    set_update(stream);
02462 }
02463 
02464 
02465 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02466 {
02467    /* mail_ping will callback here if expunged mail! */
02468    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02469    if (number == 0) return;
02470    set_update(stream);
02471 }
02472 
02473 
02474 void mm_flags(MAILSTREAM * stream, unsigned long number)
02475 {
02476    /* mail_ping will callback here if read mail! */
02477    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02478    if (number == 0) return;
02479    set_update(stream);
02480 }
02481 
02482 
02483 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02484 {
02485    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02486    mm_log (string, errflg);
02487 }
02488 
02489 
02490 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02491 {
02492    if (delimiter == '\0') {
02493       delimiter = delim;
02494    }
02495 
02496    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02497    if (attributes & LATT_NOINFERIORS)
02498       ast_debug(5, "no inferiors\n");
02499    if (attributes & LATT_NOSELECT)
02500       ast_debug(5, "no select\n");
02501    if (attributes & LATT_MARKED)
02502       ast_debug(5, "marked\n");
02503    if (attributes & LATT_UNMARKED)
02504       ast_debug(5, "unmarked\n");
02505 }
02506 
02507 
02508 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02509 {
02510    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02511    if (attributes & LATT_NOINFERIORS)
02512       ast_debug(5, "no inferiors\n");
02513    if (attributes & LATT_NOSELECT)
02514       ast_debug(5, "no select\n");
02515    if (attributes & LATT_MARKED)
02516       ast_debug(5, "marked\n");
02517    if (attributes & LATT_UNMARKED)
02518       ast_debug(5, "unmarked\n");
02519 }
02520 
02521 
02522 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02523 {
02524    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02525    if (status->flags & SA_MESSAGES)
02526       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02527    if (status->flags & SA_RECENT)
02528       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02529    if (status->flags & SA_UNSEEN)
02530       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02531    if (status->flags & SA_UIDVALIDITY)
02532       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02533    if (status->flags & SA_UIDNEXT)
02534       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02535    ast_log(AST_LOG_NOTICE, "\n");
02536 }
02537 
02538 
02539 void mm_log(char *string, long errflg)
02540 {
02541    switch ((short) errflg) {
02542       case NIL:
02543          ast_debug(1,"IMAP Info: %s\n", string);
02544          break;
02545       case PARSE:
02546       case WARN:
02547          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02548          break;
02549       case ERROR:
02550          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02551          break;
02552    }
02553 }
02554 
02555 
02556 void mm_dlog(char *string)
02557 {
02558    ast_log(AST_LOG_NOTICE, "%s\n", string);
02559 }
02560 
02561 
02562 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02563 {
02564    struct ast_vm_user *vmu;
02565 
02566    ast_debug(4, "Entering callback mm_login\n");
02567 
02568    ast_copy_string(user, mb->user, MAILTMPLEN);
02569 
02570    /* We should only do this when necessary */
02571    if (!ast_strlen_zero(authpassword)) {
02572       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02573    } else {
02574       AST_LIST_TRAVERSE(&users, vmu, list) {
02575          if (!strcasecmp(mb->user, vmu->imapuser)) {
02576             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02577             break;
02578          }
02579       }
02580       if (!vmu) {
02581          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02582             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02583             free_user(vmu);
02584          }
02585       }
02586    }
02587 }
02588 
02589 
02590 void mm_critical(MAILSTREAM * stream)
02591 {
02592 }
02593 
02594 
02595 void mm_nocritical(MAILSTREAM * stream)
02596 {
02597 }
02598 
02599 
02600 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02601 {
02602    kill (getpid (), SIGSTOP);
02603    return NIL;
02604 }
02605 
02606 
02607 void mm_fatal(char *string)
02608 {
02609    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02610 }
02611 
02612 /* C-client callback to handle quota */
02613 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02614 {
02615    struct vm_state *vms;
02616    char *mailbox = stream->mailbox, *user;
02617    char buf[1024] = "";
02618    unsigned long usage = 0, limit = 0;
02619    
02620    while (pquota) {
02621       usage = pquota->usage;
02622       limit = pquota->limit;
02623       pquota = pquota->next;
02624    }
02625    
02626    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)))) {
02627       ast_log(AST_LOG_ERROR, "No state found.\n");
02628       return;
02629    }
02630 
02631    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02632 
02633    vms->quota_usage = usage;
02634    vms->quota_limit = limit;
02635 }
02636 
02637 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02638 {
02639    char *start, *eol_pnt;
02640    int taglen;
02641 
02642    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02643       return NULL;
02644 
02645    taglen = strlen(tag) + 1;
02646    if (taglen < 1)
02647       return NULL;
02648 
02649    if (!(start = strstr(header, tag)))
02650       return NULL;
02651 
02652    /* Since we can be called multiple times we should clear our buffer */
02653    memset(buf, 0, len);
02654 
02655    ast_copy_string(buf, start+taglen, len);
02656    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02657       *eol_pnt = '\0';
02658    return buf;
02659 }
02660 
02661 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02662 {
02663    char *start, *quote, *eol_pnt;
02664 
02665    if (ast_strlen_zero(mailbox))
02666       return NULL;
02667 
02668    if (!(start = strstr(mailbox, "/user=")))
02669       return NULL;
02670 
02671    ast_copy_string(buf, start+6, len);
02672 
02673    if (!(quote = strchr(buf, '\"'))) {
02674       if (!(eol_pnt = strchr(buf, '/')))
02675          eol_pnt = strchr(buf,'}');
02676       *eol_pnt = '\0';
02677       return buf;
02678    } else {
02679       eol_pnt = strchr(buf+1,'\"');
02680       *eol_pnt = '\0';
02681       return buf+1;
02682    }
02683 }
02684 
02685 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02686 {
02687    struct vm_state *vms_p;
02688 
02689    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02690    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02691       return vms_p;
02692    }
02693    if (option_debug > 4)
02694       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02695    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02696       return NULL;
02697    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02698    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02699    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02700    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02701    vms_p->imapversion = vmu->imapversion;
02702    if (option_debug > 4)
02703       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02704    vms_p->updated = 1;
02705    /* set mailbox to INBOX! */
02706    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02707    init_vm_state(vms_p);
02708    vmstate_insert(vms_p);
02709    return vms_p;
02710 }
02711 
02712 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02713 {
02714    struct vmstate *vlist = NULL;
02715 
02716    if (interactive) {
02717       struct vm_state *vms;
02718       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02719       vms = pthread_getspecific(ts_vmstate.key);
02720       return vms;
02721    }
02722 
02723    AST_LIST_LOCK(&vmstates);
02724    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02725       if (!vlist->vms) {
02726          ast_debug(3, "error: vms is NULL for %s\n", user);
02727          continue;
02728       }
02729       if (vlist->vms->imapversion != imapversion) {
02730          continue;
02731       }
02732       if (!vlist->vms->imapuser) {
02733          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02734          continue;
02735       }
02736 
02737       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02738          AST_LIST_UNLOCK(&vmstates);
02739          return vlist->vms;
02740       }
02741    }
02742    AST_LIST_UNLOCK(&vmstates);
02743 
02744    ast_debug(3, "%s not found in vmstates\n", user);
02745 
02746    return NULL;
02747 }
02748 
02749 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02750 {
02751 
02752    struct vmstate *vlist = NULL;
02753    const char *local_context = S_OR(context, "default");
02754 
02755    if (interactive) {
02756       struct vm_state *vms;
02757       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02758       vms = pthread_getspecific(ts_vmstate.key);
02759       return vms;
02760    }
02761 
02762    AST_LIST_LOCK(&vmstates);
02763    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02764       if (!vlist->vms) {
02765          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02766          continue;
02767       }
02768       if (vlist->vms->imapversion != imapversion) {
02769          continue;
02770       }
02771       if (!vlist->vms->username || !vlist->vms->context) {
02772          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02773          continue;
02774       }
02775 
02776       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);
02777       
02778       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02779          ast_debug(3, "Found it!\n");
02780          AST_LIST_UNLOCK(&vmstates);
02781          return vlist->vms;
02782       }
02783    }
02784    AST_LIST_UNLOCK(&vmstates);
02785 
02786    ast_debug(3, "%s not found in vmstates\n", mailbox);
02787 
02788    return NULL;
02789 }
02790 
02791 static void vmstate_insert(struct vm_state *vms) 
02792 {
02793    struct vmstate *v;
02794    struct vm_state *altvms;
02795 
02796    /* If interactive, it probably already exists, and we should
02797       use the one we already have since it is more up to date.
02798       We can compare the username to find the duplicate */
02799    if (vms->interactive == 1) {
02800       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02801       if (altvms) {  
02802          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02803          vms->newmessages = altvms->newmessages;
02804          vms->oldmessages = altvms->oldmessages;
02805          vms->vmArrayIndex = altvms->vmArrayIndex;
02806          vms->lastmsg = altvms->lastmsg;
02807          vms->curmsg = altvms->curmsg;
02808          /* get a pointer to the persistent store */
02809          vms->persist_vms = altvms;
02810          /* Reuse the mailstream? */
02811 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02812          vms->mailstream = altvms->mailstream;
02813 #else
02814          vms->mailstream = NIL;
02815 #endif
02816       }
02817       return;
02818    }
02819 
02820    if (!(v = ast_calloc(1, sizeof(*v))))
02821       return;
02822    
02823    v->vms = vms;
02824 
02825    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02826 
02827    AST_LIST_LOCK(&vmstates);
02828    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02829    AST_LIST_UNLOCK(&vmstates);
02830 }
02831 
02832 static void vmstate_delete(struct vm_state *vms) 
02833 {
02834    struct vmstate *vc = NULL;
02835    struct vm_state *altvms = NULL;
02836 
02837    /* If interactive, we should copy pertinent info
02838       back to the persistent state (to make update immediate) */
02839    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02840       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02841       altvms->newmessages = vms->newmessages;
02842       altvms->oldmessages = vms->oldmessages;
02843       altvms->updated = 1;
02844       vms->mailstream = mail_close(vms->mailstream);
02845 
02846       /* Interactive states are not stored within the persistent list */
02847       return;
02848    }
02849    
02850    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02851    
02852    AST_LIST_LOCK(&vmstates);
02853    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02854       if (vc->vms == vms) {
02855          AST_LIST_REMOVE_CURRENT(list);
02856          break;
02857       }
02858    }
02859    AST_LIST_TRAVERSE_SAFE_END
02860    AST_LIST_UNLOCK(&vmstates);
02861    
02862    if (vc) {
02863       ast_mutex_destroy(&vc->vms->lock);
02864       ast_free(vc);
02865    }
02866    else
02867       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02868 }
02869 
02870 static void set_update(MAILSTREAM * stream) 
02871 {
02872    struct vm_state *vms;
02873    char *mailbox = stream->mailbox, *user;
02874    char buf[1024] = "";
02875 
02876    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02877       if (user && option_debug > 2)
02878          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02879       return;
02880    }
02881 
02882    ast_debug(3, "User %s mailbox set for update.\n", user);
02883 
02884    vms->updated = 1; /* Set updated flag since mailbox changed */
02885 }
02886 
02887 static void init_vm_state(struct vm_state *vms) 
02888 {
02889    int x;
02890    vms->vmArrayIndex = 0;
02891    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02892       vms->msgArray[x] = 0;
02893    }
02894    ast_mutex_init(&vms->lock);
02895 }
02896 
02897 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02898 {
02899    char *body_content;
02900    char *body_decoded;
02901    char *fn = is_intro ? vms->introfn : vms->fn;
02902    unsigned long len;
02903    unsigned long newlen;
02904    char filename[256];
02905    
02906    if (!body || body == NIL)
02907       return -1;
02908 
02909    ast_mutex_lock(&vms->lock);
02910    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02911    ast_mutex_unlock(&vms->lock);
02912    if (body_content != NIL) {
02913       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02914       /* ast_debug(1,body_content); */
02915       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02916       /* If the body of the file is empty, return an error */
02917       if (!newlen) {
02918          return -1;
02919       }
02920       write_file(filename, (char *) body_decoded, newlen);
02921    } else {
02922       ast_debug(5, "Body of message is NULL.\n");
02923       return -1;
02924    }
02925    return 0;
02926 }
02927 
02928 /*! 
02929  * \brief Get delimiter via mm_list callback 
02930  * \param stream
02931  *
02932  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02933  */
02934 /* MUTEX should already be held */
02935 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02936    char tmp[50];
02937    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02938    mail_list(stream, tmp, "*");
02939 }
02940 
02941 /*! 
02942  * \brief Check Quota for user 
02943  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02944  * \param mailbox the mailbox to check the quota for.
02945  *
02946  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02947  */
02948 static void check_quota(struct vm_state *vms, char *mailbox) {
02949    ast_mutex_lock(&vms->lock);
02950    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02951    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02952    if (vms && vms->mailstream != NULL) {
02953       imap_getquotaroot(vms->mailstream, mailbox);
02954    } else {
02955       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02956    }
02957    ast_mutex_unlock(&vms->lock);
02958 }
02959 
02960 #endif /* IMAP_STORAGE */
02961 
02962 /*! \brief Lock file path
02963     only return failure if ast_lock_path returns 'timeout',
02964    not if the path does not exist or any other reason
02965 */
02966 static int vm_lock_path(const char *path)
02967 {
02968    switch (ast_lock_path(path)) {
02969    case AST_LOCK_TIMEOUT:
02970       return -1;
02971    default:
02972       return 0;
02973    }
02974 }
02975 
02976 
02977 #ifdef ODBC_STORAGE
02978 struct generic_prepare_struct {
02979    char *sql;
02980    int argc;
02981    char **argv;
02982 };
02983 
02984 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02985 {
02986    struct generic_prepare_struct *gps = data;
02987    int res, i;
02988    SQLHSTMT stmt;
02989 
02990    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02991    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02992       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
02993       return NULL;
02994    }
02995    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02996    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02997       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02998       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02999       return NULL;
03000    }
03001    for (i = 0; i < gps->argc; i++)
03002       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03003 
03004    return stmt;
03005 }
03006 
03007 /*!
03008  * \brief Retrieves a file from an ODBC data store.
03009  * \param dir the path to the file to be retreived.
03010  * \param msgnum the message number, such as within a mailbox folder.
03011  * 
03012  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03013  * 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.
03014  *
03015  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03016  * The output is the message information file with the name msgnum and the extension .txt
03017  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03018  * 
03019  * \return 0 on success, -1 on error.
03020  */
03021 static int retrieve_file(char *dir, int msgnum)
03022 {
03023    int x = 0;
03024    int res;
03025    int fd=-1;
03026    size_t fdlen = 0;
03027    void *fdm = MAP_FAILED;
03028    SQLSMALLINT colcount=0;
03029    SQLHSTMT stmt;
03030    char sql[PATH_MAX];
03031    char fmt[80]="";
03032    char *c;
03033    char coltitle[256];
03034    SQLSMALLINT collen;
03035    SQLSMALLINT datatype;
03036    SQLSMALLINT decimaldigits;
03037    SQLSMALLINT nullable;
03038    SQLULEN colsize;
03039    SQLLEN colsize2;
03040    FILE *f=NULL;
03041    char rowdata[80];
03042    char fn[PATH_MAX];
03043    char full_fn[PATH_MAX];
03044    char msgnums[80];
03045    char *argv[] = { dir, msgnums };
03046    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03047 
03048    struct odbc_obj *obj;
03049    obj = ast_odbc_request_obj(odbc_database, 0);
03050    if (obj) {
03051       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03052       c = strchr(fmt, '|');
03053       if (c)
03054          *c = '\0';
03055       if (!strcasecmp(fmt, "wav49"))
03056          strcpy(fmt, "WAV");
03057       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03058       if (msgnum > -1)
03059          make_file(fn, sizeof(fn), dir, msgnum);
03060       else
03061          ast_copy_string(fn, dir, sizeof(fn));
03062 
03063       /* Create the information file */
03064       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03065       
03066       if (!(f = fopen(full_fn, "w+"))) {
03067          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03068          goto yuck;
03069       }
03070       
03071       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03072       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03073       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03074       if (!stmt) {
03075          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03076          ast_odbc_release_obj(obj);
03077          goto yuck;
03078       }
03079       res = SQLFetch(stmt);
03080       if (res == SQL_NO_DATA) {
03081          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03082          ast_odbc_release_obj(obj);
03083          goto yuck;
03084       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03085          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03086          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03087          ast_odbc_release_obj(obj);
03088          goto yuck;
03089       }
03090       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03091       if (fd < 0) {
03092          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03093          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03094          ast_odbc_release_obj(obj);
03095          goto yuck;
03096       }
03097       res = SQLNumResultCols(stmt, &colcount);
03098       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03099          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03100          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03101          ast_odbc_release_obj(obj);
03102          goto yuck;
03103       }
03104       if (f) 
03105          fprintf(f, "[message]\n");
03106       for (x=0;x<colcount;x++) {
03107          rowdata[0] = '\0';
03108          collen = sizeof(coltitle);
03109          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
03110                   &datatype, &colsize, &decimaldigits, &nullable);
03111          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03112             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03113             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03114             ast_odbc_release_obj(obj);
03115             goto yuck;
03116          }
03117          if (!strcasecmp(coltitle, "recording")) {
03118             off_t offset;
03119             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03120             fdlen = colsize2;
03121             if (fd > -1) {
03122                char tmp[1]="";
03123                lseek(fd, fdlen - 1, SEEK_SET);
03124                if (write(fd, tmp, 1) != 1) {
03125                   close(fd);
03126                   fd = -1;
03127                   continue;
03128                }
03129                /* Read out in small chunks */
03130                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03131                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03132                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03133                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03134                      ast_odbc_release_obj(obj);
03135                      goto yuck;
03136                   } else {
03137                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03138                      munmap(fdm, CHUNKSIZE);
03139                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03140                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03141                         unlink(full_fn);
03142                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03143                         ast_odbc_release_obj(obj);
03144                         goto yuck;
03145                      }
03146                   }
03147                }
03148                if (truncate(full_fn, fdlen) < 0) {
03149                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03150                }
03151             }
03152          } else {
03153             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03154             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03155                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03156                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03157                ast_odbc_release_obj(obj);
03158                goto yuck;
03159             }
03160             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03161                fprintf(f, "%s=%s\n", coltitle, rowdata);
03162          }
03163       }
03164       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03165       ast_odbc_release_obj(obj);
03166    } else
03167       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03168 yuck: 
03169    if (f)
03170       fclose(f);
03171    if (fd > -1)
03172       close(fd);
03173    return x - 1;
03174 }
03175 
03176 /*!
03177  * \brief Determines the highest message number in use for a given user and mailbox folder.
03178  * \param vmu 
03179  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03180  *
03181  * This method is used when mailboxes are stored in an ODBC back end.
03182  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03183  *
03184  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03185  */
03186 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03187 {
03188    int x = 0;
03189    int res;
03190    SQLHSTMT stmt;
03191    char sql[PATH_MAX];
03192    char rowdata[20];
03193    char *argv[] = { dir };
03194    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03195 
03196    struct odbc_obj *obj;
03197    obj = ast_odbc_request_obj(odbc_database, 0);
03198    if (obj) {
03199       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03200       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03201       if (!stmt) {
03202          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03203          ast_odbc_release_obj(obj);
03204          goto yuck;
03205       }
03206       res = SQLFetch(stmt);
03207       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03208          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03209          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03210          ast_odbc_release_obj(obj);
03211          goto yuck;
03212       }
03213       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03214       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03215          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03216          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03217          ast_odbc_release_obj(obj);
03218          goto yuck;
03219       }
03220       if (sscanf(rowdata, "%30d", &x) != 1)
03221          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03222       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03223       ast_odbc_release_obj(obj);
03224    } else
03225       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03226 yuck: 
03227    return x - 1;
03228 }
03229 
03230 /*!
03231  * \brief Determines if the specified message exists.
03232  * \param dir the folder the mailbox folder to look for messages. 
03233  * \param msgnum the message index to query for.
03234  *
03235  * This method is used when mailboxes are stored in an ODBC back end.
03236  *
03237  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03238  */
03239 static int message_exists(char *dir, int msgnum)
03240 {
03241    int x = 0;
03242    int res;
03243    SQLHSTMT stmt;
03244    char sql[PATH_MAX];
03245    char rowdata[20];
03246    char msgnums[20];
03247    char *argv[] = { dir, msgnums };
03248    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03249 
03250    struct odbc_obj *obj;
03251    obj = ast_odbc_request_obj(odbc_database, 0);
03252    if (obj) {
03253       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03254       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03255       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03256       if (!stmt) {
03257          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03258          ast_odbc_release_obj(obj);
03259          goto yuck;
03260       }
03261       res = SQLFetch(stmt);
03262       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03263          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03264          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03265          ast_odbc_release_obj(obj);
03266          goto yuck;
03267       }
03268       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03269       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03270          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03271          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03272          ast_odbc_release_obj(obj);
03273          goto yuck;
03274       }
03275       if (sscanf(rowdata, "%30d", &x) != 1)
03276          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03277       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03278       ast_odbc_release_obj(obj);
03279    } else
03280       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03281 yuck: 
03282    return x;
03283 }
03284 
03285 /*!
03286  * \brief returns the one-based count for messages.
03287  * \param vmu
03288  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03289  *
03290  * This method is used when mailboxes are stored in an ODBC back end.
03291  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03292  * one-based messages.
03293  * This method just calls last_message_index and returns +1 of its value.
03294  *
03295  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03296  */
03297 static int count_messages(struct ast_vm_user *vmu, char *dir)
03298 {
03299    return last_message_index(vmu, dir) + 1;
03300 }
03301 
03302 /*!
03303  * \brief Deletes a message from the mailbox folder.
03304  * \param sdir The mailbox folder to work in.
03305  * \param smsg The message index to be deleted.
03306  *
03307  * This method is used when mailboxes are stored in an ODBC back end.
03308  * The specified message is directly deleted from the database 'voicemessages' table.
03309  * 
03310  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03311  */
03312 static void delete_file(const char *sdir, int smsg)
03313 {
03314    SQLHSTMT stmt;
03315    char sql[PATH_MAX];
03316    char msgnums[20];
03317    char *argv[] = { NULL, msgnums };
03318    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03319    struct odbc_obj *obj;
03320 
03321    argv[0] = ast_strdupa(sdir);
03322 
03323    obj = ast_odbc_request_obj(odbc_database, 0);
03324    if (obj) {
03325       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03326       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03327       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03328       if (!stmt)
03329          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03330       else
03331          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03332       ast_odbc_release_obj(obj);
03333    } else
03334       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03335    return;  
03336 }
03337 
03338 /*!
03339  * \brief Copies a voicemail from one mailbox to another.
03340  * \param sdir the folder for which to look for the message to be copied.
03341  * \param smsg the index of the message to be copied.
03342  * \param ddir the destination folder to copy the message into.
03343  * \param dmsg the index to be used for the copied message.
03344  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03345  * \param dmailboxcontext The context for the destination user.
03346  *
03347  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03348  */
03349 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03350 {
03351    SQLHSTMT stmt;
03352    char sql[512];
03353    char msgnums[20];
03354    char msgnumd[20];
03355    struct odbc_obj *obj;
03356    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03357    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03358 
03359    delete_file(ddir, dmsg);
03360    obj = ast_odbc_request_obj(odbc_database, 0);
03361    if (obj) {
03362       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03363       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03364       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);
03365       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03366       if (!stmt)
03367          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03368       else
03369          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03370       ast_odbc_release_obj(obj);
03371    } else
03372       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03373    return;  
03374 }
03375 
03376 struct insert_data {
03377    char *sql;
03378    const char *dir;
03379    const char *msgnums;
03380    void *data;
03381    SQLLEN datalen;
03382    SQLLEN indlen;
03383    const char *context;
03384    const char *macrocontext;
03385    const char *callerid;
03386    const char *origtime;
03387    const char *duration;
03388    const char *mailboxuser;
03389    const char *mailboxcontext;
03390    const char *category;
03391    const char *flag;
03392 };
03393 
03394 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03395 {
03396    struct insert_data *data = vdata;
03397    int res;
03398    SQLHSTMT stmt;
03399 
03400    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03401    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03402       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03403       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03404       return NULL;
03405    }
03406 
03407    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03408    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03409    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03410    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03411    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03412    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03413    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03414    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03415    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03416    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03417    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03418    if (!ast_strlen_zero(data->category)) {
03419       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03420    }
03421    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03422    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03423       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03424       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03425       return NULL;
03426    }
03427 
03428    return stmt;
03429 }
03430 
03431 /*!
03432  * \brief Stores a voicemail into the database.
03433  * \param dir the folder the mailbox folder to store the message.
03434  * \param mailboxuser the user owning the mailbox folder.
03435  * \param mailboxcontext
03436  * \param msgnum the message index for the message to be stored.
03437  *
03438  * This method is used when mailboxes are stored in an ODBC back end.
03439  * The message sound file and information file is looked up on the file system. 
03440  * A SQL query is invoked to store the message into the (MySQL) database.
03441  *
03442  * \return the zero on success -1 on error.
03443  */
03444 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03445 {
03446    int res = 0;
03447    int fd = -1;
03448    void *fdm = MAP_FAILED;
03449    size_t fdlen = -1;
03450    SQLHSTMT stmt;
03451    char sql[PATH_MAX];
03452    char msgnums[20];
03453    char fn[PATH_MAX];
03454    char full_fn[PATH_MAX];
03455    char fmt[80]="";
03456    char *c;
03457    struct ast_config *cfg=NULL;
03458    struct odbc_obj *obj;
03459    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03460       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03461    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03462 
03463    delete_file(dir, msgnum);
03464    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03465       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03466       return -1;
03467    }
03468 
03469    do {
03470       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03471       c = strchr(fmt, '|');
03472       if (c)
03473          *c = '\0';
03474       if (!strcasecmp(fmt, "wav49"))
03475          strcpy(fmt, "WAV");
03476       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03477       if (msgnum > -1)
03478          make_file(fn, sizeof(fn), dir, msgnum);
03479       else
03480          ast_copy_string(fn, dir, sizeof(fn));
03481       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03482       cfg = ast_config_load(full_fn, config_flags);
03483       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03484       fd = open(full_fn, O_RDWR);
03485       if (fd < 0) {
03486          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03487          res = -1;
03488          break;
03489       }
03490       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03491          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03492             idata.context = "";
03493          }
03494          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03495             idata.macrocontext = "";
03496          }
03497          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03498             idata.callerid = "";
03499          }
03500          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03501             idata.origtime = "";
03502          }
03503          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03504             idata.duration = "";
03505          }
03506          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03507             idata.category = "";
03508          }
03509          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03510             idata.flag = "";
03511          }
03512       }
03513       fdlen = lseek(fd, 0, SEEK_END);
03514       lseek(fd, 0, SEEK_SET);
03515       printf("Length is %zd\n", fdlen);
03516       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03517       if (fdm == MAP_FAILED) {
03518          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03519          res = -1;
03520          break;
03521       } 
03522       idata.data = fdm;
03523       idata.datalen = idata.indlen = fdlen;
03524 
03525       if (!ast_strlen_zero(idata.category)) 
03526          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03527       else
03528          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03529 
03530       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03531          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03532       } else {
03533          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03534          res = -1;
03535       }
03536    } while (0);
03537    if (obj) {
03538       ast_odbc_release_obj(obj);
03539    }
03540    if (cfg)
03541       ast_config_destroy(cfg);
03542    if (fdm != MAP_FAILED)
03543       munmap(fdm, fdlen);
03544    if (fd > -1)
03545       close(fd);
03546    return res;
03547 }
03548 
03549 /*!
03550  * \brief Renames a message in a mailbox folder.
03551  * \param sdir The folder of the message to be renamed.
03552  * \param smsg The index of the message to be renamed.
03553  * \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.
03554  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03555  * \param ddir The destination folder for the message to be renamed into
03556  * \param dmsg The destination message for the message to be renamed.
03557  *
03558  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03559  * 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.
03560  * 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.
03561  */
03562 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03563 {
03564    SQLHSTMT stmt;
03565    char sql[PATH_MAX];
03566    char msgnums[20];
03567    char msgnumd[20];
03568    struct odbc_obj *obj;
03569    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03570    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03571 
03572    delete_file(ddir, dmsg);
03573    obj = ast_odbc_request_obj(odbc_database, 0);
03574    if (obj) {
03575       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03576       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03577       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03578       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03579       if (!stmt)
03580          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03581       else
03582          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03583       ast_odbc_release_obj(obj);
03584    } else
03585       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03586    return;  
03587 }
03588 
03589 /*!
03590  * \brief Removes a voicemail message file.
03591  * \param dir the path to the message file.
03592  * \param msgnum the unique number for the message within the mailbox.
03593  *
03594  * Removes the message content file and the information file.
03595  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03596  * Typical use is to clean up after a RETRIEVE operation. 
03597  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03598  * \return zero on success, -1 on error.
03599  */
03600 static int remove_file(char *dir, int msgnum)
03601 {
03602    char fn[PATH_MAX];
03603    char full_fn[PATH_MAX];
03604    char msgnums[80];
03605    
03606    if (msgnum > -1) {
03607       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03608       make_file(fn, sizeof(fn), dir, msgnum);
03609    } else
03610       ast_copy_string(fn, dir, sizeof(fn));
03611    ast_filedelete(fn, NULL);  
03612    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03613    unlink(full_fn);
03614    return 0;
03615 }
03616 #else
03617 #ifndef IMAP_STORAGE
03618 /*!
03619  * \brief Find all .txt files - even if they are not in sequence from 0000.
03620  * \param vmu
03621  * \param dir
03622  *
03623  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03624  *
03625  * \return the count of messages, zero or more.
03626  */
03627 static int count_messages(struct ast_vm_user *vmu, char *dir)
03628 {
03629 
03630    int vmcount = 0;
03631    DIR *vmdir = NULL;
03632    struct dirent *vment = NULL;
03633 
03634    if (vm_lock_path(dir))
03635       return ERROR_LOCK_PATH;
03636 
03637    if ((vmdir = opendir(dir))) {
03638       while ((vment = readdir(vmdir))) {
03639          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03640             vmcount++;
03641          }
03642       }
03643       closedir(vmdir);
03644    }
03645    ast_unlock_path(dir);
03646    
03647    return vmcount;
03648 }
03649 
03650 /*!
03651  * \brief Renames a message in a mailbox folder.
03652  * \param sfn The path to the mailbox information and data file to be renamed.
03653  * \param dfn The path for where the message data and information files will be renamed to.
03654  *
03655  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03656  */
03657 static void rename_file(char *sfn, char *dfn)
03658 {
03659    char stxt[PATH_MAX];
03660    char dtxt[PATH_MAX];
03661    ast_filerename(sfn,dfn,NULL);
03662    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03663    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03664    if (ast_check_realtime("voicemail_data")) {
03665       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03666    }
03667    rename(stxt, dtxt);
03668 }
03669 
03670 /*! 
03671  * \brief Determines the highest message number in use for a given user and mailbox folder.
03672  * \param vmu 
03673  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03674  *
03675  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03676  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03677  *
03678  * \note Should always be called with a lock already set on dir.
03679  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03680  */
03681 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03682 {
03683    int x;
03684    unsigned char map[MAXMSGLIMIT] = "";
03685    DIR *msgdir;
03686    struct dirent *msgdirent;
03687    int msgdirint;
03688 
03689    /* Reading the entire directory into a file map scales better than
03690     * doing a stat repeatedly on a predicted sequence.  I suspect this
03691     * is partially due to stat(2) internally doing a readdir(2) itself to
03692     * find each file. */
03693    if (!(msgdir = opendir(dir))) {
03694       return -1;
03695    }
03696 
03697    while ((msgdirent = readdir(msgdir))) {
03698       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03699          map[msgdirint] = 1;
03700    }
03701    closedir(msgdir);
03702 
03703    for (x = 0; x < vmu->maxmsg; x++) {
03704       if (map[x] == 0)
03705          break;
03706    }
03707 
03708    return x - 1;
03709 }
03710 
03711 #endif /* #ifndef IMAP_STORAGE */
03712 #endif /* #else of #ifdef ODBC_STORAGE */
03713 #ifndef IMAP_STORAGE
03714 /*!
03715  * \brief Utility function to copy a file.
03716  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03717  * \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.
03718  *
03719  * 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.
03720  * The copy operation copies up to 4096 bytes at once.
03721  *
03722  * \return zero on success, -1 on error.
03723  */
03724 static int copy(char *infile, char *outfile)
03725 {
03726    int ifd;
03727    int ofd;
03728    int res;
03729    int len;
03730    char buf[4096];
03731 
03732 #ifdef HARDLINK_WHEN_POSSIBLE
03733    /* Hard link if possible; saves disk space & is faster */
03734    if (link(infile, outfile)) {
03735 #endif
03736       if ((ifd = open(infile, O_RDONLY)) < 0) {
03737          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03738          return -1;
03739       }
03740       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03741          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03742          close(ifd);
03743          return -1;
03744       }
03745       do {
03746          len = read(ifd, buf, sizeof(buf));
03747          if (len < 0) {
03748             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03749             close(ifd);
03750             close(ofd);
03751             unlink(outfile);
03752          }
03753          if (len) {
03754             res = write(ofd, buf, len);
03755             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03756                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03757                close(ifd);
03758                close(ofd);
03759                unlink(outfile);
03760             }
03761          }
03762       } while (len);
03763       close(ifd);
03764       close(ofd);
03765       return 0;
03766 #ifdef HARDLINK_WHEN_POSSIBLE
03767    } else {
03768       /* Hard link succeeded */
03769       return 0;
03770    }
03771 #endif
03772 }
03773 
03774 /*!
03775  * \brief Copies a voicemail information (envelope) file.
03776  * \param frompath
03777  * \param topath 
03778  *
03779  * Every voicemail has the data (.wav) file, and the information file.
03780  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03781  * This is used by the COPY macro when not using IMAP storage.
03782  */
03783 static void copy_plain_file(char *frompath, char *topath)
03784 {
03785    char frompath2[PATH_MAX], topath2[PATH_MAX];
03786    struct ast_variable *tmp,*var = NULL;
03787    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03788    ast_filecopy(frompath, topath, NULL);
03789    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03790    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03791    if (ast_check_realtime("voicemail_data")) {
03792       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03793       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03794       for (tmp = var; tmp; tmp = tmp->next) {
03795          if (!strcasecmp(tmp->name, "origmailbox")) {
03796             origmailbox = tmp->value;
03797          } else if (!strcasecmp(tmp->name, "context")) {
03798             context = tmp->value;
03799          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03800             macrocontext = tmp->value;
03801          } else if (!strcasecmp(tmp->name, "exten")) {
03802             exten = tmp->value;
03803          } else if (!strcasecmp(tmp->name, "priority")) {
03804             priority = tmp->value;
03805          } else if (!strcasecmp(tmp->name, "callerchan")) {
03806             callerchan = tmp->value;
03807          } else if (!strcasecmp(tmp->name, "callerid")) {
03808             callerid = tmp->value;
03809          } else if (!strcasecmp(tmp->name, "origdate")) {
03810             origdate = tmp->value;
03811          } else if (!strcasecmp(tmp->name, "origtime")) {
03812             origtime = tmp->value;
03813          } else if (!strcasecmp(tmp->name, "category")) {
03814             category = tmp->value;
03815          } else if (!strcasecmp(tmp->name, "duration")) {
03816             duration = tmp->value;
03817          }
03818       }
03819       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);
03820    }
03821    copy(frompath2, topath2);
03822    ast_variables_destroy(var);
03823 }
03824 #endif
03825 
03826 /*! 
03827  * \brief Removes the voicemail sound and information file.
03828  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03829  *
03830  * This is used by the DELETE macro when voicemails are stored on the file system.
03831  *
03832  * \return zero on success, -1 on error.
03833  */
03834 static int vm_delete(char *file)
03835 {
03836    char *txt;
03837    int txtsize = 0;
03838 
03839    txtsize = (strlen(file) + 5)*sizeof(char);
03840    txt = alloca(txtsize);
03841    /* Sprintf here would safe because we alloca'd exactly the right length,
03842     * but trying to eliminate all sprintf's anyhow
03843     */
03844    if (ast_check_realtime("voicemail_data")) {
03845       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03846    }
03847    snprintf(txt, txtsize, "%s.txt", file);
03848    unlink(txt);
03849    return ast_filedelete(file, NULL);
03850 }
03851 
03852 /*!
03853  * \brief utility used by inchar(), for base_encode()
03854  */
03855 static int inbuf(struct baseio *bio, FILE *fi)
03856 {
03857    int l;
03858 
03859    if (bio->ateof)
03860       return 0;
03861 
03862    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03863       if (ferror(fi))
03864          return -1;
03865 
03866       bio->ateof = 1;
03867       return 0;
03868    }
03869 
03870    bio->iolen= l;
03871    bio->iocp= 0;
03872 
03873    return 1;
03874 }
03875 
03876 /*!
03877  * \brief utility used by base_encode()
03878  */
03879 static int inchar(struct baseio *bio, FILE *fi)
03880 {
03881    if (bio->iocp>=bio->iolen) {
03882       if (!inbuf(bio, fi))
03883          return EOF;
03884    }
03885 
03886    return bio->iobuf[bio->iocp++];
03887 }
03888 
03889 /*!
03890  * \brief utility used by base_encode()
03891  */
03892 static int ochar(struct baseio *bio, int c, FILE *so)
03893 {
03894    if (bio->linelength >= BASELINELEN) {
03895       if (fputs(eol,so) == EOF)
03896          return -1;
03897 
03898       bio->linelength= 0;
03899    }
03900 
03901    if (putc(((unsigned char)c),so) == EOF)
03902       return -1;
03903 
03904    bio->linelength++;
03905 
03906    return 1;
03907 }
03908 
03909 /*!
03910  * \brief Performs a base 64 encode algorithm on the contents of a File
03911  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03912  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03913  *
03914  * 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 ?
03915  *
03916  * \return zero on success, -1 on error.
03917  */
03918 static int base_encode(char *filename, FILE *so)
03919 {
03920    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03921       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03922       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03923       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03924    int i,hiteof= 0;
03925    FILE *fi;
03926    struct baseio bio;
03927 
03928    memset(&bio, 0, sizeof(bio));
03929    bio.iocp = BASEMAXINLINE;
03930 
03931    if (!(fi = fopen(filename, "rb"))) {
03932       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03933       return -1;
03934    }
03935 
03936    while (!hiteof){
03937       unsigned char igroup[3], ogroup[4];
03938       int c,n;
03939 
03940       igroup[0]= igroup[1]= igroup[2]= 0;
03941 
03942       for (n= 0;n<3;n++) {
03943          if ((c = inchar(&bio, fi)) == EOF) {
03944             hiteof= 1;
03945             break;
03946          }
03947 
03948          igroup[n]= (unsigned char)c;
03949       }
03950 
03951       if (n> 0) {
03952          ogroup[0]= dtable[igroup[0]>>2];
03953          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03954          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03955          ogroup[3]= dtable[igroup[2]&0x3F];
03956 
03957          if (n<3) {
03958             ogroup[3]= '=';
03959 
03960             if (n<2)
03961                ogroup[2]= '=';
03962          }
03963 
03964          for (i= 0;i<4;i++)
03965             ochar(&bio, ogroup[i], so);
03966       }
03967    }
03968 
03969    fclose(fi);
03970    
03971    if (fputs(eol,so)==EOF)
03972       return 0;
03973 
03974    return 1;
03975 }
03976 
03977 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)
03978 {
03979    char callerid[256];
03980    char fromdir[256], fromfile[256];
03981    struct ast_config *msg_cfg;
03982    const char *origcallerid, *origtime;
03983    char origcidname[80], origcidnum[80], origdate[80];
03984    int inttime;
03985    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03986 
03987    /* Prepare variables for substitution in email body and subject */
03988    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03989    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03990    snprintf(passdata, passdatasize, "%d", msgnum);
03991    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03992    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03993    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03994    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03995       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03996    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03997    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03998    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03999    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04000    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04001 
04002    /* Retrieve info from VM attribute file */
04003    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04004    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04005    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04006       strcat(fromfile, ".txt");
04007    }
04008    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04009       if (option_debug > 0) {
04010          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04011       }
04012       return;
04013    }
04014 
04015    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04016       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04017       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04018       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04019       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04020    }
04021 
04022    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04023       struct timeval tv = { inttime, };
04024       struct ast_tm tm;
04025       ast_localtime(&tv, &tm, NULL);
04026       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04027       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04028    }
04029    ast_config_destroy(msg_cfg);
04030 }
04031 
04032 /*!
04033  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04034  * \param from The string to work with.
04035  * \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.
04036  * 
04037  * \return The destination string with quotes wrapped on it (the to field).
04038  */
04039 static char *quote(const char *from, char *to, size_t len)
04040 {
04041    char *ptr = to;
04042    *ptr++ = '"';
04043    for (; ptr < to + len - 1; from++) {
04044       if (*from == '"')
04045          *ptr++ = '\\';
04046       else if (*from == '\0')
04047          break;
04048       *ptr++ = *from;
04049    }
04050    if (ptr < to + len - 1)
04051       *ptr++ = '"';
04052    *ptr = '\0';
04053    return to;
04054 }
04055 
04056 /*! \brief
04057  * fill in *tm for current time according to the proper timezone, if any.
04058  * Return tm so it can be used as a function argument.
04059  */
04060 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04061 {
04062    const struct vm_zone *z = NULL;
04063    struct timeval t = ast_tvnow();
04064 
04065    /* Does this user have a timezone specified? */
04066    if (!ast_strlen_zero(vmu->zonetag)) {
04067       /* Find the zone in the list */
04068       AST_LIST_LOCK(&zones);
04069       AST_LIST_TRAVERSE(&zones, z, list) {
04070          if (!strcmp(z->name, vmu->zonetag))
04071             break;
04072       }
04073       AST_LIST_UNLOCK(&zones);
04074    }
04075    ast_localtime(&t, tm, z ? z->timezone : NULL);
04076    return tm;
04077 }
04078 
04079 /*!\brief Check if the string would need encoding within the MIME standard, to
04080  * avoid confusing certain mail software that expects messages to be 7-bit
04081  * clean.
04082  */
04083 static int check_mime(const char *str)
04084 {
04085    for (; *str; str++) {
04086       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04087          return 1;
04088       }
04089    }
04090    return 0;
04091 }
04092 
04093 /*!\brief Encode a string according to the MIME rules for encoding strings
04094  * that are not 7-bit clean or contain control characters.
04095  *
04096  * Additionally, if the encoded string would exceed the MIME limit of 76
04097  * characters per line, then the encoding will be broken up into multiple
04098  * sections, separated by a space character, in order to facilitate
04099  * breaking up the associated header across multiple lines.
04100  *
04101  * \param start A string to be encoded
04102  * \param end An expandable buffer for holding the result
04103  * \param preamble The length of the first line already used for this string,
04104  * to ensure that each line maintains a maximum length of 76 chars.
04105  * \param postamble the length of any additional characters appended to the
04106  * line, used to ensure proper field wrapping.
04107  * \retval The encoded string.
04108  */
04109 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
04110 {
04111    char tmp[80];
04112    int first_section = 1;
04113    size_t endlen = 0, tmplen = 0;
04114    *end = '\0';
04115 
04116    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04117    for (; *start; start++) {
04118       int need_encoding = 0;
04119       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04120          need_encoding = 1;
04121       }
04122       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04123          (first_section && !need_encoding && preamble + tmplen > 72) ||
04124          (!first_section && need_encoding && tmplen > 70) ||
04125          (!first_section && !need_encoding && tmplen > 72)) {
04126          /* Start new line */
04127          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04128          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04129          first_section = 0;
04130       }
04131       if (need_encoding && *start == ' ') {
04132          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04133       } else if (need_encoding) {
04134          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04135       } else {
04136          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04137       }
04138    }
04139    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04140    return end;
04141 }
04142 
04143 /*!
04144  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04145  * \param p The output file to generate the email contents into.
04146  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04147  * \param vmu The voicemail user who is sending the voicemail.
04148  * \param msgnum The message index in the mailbox folder.
04149  * \param context 
04150  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04151  * \param cidnum The caller ID number.
04152  * \param cidname The caller ID name.
04153  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04154  * \param format The message sound file format. i.e. .wav
04155  * \param duration The time of the message content, in seconds.
04156  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04157  * \param chan
04158  * \param category
04159  * \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.
04160  *
04161  * 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.
04162  */
04163 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)
04164 {
04165    char date[256];
04166    char host[MAXHOSTNAMELEN] = "";
04167    char who[256];
04168    char bound[256];
04169    char dur[256];
04170    struct ast_tm tm;
04171    char enc_cidnum[256] = "", enc_cidname[256] = "";
04172    char *passdata = NULL, *passdata2;
04173    size_t len_passdata = 0, len_passdata2, tmplen;
04174    char *greeting_attachment; 
04175    char filename[256];
04176 
04177 #ifdef IMAP_STORAGE
04178 #define ENDL "\r\n"
04179 #else
04180 #define ENDL "\n"
04181 #endif
04182 
04183    /* One alloca for multiple fields */
04184    len_passdata2 = strlen(vmu->fullname);
04185    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04186       len_passdata2 = tmplen;
04187    }
04188    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04189       len_passdata2 = tmplen;
04190    }
04191    len_passdata2 = len_passdata2 * 3 + 200;
04192    passdata2 = alloca(len_passdata2);
04193 
04194    if (cidnum) {
04195       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04196    }
04197    if (cidname) {
04198       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04199    }
04200    gethostname(host, sizeof(host) - 1);
04201 
04202    if (strchr(srcemail, '@'))
04203       ast_copy_string(who, srcemail, sizeof(who));
04204    else 
04205       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04206    
04207    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04208    if (greeting_attachment)
04209       *greeting_attachment++ = '\0';
04210 
04211    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04212    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04213    fprintf(p, "Date: %s" ENDL, date);
04214 
04215    /* Set date format for voicemail mail */
04216    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04217 
04218    if (!ast_strlen_zero(fromstring)) {
04219       struct ast_channel *ast;
04220       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04221          char *ptr;
04222          memset(passdata2, 0, len_passdata2);
04223          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04224          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04225          len_passdata = strlen(passdata2) * 3 + 300;
04226          passdata = alloca(len_passdata);
04227          if (check_mime(passdata2)) {
04228             int first_line = 1;
04229             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04230             while ((ptr = strchr(passdata, ' '))) {
04231                *ptr = '\0';
04232                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04233                first_line = 0;
04234                passdata = ptr + 1;
04235             }
04236             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04237          } else {
04238             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04239          }
04240          ast_channel_free(ast);
04241       } else {
04242          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04243       }
04244    } else {
04245       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04246    }
04247 
04248    if (check_mime(vmu->fullname)) {
04249       int first_line = 1;
04250       char *ptr;
04251       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04252       while ((ptr = strchr(passdata2, ' '))) {
04253          *ptr = '\0';
04254          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04255          first_line = 0;
04256          passdata2 = ptr + 1;
04257       }
04258       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04259    } else {
04260       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04261    }
04262    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04263       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04264       struct ast_channel *ast;
04265       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04266          int vmlen = strlen(e_subj) * 3 + 200;
04267          /* Only allocate more space if the previous was not large enough */
04268          if (vmlen > len_passdata) {
04269             passdata = alloca(vmlen);
04270             len_passdata = vmlen;
04271          }
04272 
04273          memset(passdata, 0, len_passdata);
04274          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04275          pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
04276          if (check_mime(passdata)) {
04277             int first_line = 1;
04278             char *ptr;
04279             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04280             while ((ptr = strchr(passdata2, ' '))) {
04281                *ptr = '\0';
04282                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04283                first_line = 0;
04284                passdata2 = ptr + 1;
04285             }
04286             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04287          } else {
04288             fprintf(p, "Subject: %s" ENDL, passdata);
04289          }
04290          ast_channel_free(ast);
04291       } else {
04292          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04293       }
04294    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04295       if (ast_strlen_zero(flag)) {
04296          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04297       } else {
04298          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04299       }
04300    } else {
04301       if (ast_strlen_zero(flag)) {
04302          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04303       } else {
04304          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04305       }
04306    }
04307 
04308    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04309    if (imap) {
04310       /* additional information needed for IMAP searching */
04311       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04312       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04313       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04314       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04315 #ifdef IMAP_STORAGE
04316       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04317 #else
04318       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04319 #endif
04320       /* flag added for Urgent */
04321       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04322       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04323       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04324       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04325       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04326       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04327       if (!ast_strlen_zero(category)) {
04328          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04329       } else {
04330          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04331       }
04332       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04333       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04334       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04335    }
04336    if (!ast_strlen_zero(cidnum)) {
04337       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04338    }
04339    if (!ast_strlen_zero(cidname)) {
04340       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04341    }
04342    fprintf(p, "MIME-Version: 1.0" ENDL);
04343    if (attach_user_voicemail) {
04344       /* Something unique. */
04345       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04346 
04347       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04348       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04349       fprintf(p, "--%s" ENDL, bound);
04350    }
04351    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04352    if (emailbody || vmu->emailbody) {
04353       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04354       struct ast_channel *ast;
04355       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04356          char *passdata;
04357          int vmlen = strlen(e_body) * 3 + 200;
04358          passdata = alloca(vmlen);
04359          memset(passdata, 0, vmlen);
04360          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04361          pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
04362          fprintf(p, "%s" ENDL, passdata);
04363          ast_channel_free(ast);
04364       } else
04365          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04366    } else if (msgnum > -1) {
04367       if (strcmp(vmu->mailbox, mailbox)) {
04368          /* Forwarded type */
04369          struct ast_config *msg_cfg;
04370          const char *v;
04371          int inttime;
04372          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04373          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04374          /* Retrieve info from VM attribute file */
04375          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04376          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04377          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04378             strcat(fromfile, ".txt");
04379          }
04380          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04381             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04382                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04383             }
04384 
04385             /* You might be tempted to do origdate, except that a) it's in the wrong
04386              * format, and b) it's missing for IMAP recordings. */
04387             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04388                struct timeval tv = { inttime, };
04389                struct ast_tm tm;
04390                ast_localtime(&tv, &tm, NULL);
04391                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04392             }
04393             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04394                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04395                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04396                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04397                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04398                date, origcallerid, origdate);
04399             ast_config_destroy(msg_cfg);
04400          } else {
04401             goto plain_message;
04402          }
04403       } else {
04404 plain_message:
04405          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04406             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04407             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04408             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04409             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04410       }
04411    } else {
04412       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04413             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04414    }
04415 
04416    if (imap || attach_user_voicemail) {
04417       if (!ast_strlen_zero(attach2)) {
04418          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04419          ast_debug(5, "creating second attachment filename %s\n", filename);
04420          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04421          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04422          ast_debug(5, "creating attachment filename %s\n", filename);
04423          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04424       } else {
04425          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04426          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04427          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04428       }
04429    }
04430 }
04431 
04432 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)
04433 {
04434    char tmpdir[256], newtmp[256];
04435    char fname[256];
04436    char tmpcmd[256];
04437    int tmpfd = -1;
04438 
04439    /* Eww. We want formats to tell us their own MIME type */
04440    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04441 
04442    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04443       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04444       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04445       tmpfd = mkstemp(newtmp);
04446       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04447       ast_debug(3, "newtmp: %s\n", newtmp);
04448       if (tmpfd > -1) {
04449          int soxstatus;
04450          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04451          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04452             attach = newtmp;
04453             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04454          } else {
04455             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04456                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04457             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04458          }
04459       }
04460    }
04461    fprintf(p, "--%s" ENDL, bound);
04462    if (msgnum > -1)
04463       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04464    else
04465       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04466    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04467    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04468    if (msgnum > -1)
04469       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04470    else
04471       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04472    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04473    base_encode(fname, p);
04474    if (last)
04475       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04476    if (tmpfd > -1) {
04477       unlink(fname);
04478       close(tmpfd);
04479       unlink(newtmp);
04480    }
04481    return 0;
04482 }
04483 #undef ENDL
04484 
04485 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)
04486 {
04487    FILE *p=NULL;
04488    char tmp[80] = "/tmp/astmail-XXXXXX";
04489    char tmp2[256];
04490 
04491    if (vmu && ast_strlen_zero(vmu->email)) {
04492       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04493       return(0);
04494    }
04495    if (!strcmp(format, "wav49"))
04496       format = "WAV";
04497    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));
04498    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04499       command hangs */
04500    if ((p = vm_mkftemp(tmp)) == NULL) {
04501       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04502       return -1;
04503    } else {
04504       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04505       fclose(p);
04506       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04507       ast_safe_system(tmp2);
04508       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04509    }
04510    return 0;
04511 }
04512 
04513 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)
04514 {
04515    char date[256];
04516    char host[MAXHOSTNAMELEN] = "";
04517    char who[256];
04518    char dur[PATH_MAX];
04519    char tmp[80] = "/tmp/astmail-XXXXXX";
04520    char tmp2[PATH_MAX];
04521    struct ast_tm tm;
04522    FILE *p;
04523 
04524    if ((p = vm_mkftemp(tmp)) == NULL) {
04525       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04526       return -1;
04527    }
04528    gethostname(host, sizeof(host)-1);
04529    if (strchr(srcemail, '@'))
04530       ast_copy_string(who, srcemail, sizeof(who));
04531    else 
04532       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04533    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04534    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04535    fprintf(p, "Date: %s\n", date);
04536 
04537    if (*pagerfromstring) {
04538       struct ast_channel *ast;
04539       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04540          char *passdata;
04541          int vmlen = strlen(fromstring)*3 + 200;
04542          passdata = alloca(vmlen);
04543          memset(passdata, 0, vmlen);
04544          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04545          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04546          fprintf(p, "From: %s <%s>\n", passdata, who);
04547          ast_channel_free(ast);
04548       } else 
04549          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04550    } else
04551       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04552    fprintf(p, "To: %s\n", pager);
04553    if (pagersubject) {
04554       struct ast_channel *ast;
04555       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04556          char *passdata;
04557          int vmlen = strlen(pagersubject) * 3 + 200;
04558          passdata = alloca(vmlen);
04559          memset(passdata, 0, vmlen);
04560          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04561          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04562          fprintf(p, "Subject: %s\n\n", passdata);
04563          ast_channel_free(ast);
04564       } else
04565          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04566    } else {
04567       if (ast_strlen_zero(flag)) {
04568          fprintf(p, "Subject: New VM\n\n");
04569       } else {
04570          fprintf(p, "Subject: New %s VM\n\n", flag);
04571       }
04572    }
04573 
04574    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04575    if (pagerbody) {
04576       struct ast_channel *ast;
04577       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04578          char *passdata;
04579          int vmlen = strlen(pagerbody) * 3 + 200;
04580          passdata = alloca(vmlen);
04581          memset(passdata, 0, vmlen);
04582          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04583          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04584          fprintf(p, "%s\n", passdata);
04585          ast_channel_free(ast);
04586       } else
04587          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04588    } else {
04589       fprintf(p, "New %s long %s msg in box %s\n"
04590             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04591    }
04592    fclose(p);
04593    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04594    ast_safe_system(tmp2);
04595    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04596    return 0;
04597 }
04598 
04599 /*!
04600  * \brief Gets the current date and time, as formatted string.
04601  * \param s The buffer to hold the output formatted date.
04602  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04603  * 
04604  * The date format string used is "%a %b %e %r UTC %Y".
04605  * 
04606  * \return zero on success, -1 on error.
04607  */
04608 static int get_date(char *s, int len)
04609 {
04610    struct ast_tm tm;
04611    struct timeval t = ast_tvnow();
04612    
04613    ast_localtime(&t, &tm, "UTC");
04614 
04615    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04616 }
04617 
04618 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04619 {
04620    int res;
04621    char fn[PATH_MAX];
04622    char dest[PATH_MAX];
04623 
04624    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04625 
04626    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04627       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04628       return -1;
04629    }
04630 
04631    RETRIEVE(fn, -1, ext, context);
04632    if (ast_fileexists(fn, NULL, NULL) > 0) {
04633       res = ast_stream_and_wait(chan, fn, ecodes);
04634       if (res) {
04635          DISPOSE(fn, -1);
04636          return res;
04637       }
04638    } else {
04639       /* Dispose just in case */
04640       DISPOSE(fn, -1);
04641       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04642       if (res)
04643          return res;
04644       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04645       if (res)
04646          return res;
04647    }
04648    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04649    return res;
04650 }
04651 
04652 static void free_zone(struct vm_zone *z)
04653 {
04654    ast_free(z);
04655 }
04656 
04657 #ifdef ODBC_STORAGE
04658 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04659 {
04660    int x = -1;
04661    int res;
04662    SQLHSTMT stmt = NULL;
04663    char sql[PATH_MAX];
04664    char rowdata[20];
04665    char tmp[PATH_MAX] = "";
04666    struct odbc_obj *obj = NULL;
04667    char *context;
04668    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04669 
04670    if (newmsgs)
04671       *newmsgs = 0;
04672    if (oldmsgs)
04673       *oldmsgs = 0;
04674    if (urgentmsgs)
04675       *urgentmsgs = 0;
04676 
04677    /* If no mailbox, return immediately */
04678    if (ast_strlen_zero(mailbox))
04679       return 0;
04680 
04681    ast_copy_string(tmp, mailbox, sizeof(tmp));
04682 
04683    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04684       int u, n, o;
04685       char *next, *remaining = tmp;
04686       while ((next = strsep(&remaining, " ,"))) {
04687          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04688             return -1;
04689          }
04690          if (urgentmsgs) {
04691             *urgentmsgs += u;
04692          }
04693          if (newmsgs) {
04694             *newmsgs += n;
04695          }
04696          if (oldmsgs) {
04697             *oldmsgs += o;
04698          }
04699       }
04700       return 0;
04701    }
04702 
04703    context = strchr(tmp, '@');
04704    if (context) {
04705       *context = '\0';
04706       context++;
04707    } else
04708       context = "default";
04709 
04710    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04711       do {
04712          if (newmsgs) {
04713             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04714             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04715                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04716                break;
04717             }
04718             res = SQLFetch(stmt);
04719             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04720                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04721                break;
04722             }
04723             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04724             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04725                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04726                break;
04727             }
04728             *newmsgs = atoi(rowdata);
04729             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04730          }
04731 
04732          if (oldmsgs) {
04733             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04734             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04735                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04736                break;
04737             }
04738             res = SQLFetch(stmt);
04739             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04740                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04741                break;
04742             }
04743             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04744             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04745                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04746                break;
04747             }
04748             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04749             *oldmsgs = atoi(rowdata);
04750          }
04751 
04752          if (urgentmsgs) {
04753             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04754             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04755                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04756                break;
04757             }
04758             res = SQLFetch(stmt);
04759             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04760                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04761                break;
04762             }
04763             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04764             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04765                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04766                break;
04767             }
04768             *urgentmsgs = atoi(rowdata);
04769          }
04770 
04771          x = 0;
04772       } while (0);
04773    } else {
04774       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04775    }
04776 
04777    if (stmt) {
04778       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04779    }
04780    if (obj) {
04781       ast_odbc_release_obj(obj);
04782    }
04783 
04784    return x;
04785 }
04786 
04787 /*!
04788  * \brief Gets the number of messages that exist in a mailbox folder.
04789  * \param context
04790  * \param mailbox
04791  * \param folder
04792  * 
04793  * This method is used when ODBC backend is used.
04794  * \return The number of messages in this mailbox folder (zero or more).
04795  */
04796 static int messagecount(const char *context, const char *mailbox, const char *folder)
04797 {
04798    struct odbc_obj *obj = NULL;
04799    int nummsgs = 0;
04800    int res;
04801    SQLHSTMT stmt = NULL;
04802    char sql[PATH_MAX];
04803    char rowdata[20];
04804    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04805    if (!folder)
04806       folder = "INBOX";
04807    /* If no mailbox, return immediately */
04808    if (ast_strlen_zero(mailbox))
04809       return 0;
04810 
04811    obj = ast_odbc_request_obj(odbc_database, 0);
04812    if (obj) {
04813       if (!strcmp(folder, "INBOX")) {
04814          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);
04815       } else {
04816          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04817       }
04818       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04819       if (!stmt) {
04820          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04821          goto yuck;
04822       }
04823       res = SQLFetch(stmt);
04824       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04825          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04826          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04827          goto yuck;
04828       }
04829       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04830       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04831          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04832          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04833          goto yuck;
04834       }
04835       nummsgs = atoi(rowdata);
04836       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04837    } else
04838       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04839 
04840 yuck:
04841    if (obj)
04842       ast_odbc_release_obj(obj);
04843    return nummsgs;
04844 }
04845 
04846 /** 
04847  * \brief Determines if the given folder has messages.
04848  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04849  * 
04850  * This function is used when the mailbox is stored in an ODBC back end.
04851  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04852  * \return 1 if the folder has one or more messages. zero otherwise.
04853  */
04854 static int has_voicemail(const char *mailbox, const char *folder)
04855 {
04856    char tmp[256], *tmp2 = tmp, *box, *context;
04857    ast_copy_string(tmp, mailbox, sizeof(tmp));
04858    while ((context = box = strsep(&tmp2, ",&"))) {
04859       strsep(&context, "@");
04860       if (ast_strlen_zero(context))
04861          context = "default";
04862       if (messagecount(context, box, folder))
04863          return 1;
04864    }
04865    return 0;
04866 }
04867 #endif
04868 #ifndef IMAP_STORAGE
04869 /*! 
04870  * \brief Copies a message from one mailbox to another.
04871  * \param chan
04872  * \param vmu
04873  * \param imbox
04874  * \param msgnum
04875  * \param duration
04876  * \param recip
04877  * \param fmt
04878  * \param dir
04879  *
04880  * This is only used by file storage based mailboxes.
04881  *
04882  * \return zero on success, -1 on error.
04883  */
04884 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)
04885 {
04886    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04887    const char *frombox = mbox(imbox);
04888    int recipmsgnum;
04889 
04890    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04891 
04892    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04893       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04894    } else {
04895       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04896    }
04897    
04898    if (!dir)
04899       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04900    else
04901       ast_copy_string(fromdir, dir, sizeof(fromdir));
04902 
04903    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04904    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04905 
04906    if (vm_lock_path(todir))
04907       return ERROR_LOCK_PATH;
04908 
04909    recipmsgnum = last_message_index(recip, todir) + 1;
04910    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04911       make_file(topath, sizeof(topath), todir, recipmsgnum);
04912       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04913          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04914       } else {
04915          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04916           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04917           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04918           * much worse problem happening and IMAP storage doesn't call this function
04919           */
04920          copy_plain_file(frompath, topath);
04921          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04922          vm_delete(topath);
04923       }
04924    } else {
04925       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04926    }
04927    ast_unlock_path(todir);
04928    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04929    
04930    return 0;
04931 }
04932 #endif
04933 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04934 
04935 static int messagecount(const char *context, const char *mailbox, const char *folder)
04936 {
04937    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
04938 }
04939 
04940 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04941 {
04942    DIR *dir;
04943    struct dirent *de;
04944    char fn[256];
04945    int ret = 0;
04946 
04947    /* If no mailbox, return immediately */
04948    if (ast_strlen_zero(mailbox))
04949       return 0;
04950 
04951    if (ast_strlen_zero(folder))
04952       folder = "INBOX";
04953    if (ast_strlen_zero(context))
04954       context = "default";
04955 
04956    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04957 
04958    if (!(dir = opendir(fn)))
04959       return 0;
04960 
04961    while ((de = readdir(dir))) {
04962       if (!strncasecmp(de->d_name, "msg", 3)) {
04963          if (shortcircuit) {
04964             ret = 1;
04965             break;
04966          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
04967             ret++;
04968          }
04969       }
04970    }
04971 
04972    closedir(dir);
04973 
04974    return ret;
04975 }
04976 
04977 /** 
04978  * \brief Determines if the given folder has messages.
04979  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04980  * \param folder the folder to look in
04981  *
04982  * This function is used when the mailbox is stored in a filesystem back end.
04983  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04984  * \return 1 if the folder has one or more messages. zero otherwise.
04985  */
04986 static int has_voicemail(const char *mailbox, const char *folder)
04987 {
04988    char tmp[256], *tmp2 = tmp, *box, *context;
04989    ast_copy_string(tmp, mailbox, sizeof(tmp));
04990    if (ast_strlen_zero(folder)) {
04991       folder = "INBOX";
04992    }
04993    while ((box = strsep(&tmp2, ",&"))) {
04994       if ((context = strchr(box, '@')))
04995          *context++ = '\0';
04996       else
04997          context = "default";
04998       if (__has_voicemail(context, box, folder, 1))
04999          return 1;
05000       /* If we are checking INBOX, we should check Urgent as well */
05001       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05002          return 1;
05003       }
05004    }
05005    return 0;
05006 }
05007 
05008 
05009 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05010 {
05011    char tmp[256];
05012    char *context;
05013 
05014    /* If no mailbox, return immediately */
05015    if (ast_strlen_zero(mailbox))
05016       return 0;
05017 
05018    if (newmsgs)
05019       *newmsgs = 0;
05020    if (oldmsgs)
05021       *oldmsgs = 0;
05022    if (urgentmsgs)
05023       *urgentmsgs = 0;
05024 
05025    if (strchr(mailbox, ',')) {
05026       int tmpnew, tmpold, tmpurgent;
05027       char *mb, *cur;
05028 
05029       ast_copy_string(tmp, mailbox, sizeof(tmp));
05030       mb = tmp;
05031       while ((cur = strsep(&mb, ", "))) {
05032          if (!ast_strlen_zero(cur)) {
05033             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05034                return -1;
05035             else {
05036                if (newmsgs)
05037                   *newmsgs += tmpnew; 
05038                if (oldmsgs)
05039                   *oldmsgs += tmpold;
05040                if (urgentmsgs)
05041                   *urgentmsgs += tmpurgent;
05042             }
05043          }
05044       }
05045       return 0;
05046    }
05047 
05048    ast_copy_string(tmp, mailbox, sizeof(tmp));
05049    
05050    if ((context = strchr(tmp, '@')))
05051       *context++ = '\0';
05052    else
05053       context = "default";
05054 
05055    if (newmsgs)
05056       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05057    if (oldmsgs)
05058       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05059    if (urgentmsgs)
05060       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05061 
05062    return 0;
05063 }
05064 
05065 #endif
05066 
05067 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05068 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05069 {
05070    int urgentmsgs = 0;
05071    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05072    if (newmsgs) {
05073       *newmsgs += urgentmsgs;
05074    }
05075    return res;
05076 }
05077 
05078 static void run_externnotify(char *context, char *extension, const char *flag)
05079 {
05080    char arguments[255];
05081    char ext_context[256] = "";
05082    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05083    struct ast_smdi_mwi_message *mwi_msg;
05084 
05085    if (!ast_strlen_zero(context))
05086       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05087    else
05088       ast_copy_string(ext_context, extension, sizeof(ext_context));
05089 
05090    if (smdi_iface) {
05091       if (ast_app_has_voicemail(ext_context, NULL)) 
05092          ast_smdi_mwi_set(smdi_iface, extension);
05093       else
05094          ast_smdi_mwi_unset(smdi_iface, extension);
05095 
05096       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05097          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05098          if (!strncmp(mwi_msg->cause, "INV", 3))
05099             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05100          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05101             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05102          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05103          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05104       } else {
05105          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05106       }
05107    }
05108 
05109    if (!ast_strlen_zero(externnotify)) {
05110       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05111          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05112       } else {
05113          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05114          ast_debug(1, "Executing %s\n", arguments);
05115          ast_safe_system(arguments);
05116       }
05117    }
05118 }
05119 
05120 /*!
05121  * \brief Variables used for saving a voicemail.
05122  *
05123  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05124  */
05125 struct leave_vm_options {
05126    unsigned int flags;
05127    signed char record_gain;
05128    char *exitcontext;
05129 };
05130 
05131 /*!
05132  * \brief Prompts the user and records a voicemail to a mailbox.
05133  * \param chan
05134  * \param ext
05135  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05136  * 
05137  * 
05138  * 
05139  * \return zero on success, -1 on error.
05140  */
05141 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05142 {
05143 #ifdef IMAP_STORAGE
05144    int newmsgs, oldmsgs;
05145 #else
05146    char urgdir[PATH_MAX];
05147 #endif
05148    char txtfile[PATH_MAX];
05149    char tmptxtfile[PATH_MAX];
05150    struct vm_state *vms = NULL;
05151    char callerid[256];
05152    FILE *txt;
05153    char date[256];
05154    int txtdes;
05155    int res = 0;
05156    int msgnum;
05157    int duration = 0;
05158    int ausemacro = 0;
05159    int ousemacro = 0;
05160    int ouseexten = 0;
05161    char tmpdur[16];
05162    char priority[16];
05163    char origtime[16];
05164    char dir[PATH_MAX];
05165    char tmpdir[PATH_MAX];
05166    char fn[PATH_MAX];
05167    char prefile[PATH_MAX] = "";
05168    char tempfile[PATH_MAX] = "";
05169    char ext_context[256] = "";
05170    char fmt[80];
05171    char *context;
05172    char ecodes[17] = "#";
05173    struct ast_str *tmp = ast_str_create(16);
05174    char *tmpptr;
05175    struct ast_vm_user *vmu;
05176    struct ast_vm_user svm;
05177    const char *category = NULL;
05178    const char *code;
05179    const char *alldtmf = "0123456789ABCD*#";
05180    char flag[80];
05181 
05182    ast_str_set(&tmp, 0, "%s", ext);
05183    ext = ast_str_buffer(tmp);
05184    if ((context = strchr(ext, '@'))) {
05185       *context++ = '\0';
05186       tmpptr = strchr(context, '&');
05187    } else {
05188       tmpptr = strchr(ext, '&');
05189    }
05190 
05191    if (tmpptr)
05192       *tmpptr++ = '\0';
05193 
05194    ast_channel_lock(chan);
05195    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05196       category = ast_strdupa(category);
05197    }
05198    ast_channel_unlock(chan);
05199 
05200    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05201       ast_copy_string(flag, "Urgent", sizeof(flag));
05202    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05203       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05204    } else {
05205       flag[0] = '\0';
05206    }
05207 
05208    ast_debug(3, "Before find_user\n");
05209    if (!(vmu = find_user(&svm, context, ext))) {
05210       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05211       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05212       ast_free(tmp);
05213       return res;
05214    }
05215    /* Setup pre-file if appropriate */
05216    if (strcmp(vmu->context, "default"))
05217       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05218    else
05219       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05220 
05221    /* Set the path to the prefile. Will be one of 
05222       VM_SPOOL_DIRcontext/ext/busy
05223       VM_SPOOL_DIRcontext/ext/unavail
05224       Depending on the flag set in options.
05225    */
05226    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05227       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05228    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05229       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05230    }
05231    /* Set the path to the tmpfile as
05232       VM_SPOOL_DIR/context/ext/temp
05233       and attempt to create the folder structure.
05234    */
05235    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05236    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05237       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05238       ast_free(tmp);
05239       return -1;
05240    }
05241    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05242    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05243       ast_copy_string(prefile, tempfile, sizeof(prefile));
05244 
05245    DISPOSE(tempfile, -1);
05246    /* It's easier just to try to make it than to check for its existence */
05247    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05248 
05249    /* Check current or macro-calling context for special extensions */
05250    if (ast_test_flag(vmu, VM_OPERATOR)) {
05251       if (!ast_strlen_zero(vmu->exit)) {
05252          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05253             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05254             ouseexten = 1;
05255          }
05256       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05257          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05258          ouseexten = 1;
05259       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05260          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05261          ousemacro = 1;
05262       }
05263    }
05264 
05265    if (!ast_strlen_zero(vmu->exit)) {
05266       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05267          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05268    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05269       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05270    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05271       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05272       ausemacro = 1;
05273    }
05274 
05275    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05276       for (code = alldtmf; *code; code++) {
05277          char e[2] = "";
05278          e[0] = *code;
05279          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05280             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05281       }
05282    }
05283 
05284    /* Play the beginning intro if desired */
05285    if (!ast_strlen_zero(prefile)) {
05286 #ifdef ODBC_STORAGE
05287       int success = 
05288 #endif
05289          RETRIEVE(prefile, -1, ext, context);
05290       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05291          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05292             res = ast_waitstream(chan, ecodes);
05293 #ifdef ODBC_STORAGE
05294          if (success == -1) {
05295             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05296             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05297             store_file(prefile, vmu->mailbox, vmu->context, -1);
05298          }
05299 #endif
05300       } else {
05301          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05302          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05303       }
05304       DISPOSE(prefile, -1);
05305       if (res < 0) {
05306          ast_debug(1, "Hang up during prefile playback\n");
05307          free_user(vmu);
05308          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05309          ast_free(tmp);
05310          return -1;
05311       }
05312    }
05313    if (res == '#') {
05314       /* On a '#' we skip the instructions */
05315       ast_set_flag(options, OPT_SILENT);
05316       res = 0;
05317    }
05318    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05319       res = ast_stream_and_wait(chan, INTRO, ecodes);
05320       if (res == '#') {
05321          ast_set_flag(options, OPT_SILENT);
05322          res = 0;
05323       }
05324    }
05325    if (res > 0)
05326       ast_stopstream(chan);
05327    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05328     other than the operator -- an automated attendant or mailbox login for example */
05329    if (res == '*') {
05330       chan->exten[0] = 'a';
05331       chan->exten[1] = '\0';
05332       if (!ast_strlen_zero(vmu->exit)) {
05333          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05334       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05335          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05336       }
05337       chan->priority = 0;
05338       free_user(vmu);
05339       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05340       ast_free(tmp);
05341       return 0;
05342    }
05343 
05344    /* Check for a '0' here */
05345    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05346    transfer:
05347       if (ouseexten || ousemacro) {
05348          chan->exten[0] = 'o';
05349          chan->exten[1] = '\0';
05350          if (!ast_strlen_zero(vmu->exit)) {
05351             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05352          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05353             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05354          }
05355          ast_play_and_wait(chan, "transfer");
05356          chan->priority = 0;
05357          free_user(vmu);
05358          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05359       }
05360       ast_free(tmp);
05361       return 0;
05362    }
05363 
05364    /* Allow all other digits to exit Voicemail and return to the dialplan */
05365    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05366       if (!ast_strlen_zero(options->exitcontext))
05367          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05368       free_user(vmu);
05369       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05370       ast_free(tmp);
05371       return res;
05372    }
05373 
05374    if (res < 0) {
05375       free_user(vmu);
05376       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05377       ast_free(tmp);
05378       return -1;
05379    }
05380    /* The meat of recording the message...  All the announcements and beeps have been played*/
05381    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05382    if (!ast_strlen_zero(fmt)) {
05383       msgnum = 0;
05384 
05385 #ifdef IMAP_STORAGE
05386       /* Is ext a mailbox? */
05387       /* must open stream for this user to get info! */
05388       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05389       if (res < 0) {
05390          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05391          ast_free(tmp);
05392          return -1;
05393       }
05394       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05395       /* It is possible under certain circumstances that inboxcount did not
05396        * create a vm_state when it was needed. This is a catchall which will
05397        * rarely be used.
05398        */
05399          if (!(vms = create_vm_state_from_user(vmu))) {
05400             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05401             ast_free(tmp);
05402             return -1;
05403          }
05404       }
05405       vms->newmessages++;
05406       
05407       /* here is a big difference! We add one to it later */
05408       msgnum = newmsgs + oldmsgs;
05409       ast_debug(3, "Messagecount set to %d\n",msgnum);
05410       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05411       /* set variable for compatibility */
05412       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05413 
05414       /* Check if mailbox is full */
05415       check_quota(vms, imapfolder);
05416       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
05417          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
05418          ast_play_and_wait(chan, "vm-mailboxfull");
05419          ast_free(tmp);
05420          return -1;
05421       }
05422       
05423       /* Check if we have exceeded maxmsg */
05424       if (msgnum >= vmu->maxmsg  - inprocess_count(vmu->mailbox, vmu->context, 0)) {
05425          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);
05426          ast_play_and_wait(chan, "vm-mailboxfull");
05427          ast_free(tmp);
05428          return -1;
05429       }
05430 #else
05431       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05432          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05433          if (!res)
05434             res = ast_waitstream(chan, "");
05435          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05436          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05437          inprocess_count(vmu->mailbox, vmu->context, -1);
05438          goto leave_vm_out;
05439       }
05440 
05441 #endif
05442       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05443       txtdes = mkstemp(tmptxtfile);
05444       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05445       if (txtdes < 0) {
05446          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05447          if (!res)
05448             res = ast_waitstream(chan, "");
05449          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05450          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05451          inprocess_count(vmu->mailbox, vmu->context, -1);
05452          goto leave_vm_out;
05453       }
05454 
05455       /* Now play the beep once we have the message number for our next message. */
05456       if (res >= 0) {
05457          /* Unless we're *really* silent, try to send the beep */
05458          res = ast_stream_and_wait(chan, "beep", "");
05459       }
05460             
05461       /* Store information in real-time storage */
05462       if (ast_check_realtime("voicemail_data")) {
05463          snprintf(priority, sizeof(priority), "%d", chan->priority);
05464          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05465          get_date(date, sizeof(date));
05466          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);
05467       }
05468 
05469       /* Store information */
05470       txt = fdopen(txtdes, "w+");
05471       if (txt) {
05472          get_date(date, sizeof(date));
05473          fprintf(txt, 
05474             ";\n"
05475             "; Message Information file\n"
05476             ";\n"
05477             "[message]\n"
05478             "origmailbox=%s\n"
05479             "context=%s\n"
05480             "macrocontext=%s\n"
05481             "exten=%s\n"
05482             "priority=%d\n"
05483             "callerchan=%s\n"
05484             "callerid=%s\n"
05485             "origdate=%s\n"
05486             "origtime=%ld\n"
05487             "category=%s\n",
05488             ext,
05489             chan->context,
05490             chan->macrocontext, 
05491             chan->exten,
05492             chan->priority,
05493             chan->name,
05494             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05495             date, (long)time(NULL),
05496             category ? category : "");
05497       } else {
05498          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05499          inprocess_count(vmu->mailbox, vmu->context, -1);
05500          if (ast_check_realtime("voicemail_data")) {
05501             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05502          }
05503          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05504          goto leave_vm_out;
05505       }
05506       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05507 
05508       if (txt) {
05509          fprintf(txt, "flag=%s\n", flag);
05510          if (duration < vmminsecs) {
05511             fclose(txt);
05512             if (option_verbose > 2) 
05513                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05514             ast_filedelete(tmptxtfile, NULL);
05515             unlink(tmptxtfile);
05516             if (ast_check_realtime("voicemail_data")) {
05517                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05518             }
05519             inprocess_count(vmu->mailbox, vmu->context, -1);
05520          } else {
05521             fprintf(txt, "duration=%d\n", duration);
05522             fclose(txt);
05523             if (vm_lock_path(dir)) {
05524                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05525                /* Delete files */
05526                ast_filedelete(tmptxtfile, NULL);
05527                unlink(tmptxtfile);
05528                inprocess_count(vmu->mailbox, vmu->context, -1);
05529             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05530                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05531                unlink(tmptxtfile);
05532                ast_unlock_path(dir);
05533                inprocess_count(vmu->mailbox, vmu->context, -1);
05534                if (ast_check_realtime("voicemail_data")) {
05535                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05536                }
05537             } else {
05538 #ifndef IMAP_STORAGE
05539                msgnum = last_message_index(vmu, dir) + 1;
05540 #endif
05541                make_file(fn, sizeof(fn), dir, msgnum);
05542 
05543                /* assign a variable with the name of the voicemail file */ 
05544 #ifndef IMAP_STORAGE
05545                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05546 #else
05547                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05548 #endif
05549 
05550                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05551                ast_filerename(tmptxtfile, fn, NULL);
05552                rename(tmptxtfile, txtfile);
05553                inprocess_count(vmu->mailbox, vmu->context, -1);
05554 
05555                /* Properly set permissions on voicemail text descriptor file.
05556                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05557                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05558                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05559 
05560                ast_unlock_path(dir);
05561                if (ast_check_realtime("voicemail_data")) {
05562                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05563                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05564                }
05565                /* We must store the file first, before copying the message, because
05566                 * ODBC storage does the entire copy with SQL.
05567                 */
05568                if (ast_fileexists(fn, NULL, NULL) > 0) {
05569                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05570                }
05571 
05572                /* Are there to be more recipients of this message? */
05573                while (tmpptr) {
05574                   struct ast_vm_user recipu, *recip;
05575                   char *exten, *cntx;
05576                
05577                   exten = strsep(&tmpptr, "&");
05578                   cntx = strchr(exten, '@');
05579                   if (cntx) {
05580                      *cntx = '\0';
05581                      cntx++;
05582                   }
05583                   if ((recip = find_user(&recipu, cntx, exten))) {
05584                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05585                      free_user(recip);
05586                   }
05587                }
05588 #ifndef IMAP_STORAGE
05589                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05590                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05591                   char sfn[PATH_MAX];
05592                   char dfn[PATH_MAX];
05593                   int x;
05594                   /* It's easier just to try to make it than to check for its existence */
05595                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05596                   x = last_message_index(vmu, urgdir) + 1;
05597                   make_file(sfn, sizeof(sfn), dir, msgnum);
05598                   make_file(dfn, sizeof(dfn), urgdir, x);
05599                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05600                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05601                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05602                   ast_copy_string(fn, dfn, sizeof(fn));
05603                   msgnum = x;
05604                }
05605 #endif
05606                /* Notification needs to happen after the copy, though. */
05607                if (ast_fileexists(fn, NULL, NULL)) {
05608 #ifdef IMAP_STORAGE
05609                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05610 #else
05611                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05612 #endif
05613                }
05614 
05615                /* Disposal needs to happen after the optional move and copy */
05616                if (ast_fileexists(fn, NULL, NULL)) {
05617                   DISPOSE(dir, msgnum);
05618                }
05619             }
05620          }
05621       } else {
05622          inprocess_count(vmu->mailbox, vmu->context, -1);
05623       }
05624       if (res == '0') {
05625          goto transfer;
05626       } else if (res > 0)
05627          res = 0;
05628 
05629       if (duration < vmminsecs)
05630          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05631          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05632       else
05633          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05634    } else
05635       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05636 leave_vm_out:
05637    free_user(vmu);
05638 
05639 #ifdef IMAP_STORAGE
05640    /* expunge message - use UID Expunge if supported on IMAP server*/
05641    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05642    if (expungeonhangup == 1) {
05643       ast_mutex_lock(&vms->lock);
05644 #ifdef HAVE_IMAP_TK2006
05645       if (LEVELUIDPLUS (vms->mailstream)) {
05646          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05647       } else 
05648 #endif
05649          mail_expunge(vms->mailstream);
05650       ast_mutex_unlock(&vms->lock);
05651    }
05652 #endif
05653 
05654    ast_free(tmp);
05655    return res;
05656 }
05657 
05658 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05659 {
05660    int d;
05661    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05662    return d;
05663 }
05664 
05665 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05666 {
05667 #ifdef IMAP_STORAGE
05668    /* we must use mbox(x) folder names, and copy the message there */
05669    /* simple. huh? */
05670    char sequence[10];
05671    char mailbox[256];
05672    int res;
05673 
05674    /* get the real IMAP message number for this message */
05675    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05676    
05677    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05678    ast_mutex_lock(&vms->lock);
05679    /* if save to Old folder, put in INBOX as read */
05680    if (box == OLD_FOLDER) {
05681       mail_setflag(vms->mailstream, sequence, "\\Seen");
05682       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05683    } else if (box == NEW_FOLDER) {
05684       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05685       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05686    }
05687    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05688       ast_mutex_unlock(&vms->lock);
05689       return 0;
05690    }
05691    /* Create the folder if it don't exist */
05692    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05693    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05694    if (mail_create(vms->mailstream, mailbox) == NIL) 
05695       ast_debug(5, "Folder exists.\n");
05696    else
05697       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05698    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05699    ast_mutex_unlock(&vms->lock);
05700    return res;
05701 #else
05702    char *dir = vms->curdir;
05703    char *username = vms->username;
05704    char *context = vmu->context;
05705    char sfn[PATH_MAX];
05706    char dfn[PATH_MAX];
05707    char ddir[PATH_MAX];
05708    const char *dbox = mbox(box);
05709    int x, i;
05710    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05711 
05712    if (vm_lock_path(ddir))
05713       return ERROR_LOCK_PATH;
05714 
05715    x = last_message_index(vmu, ddir) + 1;
05716 
05717    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05718       x--;
05719       for (i = 1; i <= x; i++) {
05720          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05721          make_file(sfn, sizeof(sfn), ddir, i);
05722          make_file(dfn, sizeof(dfn), ddir, i - 1);
05723          if (EXISTS(ddir, i, sfn, NULL)) {
05724             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05725          } else
05726             break;
05727       }
05728    } else {
05729       if (x >= vmu->maxmsg) {
05730          ast_unlock_path(ddir);
05731          return -1;
05732       }
05733    }
05734    make_file(sfn, sizeof(sfn), dir, msg);
05735    make_file(dfn, sizeof(dfn), ddir, x);
05736    if (strcmp(sfn, dfn)) {
05737       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05738    }
05739    ast_unlock_path(ddir);
05740 #endif
05741    return 0;
05742 }
05743 
05744 static int adsi_logo(unsigned char *buf)
05745 {
05746    int bytes = 0;
05747    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05748    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05749    return bytes;
05750 }
05751 
05752 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05753 {
05754    unsigned char buf[256];
05755    int bytes=0;
05756    int x;
05757    char num[5];
05758 
05759    *useadsi = 0;
05760    bytes += ast_adsi_data_mode(buf + bytes);
05761    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05762 
05763    bytes = 0;
05764    bytes += adsi_logo(buf);
05765    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05766 #ifdef DISPLAY
05767    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05768 #endif
05769    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05770    bytes += ast_adsi_data_mode(buf + bytes);
05771    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05772 
05773    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05774       bytes = 0;
05775       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05776       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05777       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05778       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05779       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05780       return 0;
05781    }
05782 
05783 #ifdef DISPLAY
05784    /* Add a dot */
05785    bytes = 0;
05786    bytes += ast_adsi_logo(buf);
05787    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05788    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05789    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05790    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05791 #endif
05792    bytes = 0;
05793    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05794    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05795    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05796    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05797    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05798    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05799    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05800 
05801 #ifdef DISPLAY
05802    /* Add another dot */
05803    bytes = 0;
05804    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05805    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05806 
05807    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05808    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05809 #endif
05810 
05811    bytes = 0;
05812    /* These buttons we load but don't use yet */
05813    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05814    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05815    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05816    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05817    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05818    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05819    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05820 
05821 #ifdef DISPLAY
05822    /* Add another dot */
05823    bytes = 0;
05824    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05825    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05826    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05827 #endif
05828 
05829    bytes = 0;
05830    for (x=0;x<5;x++) {
05831       snprintf(num, sizeof(num), "%d", x);
05832       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05833    }
05834    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05835    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05836 
05837 #ifdef DISPLAY
05838    /* Add another dot */
05839    bytes = 0;
05840    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05841    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05842    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05843 #endif
05844 
05845    if (ast_adsi_end_download(chan)) {
05846       bytes = 0;
05847       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05848       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05849       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05850       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05851       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05852       return 0;
05853    }
05854    bytes = 0;
05855    bytes += ast_adsi_download_disconnect(buf + bytes);
05856    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05857    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05858 
05859    ast_debug(1, "Done downloading scripts...\n");
05860 
05861 #ifdef DISPLAY
05862    /* Add last dot */
05863    bytes = 0;
05864    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05865    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05866 #endif
05867    ast_debug(1, "Restarting session...\n");
05868 
05869    bytes = 0;
05870    /* Load the session now */
05871    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05872       *useadsi = 1;
05873       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05874    } else
05875       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05876 
05877    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05878    return 0;
05879 }
05880 
05881 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05882 {
05883    int x;
05884    if (!ast_adsi_available(chan))
05885       return;
05886    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05887    if (x < 0)
05888       return;
05889    if (!x) {
05890       if (adsi_load_vmail(chan, useadsi)) {
05891          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05892          return;
05893       }
05894    } else
05895       *useadsi = 1;
05896 }
05897 
05898 static void adsi_login(struct ast_channel *chan)
05899 {
05900    unsigned char buf[256];
05901    int bytes=0;
05902    unsigned char keys[8];
05903    int x;
05904    if (!ast_adsi_available(chan))
05905       return;
05906 
05907    for (x=0;x<8;x++)
05908       keys[x] = 0;
05909    /* Set one key for next */
05910    keys[3] = ADSI_KEY_APPS + 3;
05911 
05912    bytes += adsi_logo(buf + bytes);
05913    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05914    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05915    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05916    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05917    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05918    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05919    bytes += ast_adsi_set_keys(buf + bytes, keys);
05920    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05921    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05922 }
05923 
05924 static void adsi_password(struct ast_channel *chan)
05925 {
05926    unsigned char buf[256];
05927    int bytes=0;
05928    unsigned char keys[8];
05929    int x;
05930    if (!ast_adsi_available(chan))
05931       return;
05932 
05933    for (x=0;x<8;x++)
05934       keys[x] = 0;
05935    /* Set one key for next */
05936    keys[3] = ADSI_KEY_APPS + 3;
05937 
05938    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05939    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05940    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05941    bytes += ast_adsi_set_keys(buf + bytes, keys);
05942    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05943    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05944 }
05945 
05946 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05947 {
05948    unsigned char buf[256];
05949    int bytes=0;
05950    unsigned char keys[8];
05951    int x,y;
05952 
05953    if (!ast_adsi_available(chan))
05954       return;
05955 
05956    for (x=0;x<5;x++) {
05957       y = ADSI_KEY_APPS + 12 + start + x;
05958       if (y > ADSI_KEY_APPS + 12 + 4)
05959          y = 0;
05960       keys[x] = ADSI_KEY_SKT | y;
05961    }
05962    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05963    keys[6] = 0;
05964    keys[7] = 0;
05965 
05966    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05967    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05968    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05969    bytes += ast_adsi_set_keys(buf + bytes, keys);
05970    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05971 
05972    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05973 }
05974 
05975 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05976 {
05977    int bytes=0;
05978    unsigned char buf[256]; 
05979    char buf1[256], buf2[256];
05980    char fn2[PATH_MAX];
05981 
05982    char cid[256]="";
05983    char *val;
05984    char *name, *num;
05985    char datetime[21]="";
05986    FILE *f;
05987 
05988    unsigned char keys[8];
05989 
05990    int x;
05991 
05992    if (!ast_adsi_available(chan))
05993       return;
05994 
05995    /* Retrieve important info */
05996    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05997    f = fopen(fn2, "r");
05998    if (f) {
05999       while (!feof(f)) {   
06000          if (!fgets((char *)buf, sizeof(buf), f)) {
06001             continue;
06002          }
06003          if (!feof(f)) {
06004             char *stringp=NULL;
06005             stringp = (char *)buf;
06006             strsep(&stringp, "=");
06007             val = strsep(&stringp, "=");
06008             if (!ast_strlen_zero(val)) {
06009                if (!strcmp((char *)buf, "callerid"))
06010                   ast_copy_string(cid, val, sizeof(cid));
06011                if (!strcmp((char *)buf, "origdate"))
06012                   ast_copy_string(datetime, val, sizeof(datetime));
06013             }
06014          }
06015       }
06016       fclose(f);
06017    }
06018    /* New meaning for keys */
06019    for (x=0;x<5;x++)
06020       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06021    keys[6] = 0x0;
06022    keys[7] = 0x0;
06023 
06024    if (!vms->curmsg) {
06025       /* No prev key, provide "Folder" instead */
06026       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06027    }
06028    if (vms->curmsg >= vms->lastmsg) {
06029       /* If last message ... */
06030       if (vms->curmsg) {
06031          /* but not only message, provide "Folder" instead */
06032          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06033          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06034 
06035       } else {
06036          /* Otherwise if only message, leave blank */
06037          keys[3] = 1;
06038       }
06039    }
06040 
06041    if (!ast_strlen_zero(cid)) {
06042       ast_callerid_parse(cid, &name, &num);
06043       if (!name)
06044          name = num;
06045    } else
06046       name = "Unknown Caller";
06047 
06048    /* If deleted, show "undeleted" */
06049 
06050    if (vms->deleted[vms->curmsg])
06051       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06052 
06053    /* Except "Exit" */
06054    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06055    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06056       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06057    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06058 
06059    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06060    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06061    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06062    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06063    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06064    bytes += ast_adsi_set_keys(buf + bytes, keys);
06065    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06066 
06067    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06068 }
06069 
06070 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06071 {
06072    int bytes=0;
06073    unsigned char buf[256];
06074    unsigned char keys[8];
06075 
06076    int x;
06077 
06078    if (!ast_adsi_available(chan))
06079       return;
06080 
06081    /* New meaning for keys */
06082    for (x=0;x<5;x++)
06083       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06084 
06085    keys[6] = 0x0;
06086    keys[7] = 0x0;
06087 
06088    if (!vms->curmsg) {
06089       /* No prev key, provide "Folder" instead */
06090       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06091    }
06092    if (vms->curmsg >= vms->lastmsg) {
06093       /* If last message ... */
06094       if (vms->curmsg) {
06095          /* but not only message, provide "Folder" instead */
06096          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06097       } else {
06098          /* Otherwise if only message, leave blank */
06099          keys[3] = 1;
06100       }
06101    }
06102 
06103    /* If deleted, show "undeleted" */
06104    if (vms->deleted[vms->curmsg]) 
06105       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06106 
06107    /* Except "Exit" */
06108    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06109    bytes += ast_adsi_set_keys(buf + bytes, keys);
06110    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06111 
06112    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06113 }
06114 
06115 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06116 {
06117    unsigned char buf[256] = "";
06118    char buf1[256] = "", buf2[256] = "";
06119    int bytes=0;
06120    unsigned char keys[8];
06121    int x;
06122 
06123    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06124    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06125    if (!ast_adsi_available(chan))
06126       return;
06127    if (vms->newmessages) {
06128       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06129       if (vms->oldmessages) {
06130          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06131          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06132       } else {
06133          snprintf(buf2, sizeof(buf2), "%s.", newm);
06134       }
06135    } else if (vms->oldmessages) {
06136       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06137       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06138    } else {
06139       strcpy(buf1, "You have no messages.");
06140       buf2[0] = ' ';
06141       buf2[1] = '\0';
06142    }
06143    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06144    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06145    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06146 
06147    for (x=0;x<6;x++)
06148       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06149    keys[6] = 0;
06150    keys[7] = 0;
06151 
06152    /* Don't let them listen if there are none */
06153    if (vms->lastmsg < 0)
06154       keys[0] = 1;
06155    bytes += ast_adsi_set_keys(buf + bytes, keys);
06156 
06157    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06158 
06159    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06160 }
06161 
06162 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06163 {
06164    unsigned char buf[256] = "";
06165    char buf1[256] = "", buf2[256] = "";
06166    int bytes=0;
06167    unsigned char keys[8];
06168    int x;
06169 
06170    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06171 
06172    if (!ast_adsi_available(chan))
06173       return;
06174 
06175    /* Original command keys */
06176    for (x=0;x<6;x++)
06177       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06178 
06179    keys[6] = 0;
06180    keys[7] = 0;
06181 
06182    if ((vms->lastmsg + 1) < 1)
06183       keys[0] = 0;
06184 
06185    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06186       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06187 
06188    if (vms->lastmsg + 1)
06189       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06190    else
06191       strcpy(buf2, "no messages.");
06192    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06193    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06194    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06195    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06196    bytes += ast_adsi_set_keys(buf + bytes, keys);
06197 
06198    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06199 
06200    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06201    
06202 }
06203 
06204 /*
06205 static void adsi_clear(struct ast_channel *chan)
06206 {
06207    char buf[256];
06208    int bytes=0;
06209    if (!ast_adsi_available(chan))
06210       return;
06211    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06212    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06213 
06214    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06215 }
06216 */
06217 
06218 static void adsi_goodbye(struct ast_channel *chan)
06219 {
06220    unsigned char buf[256];
06221    int bytes=0;
06222 
06223    if (!ast_adsi_available(chan))
06224       return;
06225    bytes += adsi_logo(buf + bytes);
06226    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06227    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06228    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06229    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06230 
06231    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06232 }
06233 
06234 /*!\brief get_folder: Folder menu
06235  * Plays "press 1 for INBOX messages" etc.
06236  * Should possibly be internationalized
06237  */
06238 static int get_folder(struct ast_channel *chan, int start)
06239 {
06240    int x;
06241    int d;
06242    char fn[PATH_MAX];
06243    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06244    if (d)
06245       return d;
06246    for (x = start; x< 5; x++) {  /* For all folders */
06247       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06248          return d;
06249       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06250       if (d)
06251          return d;
06252       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06253       d = vm_play_folder_name(chan, fn);
06254       if (d)
06255          return d;
06256       d = ast_waitfordigit(chan, 500);
06257       if (d)
06258          return d;
06259    }
06260    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06261    if (d)
06262       return d;
06263    d = ast_waitfordigit(chan, 4000);
06264    return d;
06265 }
06266 
06267 /*!
06268  * \brief plays a prompt and waits for a keypress.
06269  * \param chan
06270  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06271  * \param start Does not appear to be used at this time.
06272  *
06273  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06274  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06275  * prompting for the number inputs that correspond to the available folders.
06276  * 
06277  * \return zero on success, or -1 on error.
06278  */
06279 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06280 {
06281    int res = 0;
06282    res = ast_play_and_wait(chan, fn);  /* Folder name */
06283    while (((res < '0') || (res > '9')) &&
06284          (res != '#') && (res >= 0)) {
06285       res = get_folder(chan, 0);
06286    }
06287    return res;
06288 }
06289 
06290 /*!
06291  * \brief presents the option to prepend to an existing message when forwarding it.
06292  * \param chan
06293  * \param vmu
06294  * \param curdir
06295  * \param curmsg
06296  * \param vmfmts
06297  * \param context
06298  * \param record_gain
06299  * \param duration
06300  * \param vms
06301  *
06302  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06303  *
06304  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06305  * \return zero on success, -1 on error.
06306  */
06307 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06308          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06309 {
06310 #ifdef IMAP_STORAGE
06311    int res;
06312 #endif
06313    int cmd = 0;
06314    int retries = 0, prepend_duration = 0, already_recorded = 0;
06315    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06316    char textfile[PATH_MAX];
06317    struct ast_config *msg_cfg;
06318    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06319 #ifndef IMAP_STORAGE
06320    signed char zero_gain = 0;
06321 #endif
06322    const char *duration_str;
06323 
06324    /* Must always populate duration correctly */
06325    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06326    strcpy(textfile, msgfile);
06327    strcpy(backup, msgfile);
06328    strcpy(backup_textfile, msgfile);
06329    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06330    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06331    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06332 
06333    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06334       *duration = atoi(duration_str);
06335    } else {
06336       *duration = 0;
06337    }
06338 
06339    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06340       if (cmd)
06341          retries = 0;
06342       switch (cmd) {
06343       case '1': 
06344 
06345 #ifdef IMAP_STORAGE
06346          /* Record new intro file */
06347          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06348          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06349          res = ast_play_and_wait(chan, INTRO);
06350          res = ast_play_and_wait(chan, "beep");
06351          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06352          cmd = 't';
06353 #else
06354 
06355          /* prepend a message to the current message, update the metadata and return */
06356 
06357          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06358          strcpy(textfile, msgfile);
06359          strncat(textfile, ".txt", sizeof(textfile) - 1);
06360          *duration = 0;
06361 
06362          /* if we can't read the message metadata, stop now */
06363          if (!msg_cfg) {
06364             cmd = 0;
06365             break;
06366          }
06367          
06368          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06369          if (already_recorded) {
06370             ast_filecopy(backup, msgfile, NULL);
06371             copy(backup_textfile, textfile);
06372          }
06373          else {
06374             ast_filecopy(msgfile, backup, NULL);
06375             copy(textfile,backup_textfile);
06376          }
06377          already_recorded = 1;
06378 
06379          if (record_gain)
06380             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06381 
06382          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06383          if (record_gain)
06384             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06385 
06386          
06387          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06388             *duration = atoi(duration_str);
06389 
06390          if (prepend_duration) {
06391             struct ast_category *msg_cat;
06392             /* need enough space for a maximum-length message duration */
06393             char duration_buf[12];
06394 
06395             *duration += prepend_duration;
06396             msg_cat = ast_category_get(msg_cfg, "message");
06397             snprintf(duration_buf, 11, "%ld", *duration);
06398             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06399                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06400             }
06401          }
06402 
06403 #endif
06404          break;
06405       case '2': 
06406          /* NULL out introfile so we know there is no intro! */
06407 #ifdef IMAP_STORAGE
06408          *vms->introfn = '\0';
06409 #endif
06410          cmd = 't';
06411          break;
06412       case '*':
06413          cmd = '*';
06414          break;
06415       default: 
06416          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06417             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06418          if (!cmd)
06419             cmd = ast_play_and_wait(chan,"vm-starmain");
06420             /* "press star to return to the main menu" */
06421          if (!cmd)
06422             cmd = ast_waitfordigit(chan,6000);
06423          if (!cmd)
06424             retries++;
06425          if (retries > 3)
06426             cmd = 't';
06427       }
06428    }
06429 
06430    if (msg_cfg)
06431       ast_config_destroy(msg_cfg);
06432    if (prepend_duration)
06433       *duration = prepend_duration;
06434 
06435    if (already_recorded && cmd == -1) {
06436       /* restore original message if prepention cancelled */
06437       ast_filerename(backup, msgfile, NULL);
06438       rename(backup_textfile, textfile);
06439    }
06440 
06441    if (cmd == 't' || cmd == 'S')
06442       cmd = 0;
06443    return cmd;
06444 }
06445 
06446 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06447 {
06448    struct ast_event *event;
06449    char *mailbox, *context;
06450 
06451    /* Strip off @default */
06452    context = mailbox = ast_strdupa(box);
06453    strsep(&context, "@");
06454    if (ast_strlen_zero(context))
06455       context = "default";
06456 
06457    if (!(event = ast_event_new(AST_EVENT_MWI,
06458          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06459          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06460          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06461          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06462          AST_EVENT_IE_END))) {
06463       return;
06464    }
06465 
06466    ast_event_queue_and_cache(event);
06467 }
06468 
06469 /*!
06470  * \brief Sends email notification that a user has a new voicemail waiting for them.
06471  * \param chan
06472  * \param vmu
06473  * \param vms
06474  * \param msgnum
06475  * \param duration
06476  * \param fmt
06477  * \param cidnum The Caller ID phone number value.
06478  * \param cidname The Caller ID name value.
06479  *
06480  * \return zero on success, -1 on error.
06481  */
06482 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)
06483 {
06484    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06485    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06486    const char *category;
06487    char *myserveremail = serveremail;
06488 
06489    ast_channel_lock(chan);
06490    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06491       category = ast_strdupa(category);
06492    }
06493    ast_channel_unlock(chan);
06494 
06495    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06496    make_file(fn, sizeof(fn), todir, msgnum);
06497    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06498 
06499    if (!ast_strlen_zero(vmu->attachfmt)) {
06500       if (strstr(fmt, vmu->attachfmt))
06501          fmt = vmu->attachfmt;
06502       else
06503          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);
06504    }
06505 
06506    /* Attach only the first format */
06507    fmt = ast_strdupa(fmt);
06508    stringp = fmt;
06509    strsep(&stringp, "|");
06510 
06511    if (!ast_strlen_zero(vmu->serveremail))
06512       myserveremail = vmu->serveremail;
06513 
06514    if (!ast_strlen_zero(vmu->email)) {
06515       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06516       if (!attach_user_voicemail)
06517          attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
06518 
06519       if (attach_user_voicemail)
06520          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06521 
06522       /* XXX possible imap issue, should category be NULL XXX */
06523       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06524 
06525       if (attach_user_voicemail)
06526          DISPOSE(todir, msgnum);
06527    }
06528 
06529    if (!ast_strlen_zero(vmu->pager)) {
06530       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06531    }
06532 
06533    if (ast_test_flag(vmu, VM_DELETE))
06534       DELETE(todir, msgnum, fn, vmu);
06535 
06536    /* Leave voicemail for someone */
06537    if (ast_app_has_voicemail(ext_context, NULL)) 
06538       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06539 
06540    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06541 
06542    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);
06543    run_externnotify(vmu->context, vmu->mailbox, flag);
06544 
06545 #ifdef IMAP_STORAGE
06546    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06547    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06548       vm_imap_delete(NULL, vms->curmsg, vmu);
06549       vms->newmessages--;  /* Fix new message count */
06550    }
06551 #endif
06552 
06553    return 0;
06554 }
06555 
06556 /*!
06557  * \brief Sends a voicemail message to a mailbox recipient.
06558  * \param ast_channel
06559  * \param context
06560  * \param vms
06561  * \param sender
06562  * \param fmt
06563  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06564  *             Will be 0 when called to forward an existing message (option 8)
06565  *             Will be 1 when called to leave a message (option 3->5)
06566  * \param record_gain 
06567  *
06568  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06569  * 
06570  * When in the leave message mode (is_new_message == 1):
06571  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06572  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06573  *
06574  * When in the forward message mode (is_new_message == 0):
06575  *   - retreives the current message to be forwarded
06576  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06577  *   - determines the target mailbox and folders
06578  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06579  *
06580  * \return zero on success, -1 on error.
06581  */
06582 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)
06583 {
06584 #ifdef IMAP_STORAGE
06585    int todircount=0;
06586    struct vm_state *dstvms;
06587 #endif
06588    char username[70]="";
06589    char fn[PATH_MAX]; /* for playback of name greeting */
06590    char ecodes[16] = "#";
06591    int res = 0, cmd = 0;
06592    struct ast_vm_user *receiver = NULL, *vmtmp;
06593    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06594    char *stringp;
06595    const char *s;
06596    int saved_messages = 0, found = 0;
06597    int valid_extensions = 0;
06598    char *dir;
06599    int curmsg;
06600    char urgent_str[7] = "";
06601    char tmptxtfile[PATH_MAX];
06602 #ifndef IMAP_STORAGE
06603    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06604 #endif
06605    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06606       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06607    }
06608 
06609    if (vms == NULL) return -1;
06610    dir = vms->curdir;
06611    curmsg = vms->curmsg;
06612 
06613    tmptxtfile[0] = '\0';
06614    while (!res && !valid_extensions) {
06615       int use_directory = 0;
06616       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06617          int done = 0;
06618          int retries = 0;
06619          cmd=0;
06620          while ((cmd >= 0) && !done ){
06621             if (cmd)
06622                retries = 0;
06623             switch (cmd) {
06624             case '1': 
06625                use_directory = 0;
06626                done = 1;
06627                break;
06628             case '2': 
06629                use_directory = 1;
06630                done=1;
06631                break;
06632             case '*': 
06633                cmd = 't';
06634                done = 1;
06635                break;
06636             default: 
06637                /* Press 1 to enter an extension press 2 to use the directory */
06638                cmd = ast_play_and_wait(chan,"vm-forward");
06639                if (!cmd)
06640                   cmd = ast_waitfordigit(chan,3000);
06641                if (!cmd)
06642                   retries++;
06643                if (retries > 3) {
06644                   cmd = 't';
06645                   done = 1;
06646                }
06647                
06648             }
06649          }
06650          if (cmd < 0 || cmd == 't')
06651             break;
06652       }
06653       
06654       if (use_directory) {
06655          /* use app_directory */
06656          
06657          char old_context[sizeof(chan->context)];
06658          char old_exten[sizeof(chan->exten)];
06659          int old_priority;
06660          struct ast_app* directory_app;
06661 
06662          directory_app = pbx_findapp("Directory");
06663          if (directory_app) {
06664             char vmcontext[256];
06665             /* make backup copies */
06666             memcpy(old_context, chan->context, sizeof(chan->context));
06667             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06668             old_priority = chan->priority;
06669             
06670             /* call the the Directory, changes the channel */
06671             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06672             res = pbx_exec(chan, directory_app, vmcontext);
06673             
06674             ast_copy_string(username, chan->exten, sizeof(username));
06675             
06676             /* restore the old context, exten, and priority */
06677             memcpy(chan->context, old_context, sizeof(chan->context));
06678             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06679             chan->priority = old_priority;
06680          } else {
06681             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06682             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06683          }
06684       } else {
06685          /* Ask for an extension */
06686          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06687          if (res)
06688             break;
06689          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06690             break;
06691       }
06692       
06693       /* start all over if no username */
06694       if (ast_strlen_zero(username))
06695          continue;
06696       stringp = username;
06697       s = strsep(&stringp, "*");
06698       /* start optimistic */
06699       valid_extensions = 1;
06700       while (s) {
06701          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06702             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06703             found++;
06704          } else {
06705             /* XXX Optimization for the future.  When we encounter a single bad extension,
06706              * bailing out on all of the extensions may not be the way to go.  We should
06707              * probably just bail on that single extension, then allow the user to enter
06708              * several more. XXX
06709              */
06710             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06711                free_user(receiver);
06712             }
06713             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06714             valid_extensions = 0;
06715             break;
06716          }
06717 
06718          /* play name if available, else play extension number */
06719          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06720          RETRIEVE(fn, -1, s, receiver->context);
06721          if (ast_fileexists(fn, NULL, NULL) > 0) {
06722             res = ast_stream_and_wait(chan, fn, ecodes);
06723             if (res) {
06724                DISPOSE(fn, -1);
06725                return res;
06726             }
06727          } else {
06728             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06729          }
06730          DISPOSE(fn, -1);
06731 
06732          s = strsep(&stringp, "*");
06733       }
06734       /* break from the loop of reading the extensions */
06735       if (valid_extensions)
06736          break;
06737       /* "I am sorry, that's not a valid extension.  Please try again." */
06738       res = ast_play_and_wait(chan, "pbx-invalid");
06739    }
06740    /* check if we're clear to proceed */
06741    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06742       return res;
06743    if (is_new_message == 1) {
06744       struct leave_vm_options leave_options;
06745       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06746       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06747 
06748       /* Send VoiceMail */
06749       memset(&leave_options, 0, sizeof(leave_options));
06750       leave_options.record_gain = record_gain;
06751       cmd = leave_voicemail(chan, mailbox, &leave_options);
06752    } else {
06753       /* Forward VoiceMail */
06754       long duration = 0;
06755       struct vm_state vmstmp;
06756       memcpy(&vmstmp, vms, sizeof(vmstmp));
06757 
06758       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06759 
06760       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06761       if (!cmd) {
06762          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06763 #ifdef IMAP_STORAGE
06764             int attach_user_voicemail;
06765             char *myserveremail = serveremail;
06766             
06767             /* get destination mailbox */
06768             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06769             if (!dstvms) {
06770                dstvms = create_vm_state_from_user(vmtmp);
06771             }
06772             if (dstvms) {
06773                init_mailstream(dstvms, 0);
06774                if (!dstvms->mailstream) {
06775                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06776                } else {
06777                   STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06778                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06779                }
06780             } else {
06781                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06782             }
06783             if (!ast_strlen_zero(vmtmp->serveremail))
06784                myserveremail = vmtmp->serveremail;
06785             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06786             /* NULL category for IMAP storage */
06787             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);
06788 #else
06789             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06790 #endif
06791             saved_messages++;
06792             AST_LIST_REMOVE_CURRENT(list);
06793             free_user(vmtmp);
06794             if (res)
06795                break;
06796          }
06797          AST_LIST_TRAVERSE_SAFE_END;
06798          if (saved_messages > 0) {
06799             /* give confirmation that the message was saved */
06800             /* commented out since we can't forward batches yet
06801             if (saved_messages == 1)
06802                res = ast_play_and_wait(chan, "vm-message");
06803             else
06804                res = ast_play_and_wait(chan, "vm-messages");
06805             if (!res)
06806                res = ast_play_and_wait(chan, "vm-saved"); */
06807 #ifdef IMAP_STORAGE
06808             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06809             if (ast_strlen_zero(vmstmp.introfn))
06810 #endif
06811             res = ast_play_and_wait(chan, "vm-msgsaved");
06812          }  
06813 #ifndef IMAP_STORAGE
06814          /* Restore original message without prepended message if backup exists */
06815          make_file(msgfile, sizeof(msgfile), dir, curmsg);
06816          strcpy(textfile, msgfile);
06817          strcpy(backup, msgfile);
06818          strcpy(backup_textfile, msgfile);
06819          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06820          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06821          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06822          if (ast_fileexists(backup, NULL, NULL) > 0) {
06823             ast_filerename(backup, msgfile, NULL);
06824             rename(backup_textfile, textfile);
06825          }
06826 #endif
06827       }
06828       DISPOSE(dir, curmsg);
06829    }
06830 
06831    /* If anything failed above, we still have this list to free */
06832    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06833       free_user(vmtmp);
06834    return res ? res : cmd;
06835 }
06836 
06837 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06838 {
06839    int res;
06840    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06841       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06842    return res;
06843 }
06844 
06845 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06846 {
06847    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);
06848 }
06849 
06850 static int play_message_category(struct ast_channel *chan, const char *category)
06851 {
06852    int res = 0;
06853 
06854    if (!ast_strlen_zero(category))
06855       res = ast_play_and_wait(chan, category);
06856 
06857    if (res) {
06858       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06859       res = 0;
06860    }
06861 
06862    return res;
06863 }
06864 
06865 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06866 {
06867    int res = 0;
06868    struct vm_zone *the_zone = NULL;
06869    time_t t;
06870 
06871    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06872       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06873       return 0;
06874    }
06875 
06876    /* Does this user have a timezone specified? */
06877    if (!ast_strlen_zero(vmu->zonetag)) {
06878       /* Find the zone in the list */
06879       struct vm_zone *z;
06880       AST_LIST_LOCK(&zones);
06881       AST_LIST_TRAVERSE(&zones, z, list) {
06882          if (!strcmp(z->name, vmu->zonetag)) {
06883             the_zone = z;
06884             break;
06885          }
06886       }
06887       AST_LIST_UNLOCK(&zones);
06888    }
06889 
06890 /* No internal variable parsing for now, so we'll comment it out for the time being */
06891 #if 0
06892    /* Set the DIFF_* variables */
06893    ast_localtime(&t, &time_now, NULL);
06894    tv_now = ast_tvnow();
06895    ast_localtime(&tv_now, &time_then, NULL);
06896 
06897    /* Day difference */
06898    if (time_now.tm_year == time_then.tm_year)
06899       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06900    else
06901       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06902    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06903 
06904    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06905 #endif
06906    if (the_zone) {
06907       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06908    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
06909       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06910    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
06911       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06912    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
06913       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);
06914    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
06915       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06916    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
06917       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06918    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
06919       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06920    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
06921       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);
06922    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
06923       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06924    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
06925       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
06926    } else {
06927       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06928    }
06929 #if 0
06930    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06931 #endif
06932    return res;
06933 }
06934 
06935 
06936 
06937 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06938 {
06939    int res = 0;
06940    int i;
06941    char *callerid, *name;
06942    char prefile[PATH_MAX] = "";
06943    
06944 
06945    /* If voicemail cid is not enabled, or we didn't get cid or context from
06946     * the attribute file, leave now.
06947     *
06948     * TODO Still need to change this so that if this function is called by the
06949     * message envelope (and someone is explicitly requesting to hear the CID),
06950     * it does not check to see if CID is enabled in the config file.
06951     */
06952    if ((cid == NULL)||(context == NULL))
06953       return res;
06954 
06955    /* Strip off caller ID number from name */
06956    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
06957    ast_callerid_parse(cid, &name, &callerid);
06958    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
06959       /* Check for internal contexts and only */
06960       /* say extension when the call didn't come from an internal context in the list */
06961       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
06962          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
06963          if ((strcmp(cidinternalcontexts[i], context) == 0))
06964             break;
06965       }
06966       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
06967          if (!res) {
06968             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
06969             if (!ast_strlen_zero(prefile)) {
06970             /* See if we can find a recorded name for this person instead of their extension number */
06971                if (ast_fileexists(prefile, NULL, NULL) > 0) {
06972                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
06973                   if (!callback)
06974                      res = wait_file2(chan, vms, "vm-from");
06975                   res = ast_stream_and_wait(chan, prefile, "");
06976                } else {
06977                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
06978                   /* Say "from extension" as one saying to sound smoother */
06979                   if (!callback)
06980                      res = wait_file2(chan, vms, "vm-from-extension");
06981                   res = ast_say_digit_str(chan, callerid, "", chan->language);
06982                }
06983             }
06984          }
06985       } else if (!res) {
06986          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
06987          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
06988          if (!callback)
06989             res = wait_file2(chan, vms, "vm-from-phonenumber");
06990          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
06991       }
06992    } else {
06993       /* Number unknown */
06994       ast_debug(1, "VM-CID: From an unknown number\n");
06995       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
06996       res = wait_file2(chan, vms, "vm-unknown-caller");
06997    }
06998    return res;
06999 }
07000 
07001 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07002 {
07003    int res = 0;
07004    int durationm;
07005    int durations;
07006    /* Verify that we have a duration for the message */
07007    if (duration == NULL)
07008       return res;
07009 
07010    /* Convert from seconds to minutes */
07011    durations=atoi(duration);
07012    durationm=(durations / 60);
07013 
07014    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07015 
07016    if ((!res) && (durationm >= minduration)) {
07017       res = wait_file2(chan, vms, "vm-duration");
07018 
07019       /* POLISH syntax */
07020       if (!strncasecmp(chan->language, "pl", 2)) {
07021          div_t num = div(durationm, 10);
07022 
07023          if (durationm == 1) {
07024             res = ast_play_and_wait(chan, "digits/1z");
07025             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07026          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07027             if (num.rem == 2) {
07028                if (!num.quot) {
07029                   res = ast_play_and_wait(chan, "digits/2-ie");
07030                } else {
07031                   res = say_and_wait(chan, durationm - 2 , chan->language);
07032                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07033                }
07034             } else {
07035                res = say_and_wait(chan, durationm, chan->language);
07036             }
07037             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07038          } else {
07039             res = say_and_wait(chan, durationm, chan->language);
07040             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07041          }
07042       /* DEFAULT syntax */
07043       } else {
07044          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07045          res = wait_file2(chan, vms, "vm-minutes");
07046       }
07047    }
07048    return res;
07049 }
07050 
07051 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07052 {
07053    int res = 0;
07054    char filename[256], *cid;
07055    const char *origtime, *context, *category, *duration, *flag;
07056    struct ast_config *msg_cfg;
07057    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07058 
07059    vms->starting = 0;
07060    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07061    adsi_message(chan, vms);
07062    if (!vms->curmsg)
07063       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07064    else if (vms->curmsg == vms->lastmsg)
07065       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07066 
07067    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07068    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07069    msg_cfg = ast_config_load(filename, config_flags);
07070    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07071       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07072       return 0;
07073    }
07074    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07075 
07076    /* Play the word urgent if we are listening to urgent messages */
07077    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07078       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07079    }
07080 
07081    if (!res) {
07082       /* POLISH syntax */
07083       if (!strncasecmp(chan->language, "pl", 2)) {
07084          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07085             int ten, one;
07086             char nextmsg[256];
07087             ten = (vms->curmsg + 1) / 10;
07088             one = (vms->curmsg + 1) % 10;
07089 
07090             if (vms->curmsg < 20) {
07091                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07092                res = wait_file2(chan, vms, nextmsg);
07093             } else {
07094                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07095                res = wait_file2(chan, vms, nextmsg);
07096                if (one > 0) {
07097                   if (!res) {
07098                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07099                      res = wait_file2(chan, vms, nextmsg);
07100                   }
07101                }
07102             }
07103          }
07104          if (!res)
07105             res = wait_file2(chan, vms, "vm-message");
07106       /* HEBREW syntax */
07107       } else if (!strncasecmp(chan->language, "he", 2)) {
07108          if (!vms->curmsg) {
07109             res = wait_file2(chan, vms, "vm-message");
07110             res = wait_file2(chan, vms, "vm-first");
07111          } else if (vms->curmsg == vms->lastmsg) {
07112             res = wait_file2(chan, vms, "vm-message");
07113             res = wait_file2(chan, vms, "vm-last");
07114          } else {
07115             res = wait_file2(chan, vms, "vm-message");
07116             res = wait_file2(chan, vms, "vm-number");
07117             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07118          }
07119       } else {
07120          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07121             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07122          } else { /* DEFAULT syntax */
07123             res = wait_file2(chan, vms, "vm-message");
07124          }
07125          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07126             if (!res) {
07127                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07128             }
07129          }
07130       }
07131    }
07132 
07133    if (!msg_cfg) {
07134       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07135       return 0;
07136    }
07137 
07138    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07139       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07140       DISPOSE(vms->curdir, vms->curmsg);
07141       ast_config_destroy(msg_cfg);
07142       return 0;
07143    }
07144 
07145    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07146    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07147    category = ast_variable_retrieve(msg_cfg, "message", "category");
07148 
07149    context = ast_variable_retrieve(msg_cfg, "message", "context");
07150    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07151       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07152    if (!res) {
07153       res = play_message_category(chan, category);
07154    }
07155    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07156       res = play_message_datetime(chan, vmu, origtime, filename);
07157    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07158       res = play_message_callerid(chan, vms, cid, context, 0);
07159    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07160       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07161    /* Allow pressing '1' to skip envelope / callerid */
07162    if (res == '1')
07163       res = 0;
07164    ast_config_destroy(msg_cfg);
07165 
07166    if (!res) {
07167       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07168       vms->heard[vms->curmsg] = 1;
07169 #ifdef IMAP_STORAGE
07170       /*IMAP storage stores any prepended message from a forward
07171        * as a separate file from the rest of the message
07172        */
07173       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07174          wait_file(chan, vms, vms->introfn);
07175       }
07176 #endif
07177       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07178          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07179          res = 0;
07180       }
07181    }
07182    DISPOSE(vms->curdir, vms->curmsg);
07183    return res;
07184 }
07185 
07186 #ifdef IMAP_STORAGE
07187 static int imap_remove_file(char *dir, int msgnum)
07188 {
07189    char fn[PATH_MAX];
07190    char full_fn[PATH_MAX];
07191    char intro[PATH_MAX] = {0,};
07192    
07193    if (msgnum > -1) {
07194       make_file(fn, sizeof(fn), dir, msgnum);
07195       snprintf(intro, sizeof(intro), "%sintro", fn);
07196    } else
07197       ast_copy_string(fn, dir, sizeof(fn));
07198    
07199    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07200       ast_filedelete(fn, NULL);
07201       if (!ast_strlen_zero(intro)) {
07202          ast_filedelete(intro, NULL);
07203       }
07204       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07205       unlink(full_fn);
07206    }
07207    return 0;
07208 }
07209 
07210 
07211 
07212 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07213 {
07214    char *file, *filename;
07215    char *attachment;
07216    char arg[10];
07217    int i;
07218    BODY* body;
07219 
07220    
07221    file = strrchr(ast_strdupa(dir), '/');
07222    if (file)
07223       *file++ = '\0';
07224    else {
07225       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07226       return -1;
07227    }
07228 
07229    ast_mutex_lock(&vms->lock);
07230    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07231       mail_fetchstructure(vms->mailstream, i + 1, &body);
07232       /* We have the body, now we extract the file name of the first attachment. */
07233       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07234          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07235       } else {
07236          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07237          ast_mutex_unlock(&vms->lock);
07238          return -1;
07239       }
07240       filename = strsep(&attachment, ".");
07241       if (!strcmp(filename, file)) {
07242          sprintf (arg,"%d", i+1);
07243          mail_setflag (vms->mailstream,arg,"\\DELETED");
07244       }
07245    }
07246    mail_expunge(vms->mailstream);
07247    ast_mutex_unlock(&vms->lock);
07248    return 0;
07249 }
07250 
07251 #else
07252 #ifndef IMAP_STORAGE
07253 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07254 {
07255    int count_msg, last_msg;
07256 
07257    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07258    
07259    /* Rename the member vmbox HERE so that we don't try to return before
07260     * we know what's going on.
07261     */
07262    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07263    
07264    /* Faster to make the directory than to check if it exists. */
07265    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07266 
07267    count_msg = count_messages(vmu, vms->curdir);
07268    if (count_msg < 0)
07269       return count_msg;
07270    else
07271       vms->lastmsg = count_msg - 1;
07272 
07273    /*
07274    The following test is needed in case sequencing gets messed up.
07275    There appears to be more than one way to mess up sequence, so
07276    we will not try to find all of the root causes--just fix it when
07277    detected.
07278    */
07279 
07280    if (vm_lock_path(vms->curdir)) {
07281       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07282       return -1;
07283    }
07284 
07285    last_msg = last_message_index(vmu, vms->curdir);
07286    ast_unlock_path(vms->curdir);
07287 
07288    if (last_msg < 0) 
07289       return last_msg;
07290 
07291    return 0;
07292 }
07293 #endif
07294 #endif
07295 
07296 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07297 {
07298    int x = 0;
07299 #ifndef IMAP_STORAGE
07300    int res = 0, nummsg;
07301    char fn2[PATH_MAX];
07302 #endif
07303 
07304    if (vms->lastmsg <= -1)
07305       goto done;
07306 
07307    vms->curmsg = -1; 
07308 #ifndef IMAP_STORAGE
07309    /* Get the deleted messages fixed */ 
07310    if (vm_lock_path(vms->curdir))
07311       return ERROR_LOCK_PATH;
07312 
07313    for (x = 0; x < vmu->maxmsg; x++) { 
07314       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
07315          /* Save this message.  It's not in INBOX or hasn't been heard */ 
07316          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
07317          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
07318             break;
07319          vms->curmsg++; 
07320          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
07321          if (strcmp(vms->fn, fn2)) { 
07322             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07323          } 
07324       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07325          /* Move to old folder before deleting */ 
07326          res = save_to_folder(vmu, vms, x, 1);
07327          if (res == ERROR_LOCK_PATH) {
07328             /* If save failed do not delete the message */
07329             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07330             vms->deleted[x] = 0;
07331             vms->heard[x] = 0;
07332             --x;
07333          }
07334       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07335          /* Move to deleted folder */ 
07336          res = save_to_folder(vmu, vms, x, 10);
07337          if (res == ERROR_LOCK_PATH) {
07338             /* If save failed do not delete the message */
07339             vms->deleted[x] = 0;
07340             vms->heard[x] = 0;
07341             --x;
07342          }
07343       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07344          /* If realtime storage enabled - we should explicitly delete this message,
07345          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07346          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07347          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07348             DELETE(vms->curdir, x, vms->fn, vmu);
07349       }
07350    } 
07351 
07352    /* Delete ALL remaining messages */
07353    nummsg = x - 1;
07354    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07355       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07356       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07357          DELETE(vms->curdir, x, vms->fn, vmu);
07358    }
07359    ast_unlock_path(vms->curdir);
07360 #else
07361    if (vms->deleted) {
07362       for (x=0;x < vmu->maxmsg;x++) { 
07363          if (vms->deleted[x]) { 
07364             ast_debug(3,"IMAP delete of %d\n",x);
07365             DELETE(vms->curdir, x, vms->fn, vmu);
07366          }
07367       }
07368    }
07369 #endif
07370 
07371 done:
07372    if (vms->deleted)
07373       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07374    if (vms->heard)
07375       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07376 
07377    return 0;
07378 }
07379 
07380 /* In Greek even though we CAN use a syntax like "friends messages"
07381  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07382  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07383  * syntax for the above three categories which is more elegant. 
07384  */
07385 
07386 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07387 {
07388    int cmd;
07389    char *buf;
07390 
07391    buf = alloca(strlen(box)+2); 
07392    strcpy(buf, box);
07393    strcat(buf,"s");
07394 
07395    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07396       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07397       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07398    } else {
07399       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07400       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07401    }
07402 }
07403 
07404 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07405 {
07406    int cmd;
07407 
07408    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07409       if (!strcasecmp(box, "vm-INBOX"))
07410          cmd = ast_play_and_wait(chan, "vm-new-e");
07411       else
07412          cmd = ast_play_and_wait(chan, "vm-old-e");
07413       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07414    } else {
07415       cmd = ast_play_and_wait(chan, "vm-messages");
07416       return cmd ? cmd : ast_play_and_wait(chan, box);
07417    }
07418 }
07419 
07420 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07421 {
07422    int cmd;
07423 
07424    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07425       cmd = ast_play_and_wait(chan, "vm-messages");
07426       return cmd ? cmd : ast_play_and_wait(chan, box);
07427    } else {
07428       cmd = ast_play_and_wait(chan, box);
07429       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07430    }
07431 }
07432 
07433 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07434 {
07435    int cmd;
07436 
07437    if (  !strncasecmp(chan->language, "it", 2) ||
07438         !strncasecmp(chan->language, "es", 2) ||
07439         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07440       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07441       return cmd ? cmd : ast_play_and_wait(chan, box);
07442    } else if (!strncasecmp(chan->language, "gr", 2)) {
07443       return vm_play_folder_name_gr(chan, box);
07444    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07445       return ast_play_and_wait(chan, box);
07446    } else if (!strncasecmp(chan->language, "pl", 2)) {
07447       return vm_play_folder_name_pl(chan, box);
07448    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07449       return vm_play_folder_name_ua(chan, box);
07450    } else {  /* Default English */
07451       cmd = ast_play_and_wait(chan, box);
07452       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07453    }
07454 }
07455 
07456 /* GREEK SYNTAX
07457    In greek the plural for old/new is
07458    different so we need the following files
07459    We also need vm-denExeteMynhmata because
07460    this syntax is different.
07461 
07462    -> vm-Olds.wav : "Palia"
07463    -> vm-INBOXs.wav : "Nea"
07464    -> vm-denExeteMynhmata : "den exete mynhmata"
07465 */
07466 
07467 
07468 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07469 {
07470    int res = 0;
07471 
07472    if (vms->newmessages) {
07473       res = ast_play_and_wait(chan, "vm-youhave");
07474       if (!res) 
07475          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07476       if (!res) {
07477          if ((vms->newmessages == 1)) {
07478             res = ast_play_and_wait(chan, "vm-INBOX");
07479             if (!res)
07480                res = ast_play_and_wait(chan, "vm-message");
07481          } else {
07482             res = ast_play_and_wait(chan, "vm-INBOXs");
07483             if (!res)
07484                res = ast_play_and_wait(chan, "vm-messages");
07485          }
07486       }
07487    } else if (vms->oldmessages){
07488       res = ast_play_and_wait(chan, "vm-youhave");
07489       if (!res)
07490          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07491       if ((vms->oldmessages == 1)){
07492          res = ast_play_and_wait(chan, "vm-Old");
07493          if (!res)
07494             res = ast_play_and_wait(chan, "vm-message");
07495       } else {
07496          res = ast_play_and_wait(chan, "vm-Olds");
07497          if (!res)
07498             res = ast_play_and_wait(chan, "vm-messages");
07499       }
07500    } else if (!vms->oldmessages && !vms->newmessages) 
07501       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07502    return res;
07503 }
07504 
07505 /* Version of vm_intro() designed to work for many languages.
07506  *
07507  * It is hoped that this function can prevent the proliferation of 
07508  * language-specific vm_intro() functions and in time replace the language-
07509  * specific functions which already exist.  An examination of the language-
07510  * specific functions revealed that they all corrected the same deficiencies
07511  * in vm_intro_en() (which was the default function). Namely:
07512  *
07513  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07514  *     wording of the voicemail greeting hides this problem.  For example,
07515  *     vm-INBOX contains only the word "new".  This means that both of these
07516  *     sequences produce valid utterances:
07517  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07518  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07519  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07520  *     in many languages) the first utterance becomes "you have 1 the new message".
07521  *  2) The function contains hardcoded rules for pluralizing the word "message".
07522  *     These rules are correct for English, but not for many other languages.
07523  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07524  *     required in many languages.
07525  *  4) The gender of the word for "message" is not specified. This is a problem
07526  *     because in many languages the gender of the number in phrases such
07527  *     as "you have one new message" must match the gender of the word
07528  *     meaning "message".
07529  *
07530  * Fixing these problems for each new language has meant duplication of effort.
07531  * This new function solves the problems in the following general ways:
07532  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07533  *     and vm-Old respectively for those languages where it makes sense.
07534  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07535  *     on vm-message.
07536  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07537  *     prefix on vm-new and vm-old (none for English).
07538  *  4) Pass the gender of the language's word for "message" as an agument to
07539  *     this function which is can in turn pass on to the functions which 
07540  *     say numbers and put endings on nounds and adjectives.
07541  *
07542  * All languages require these messages:
07543  *  vm-youhave    "You have..."
07544  *  vm-and     "and"
07545  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07546  *
07547  * To use it for English, you will need these additional sound files:
07548  *  vm-new     "new"
07549  *  vm-message    "message", singular
07550  *  vm-messages      "messages", plural
07551  *
07552  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07553  *
07554  *  vm-newn    "novoye" (singular, neuter)
07555  *  vm-newx    "novikh" (counting plural form, genative plural)
07556  *  vm-message    "sobsheniye" (singular form)
07557  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07558  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07559  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07560  *  digits/2n     "dva" (neuter singular)
07561  */
07562 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07563 {
07564    int res;
07565    int lastnum = 0;
07566 
07567    res = ast_play_and_wait(chan, "vm-youhave");
07568 
07569    if (!res && vms->newmessages) {
07570       lastnum = vms->newmessages;
07571 
07572       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07573          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07574       }
07575 
07576       if (!res && vms->oldmessages) {
07577          res = ast_play_and_wait(chan, "vm-and");
07578       }
07579    }
07580 
07581    if (!res && vms->oldmessages) {
07582       lastnum = vms->oldmessages;
07583 
07584       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07585          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07586       }
07587    }
07588 
07589    if (!res) {
07590       if (lastnum == 0) {
07591          res = ast_play_and_wait(chan, "vm-no");
07592       }
07593       if (!res) {
07594          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07595       }
07596    }
07597 
07598    return res;
07599 }
07600 
07601 /* Default Hebrew syntax */
07602 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07603 {
07604    int res = 0;
07605 
07606    /* Introduce messages they have */
07607    if (!res) {
07608       if ((vms->newmessages) || (vms->oldmessages)) {
07609          res = ast_play_and_wait(chan, "vm-youhave");
07610       }
07611       /*
07612        * The word "shtei" refers to the number 2 in hebrew when performing a count
07613        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07614        * an element, this is one of them.
07615        */
07616       if (vms->newmessages) {
07617          if (!res) {
07618             if (vms->newmessages == 1) {
07619                res = ast_play_and_wait(chan, "vm-INBOX1");
07620             } else {
07621                if (vms->newmessages == 2) {
07622                   res = ast_play_and_wait(chan, "vm-shtei");
07623                } else {
07624                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07625                }
07626                res = ast_play_and_wait(chan, "vm-INBOX");
07627             }
07628          }
07629          if (vms->oldmessages && !res) {
07630             res = ast_play_and_wait(chan, "vm-and");
07631             if (vms->oldmessages == 1) {
07632                res = ast_play_and_wait(chan, "vm-Old1");
07633             } else {
07634                if (vms->oldmessages == 2) {
07635                   res = ast_play_and_wait(chan, "vm-shtei");
07636                } else {
07637                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07638                }
07639                res = ast_play_and_wait(chan, "vm-Old");
07640             }
07641          }
07642       }
07643       if (!res && vms->oldmessages && !vms->newmessages) {
07644          if (!res) {
07645             if (vms->oldmessages == 1) {
07646                res = ast_play_and_wait(chan, "vm-Old1");
07647             } else {
07648                if (vms->oldmessages == 2) {
07649                   res = ast_play_and_wait(chan, "vm-shtei");
07650                } else {
07651                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07652                }
07653                res = ast_play_and_wait(chan, "vm-Old");
07654             }
07655          }
07656       }
07657       if (!res) {
07658          if (!vms->oldmessages && !vms->newmessages) {
07659             if (!res) {
07660                res = ast_play_and_wait(chan, "vm-nomessages");
07661             }
07662          }
07663       }
07664    }
07665    return res;
07666 }
07667    
07668 /* Default English syntax */
07669 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07670 {
07671    int res;
07672 
07673    /* Introduce messages they have */
07674    res = ast_play_and_wait(chan, "vm-youhave");
07675    if (!res) {
07676       if (vms->urgentmessages) {
07677          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07678          if (!res)
07679             res = ast_play_and_wait(chan, "vm-Urgent");
07680          if ((vms->oldmessages || vms->newmessages) && !res) {
07681             res = ast_play_and_wait(chan, "vm-and");
07682          } else if (!res) {
07683             if ((vms->urgentmessages == 1))
07684                res = ast_play_and_wait(chan, "vm-message");
07685             else
07686                res = ast_play_and_wait(chan, "vm-messages");
07687          }
07688       }
07689       if (vms->newmessages) {
07690          res = say_and_wait(chan, vms->newmessages, chan->language);
07691          if (!res)
07692             res = ast_play_and_wait(chan, "vm-INBOX");
07693          if (vms->oldmessages && !res)
07694             res = ast_play_and_wait(chan, "vm-and");
07695          else if (!res) {
07696             if ((vms->newmessages == 1))
07697                res = ast_play_and_wait(chan, "vm-message");
07698             else
07699                res = ast_play_and_wait(chan, "vm-messages");
07700          }
07701             
07702       }
07703       if (!res && vms->oldmessages) {
07704          res = say_and_wait(chan, vms->oldmessages, chan->language);
07705          if (!res)
07706             res = ast_play_and_wait(chan, "vm-Old");
07707          if (!res) {
07708             if (vms->oldmessages == 1)
07709                res = ast_play_and_wait(chan, "vm-message");
07710             else
07711                res = ast_play_and_wait(chan, "vm-messages");
07712          }
07713       }
07714       if (!res) {
07715          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07716             res = ast_play_and_wait(chan, "vm-no");
07717             if (!res)
07718                res = ast_play_and_wait(chan, "vm-messages");
07719          }
07720       }
07721    }
07722    return res;
07723 }
07724 
07725 /* ITALIAN syntax */
07726 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07727 {
07728    /* Introduce messages they have */
07729    int res;
07730    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07731       res = ast_play_and_wait(chan, "vm-no") ||
07732          ast_play_and_wait(chan, "vm-message");
07733    else
07734       res = ast_play_and_wait(chan, "vm-youhave");
07735    if (!res && vms->newmessages) {
07736       res = (vms->newmessages == 1) ?
07737          ast_play_and_wait(chan, "digits/un") ||
07738          ast_play_and_wait(chan, "vm-nuovo") ||
07739          ast_play_and_wait(chan, "vm-message") :
07740          /* 2 or more new messages */
07741          say_and_wait(chan, vms->newmessages, chan->language) ||
07742          ast_play_and_wait(chan, "vm-nuovi") ||
07743          ast_play_and_wait(chan, "vm-messages");
07744       if (!res && vms->oldmessages)
07745          res = ast_play_and_wait(chan, "vm-and");
07746    }
07747    if (!res && vms->oldmessages) {
07748       res = (vms->oldmessages == 1) ?
07749          ast_play_and_wait(chan, "digits/un") ||
07750          ast_play_and_wait(chan, "vm-vecchio") ||
07751          ast_play_and_wait(chan, "vm-message") :
07752          /* 2 or more old messages */
07753          say_and_wait(chan, vms->oldmessages, chan->language) ||
07754          ast_play_and_wait(chan, "vm-vecchi") ||
07755          ast_play_and_wait(chan, "vm-messages");
07756    }
07757    return res;
07758 }
07759 
07760 /* POLISH syntax */
07761 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07762 {
07763    /* Introduce messages they have */
07764    int res;
07765    div_t num;
07766 
07767    if (!vms->oldmessages && !vms->newmessages) {
07768       res = ast_play_and_wait(chan, "vm-no");
07769       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07770       return res;
07771    } else {
07772       res = ast_play_and_wait(chan, "vm-youhave");
07773    }
07774 
07775    if (vms->newmessages) {
07776       num = div(vms->newmessages, 10);
07777       if (vms->newmessages == 1) {
07778          res = ast_play_and_wait(chan, "digits/1-a");
07779          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07780          res = res ? res : ast_play_and_wait(chan, "vm-message");
07781       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07782          if (num.rem == 2) {
07783             if (!num.quot) {
07784                res = ast_play_and_wait(chan, "digits/2-ie");
07785             } else {
07786                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07787                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07788             }
07789          } else {
07790             res = say_and_wait(chan, vms->newmessages, chan->language);
07791          }
07792          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07793          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07794       } else {
07795          res = say_and_wait(chan, vms->newmessages, chan->language);
07796          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07797          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07798       }
07799       if (!res && vms->oldmessages)
07800          res = ast_play_and_wait(chan, "vm-and");
07801    }
07802    if (!res && vms->oldmessages) {
07803       num = div(vms->oldmessages, 10);
07804       if (vms->oldmessages == 1) {
07805          res = ast_play_and_wait(chan, "digits/1-a");
07806          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07807          res = res ? res : ast_play_and_wait(chan, "vm-message");
07808       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07809          if (num.rem == 2) {
07810             if (!num.quot) {
07811                res = ast_play_and_wait(chan, "digits/2-ie");
07812             } else {
07813                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07814                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07815             }
07816          } else {
07817             res = say_and_wait(chan, vms->oldmessages, chan->language);
07818          }
07819          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07820          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07821       } else {
07822          res = say_and_wait(chan, vms->oldmessages, chan->language);
07823          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07824          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07825       }
07826    }
07827 
07828    return res;
07829 }
07830 
07831 /* SWEDISH syntax */
07832 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07833 {
07834    /* Introduce messages they have */
07835    int res;
07836 
07837    res = ast_play_and_wait(chan, "vm-youhave");
07838    if (res)
07839       return res;
07840 
07841    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07842       res = ast_play_and_wait(chan, "vm-no");
07843       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07844       return res;
07845    }
07846 
07847    if (vms->newmessages) {
07848       if ((vms->newmessages == 1)) {
07849          res = ast_play_and_wait(chan, "digits/ett");
07850          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07851          res = res ? res : ast_play_and_wait(chan, "vm-message");
07852       } else {
07853          res = say_and_wait(chan, vms->newmessages, chan->language);
07854          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07855          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07856       }
07857       if (!res && vms->oldmessages)
07858          res = ast_play_and_wait(chan, "vm-and");
07859    }
07860    if (!res && vms->oldmessages) {
07861       if (vms->oldmessages == 1) {
07862          res = ast_play_and_wait(chan, "digits/ett");
07863          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07864          res = res ? res : ast_play_and_wait(chan, "vm-message");
07865       } else {
07866          res = say_and_wait(chan, vms->oldmessages, chan->language);
07867          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07868          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07869       }
07870    }
07871 
07872    return res;
07873 }
07874 
07875 /* NORWEGIAN syntax */
07876 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07877 {
07878    /* Introduce messages they have */
07879    int res;
07880 
07881    res = ast_play_and_wait(chan, "vm-youhave");
07882    if (res)
07883       return res;
07884 
07885    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07886       res = ast_play_and_wait(chan, "vm-no");
07887       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07888       return res;
07889    }
07890 
07891    if (vms->newmessages) {
07892       if ((vms->newmessages == 1)) {
07893          res = ast_play_and_wait(chan, "digits/1");
07894          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07895          res = res ? res : ast_play_and_wait(chan, "vm-message");
07896       } else {
07897          res = say_and_wait(chan, vms->newmessages, chan->language);
07898          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07899          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07900       }
07901       if (!res && vms->oldmessages)
07902          res = ast_play_and_wait(chan, "vm-and");
07903    }
07904    if (!res && vms->oldmessages) {
07905       if (vms->oldmessages == 1) {
07906          res = ast_play_and_wait(chan, "digits/1");
07907          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07908          res = res ? res : ast_play_and_wait(chan, "vm-message");
07909       } else {
07910          res = say_and_wait(chan, vms->oldmessages, chan->language);
07911          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07912          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07913       }
07914    }
07915 
07916    return res;
07917 }
07918 
07919 /* GERMAN syntax */
07920 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07921 {
07922    /* Introduce messages they have */
07923    int res;
07924    res = ast_play_and_wait(chan, "vm-youhave");
07925    if (!res) {
07926       if (vms->newmessages) {
07927          if ((vms->newmessages == 1))
07928             res = ast_play_and_wait(chan, "digits/1F");
07929          else
07930             res = say_and_wait(chan, vms->newmessages, chan->language);
07931          if (!res)
07932             res = ast_play_and_wait(chan, "vm-INBOX");
07933          if (vms->oldmessages && !res)
07934             res = ast_play_and_wait(chan, "vm-and");
07935          else if (!res) {
07936             if ((vms->newmessages == 1))
07937                res = ast_play_and_wait(chan, "vm-message");
07938             else
07939                res = ast_play_and_wait(chan, "vm-messages");
07940          }
07941             
07942       }
07943       if (!res && vms->oldmessages) {
07944          if (vms->oldmessages == 1)
07945             res = ast_play_and_wait(chan, "digits/1F");
07946          else
07947             res = say_and_wait(chan, vms->oldmessages, chan->language);
07948          if (!res)
07949             res = ast_play_and_wait(chan, "vm-Old");
07950          if (!res) {
07951             if (vms->oldmessages == 1)
07952                res = ast_play_and_wait(chan, "vm-message");
07953             else
07954                res = ast_play_and_wait(chan, "vm-messages");
07955          }
07956       }
07957       if (!res) {
07958          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07959             res = ast_play_and_wait(chan, "vm-no");
07960             if (!res)
07961                res = ast_play_and_wait(chan, "vm-messages");
07962          }
07963       }
07964    }
07965    return res;
07966 }
07967 
07968 /* SPANISH syntax */
07969 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
07970 {
07971    /* Introduce messages they have */
07972    int res;
07973    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07974       res = ast_play_and_wait(chan, "vm-youhaveno");
07975       if (!res)
07976          res = ast_play_and_wait(chan, "vm-messages");
07977    } else {
07978       res = ast_play_and_wait(chan, "vm-youhave");
07979    }
07980    if (!res) {
07981       if (vms->newmessages) {
07982          if (!res) {
07983             if ((vms->newmessages == 1)) {
07984                res = ast_play_and_wait(chan, "digits/1M");
07985                if (!res)
07986                   res = ast_play_and_wait(chan, "vm-message");
07987                if (!res)
07988                   res = ast_play_and_wait(chan, "vm-INBOXs");
07989             } else {
07990                res = say_and_wait(chan, vms->newmessages, chan->language);
07991                if (!res)
07992                   res = ast_play_and_wait(chan, "vm-messages");
07993                if (!res)
07994                   res = ast_play_and_wait(chan, "vm-INBOX");
07995             }
07996          }
07997          if (vms->oldmessages && !res)
07998             res = ast_play_and_wait(chan, "vm-and");
07999       }
08000       if (vms->oldmessages) {
08001          if (!res) {
08002             if (vms->oldmessages == 1) {
08003                res = ast_play_and_wait(chan, "digits/1M");
08004                if (!res)
08005                   res = ast_play_and_wait(chan, "vm-message");
08006                if (!res)
08007                   res = ast_play_and_wait(chan, "vm-Olds");
08008             } else {
08009                res = say_and_wait(chan, vms->oldmessages, chan->language);
08010                if (!res)
08011                   res = ast_play_and_wait(chan, "vm-messages");
08012                if (!res)
08013                   res = ast_play_and_wait(chan, "vm-Old");
08014             }
08015          }
08016       }
08017    }
08018 return res;
08019 }
08020 
08021 /* BRAZILIAN PORTUGUESE syntax */
08022 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
08023    /* Introduce messages they have */
08024    int res;
08025    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08026       res = ast_play_and_wait(chan, "vm-nomessages");
08027       return res;
08028    } else {
08029       res = ast_play_and_wait(chan, "vm-youhave");
08030    }
08031    if (vms->newmessages) {
08032       if (!res)
08033          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08034       if ((vms->newmessages == 1)) {
08035          if (!res)
08036             res = ast_play_and_wait(chan, "vm-message");
08037          if (!res)
08038             res = ast_play_and_wait(chan, "vm-INBOXs");
08039       } else {
08040          if (!res)
08041             res = ast_play_and_wait(chan, "vm-messages");
08042          if (!res)
08043             res = ast_play_and_wait(chan, "vm-INBOX");
08044       }
08045       if (vms->oldmessages && !res)
08046          res = ast_play_and_wait(chan, "vm-and");
08047    }
08048    if (vms->oldmessages) {
08049       if (!res)
08050          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08051       if (vms->oldmessages == 1) {
08052          if (!res)
08053             res = ast_play_and_wait(chan, "vm-message");
08054          if (!res)
08055             res = ast_play_and_wait(chan, "vm-Olds");
08056       } else {
08057          if (!res)
08058             res = ast_play_and_wait(chan, "vm-messages");
08059          if (!res)
08060             res = ast_play_and_wait(chan, "vm-Old");
08061       }
08062    }
08063    return res;
08064 }
08065 
08066 /* FRENCH syntax */
08067 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
08068 {
08069    /* Introduce messages they have */
08070    int res;
08071    res = ast_play_and_wait(chan, "vm-youhave");
08072    if (!res) {
08073       if (vms->newmessages) {
08074          res = say_and_wait(chan, vms->newmessages, chan->language);
08075          if (!res)
08076             res = ast_play_and_wait(chan, "vm-INBOX");
08077          if (vms->oldmessages && !res)
08078             res = ast_play_and_wait(chan, "vm-and");
08079          else if (!res) {
08080             if ((vms->newmessages == 1))
08081                res = ast_play_and_wait(chan, "vm-message");
08082             else
08083                res = ast_play_and_wait(chan, "vm-messages");
08084          }
08085             
08086       }
08087       if (!res && vms->oldmessages) {
08088          res = say_and_wait(chan, vms->oldmessages, chan->language);
08089          if (!res)
08090             res = ast_play_and_wait(chan, "vm-Old");
08091          if (!res) {
08092             if (vms->oldmessages == 1)
08093                res = ast_play_and_wait(chan, "vm-message");
08094             else
08095                res = ast_play_and_wait(chan, "vm-messages");
08096          }
08097       }
08098       if (!res) {
08099          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08100             res = ast_play_and_wait(chan, "vm-no");
08101             if (!res)
08102                res = ast_play_and_wait(chan, "vm-messages");
08103          }
08104       }
08105    }
08106    return res;
08107 }
08108 
08109 /* DUTCH syntax */
08110 static int vm_intro_nl(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             if (vms->newmessages == 1)
08120                res = ast_play_and_wait(chan, "vm-INBOXs");
08121             else
08122                res = ast_play_and_wait(chan, "vm-INBOX");
08123          }
08124          if (vms->oldmessages && !res)
08125             res = ast_play_and_wait(chan, "vm-and");
08126          else if (!res) {
08127             if ((vms->newmessages == 1))
08128                res = ast_play_and_wait(chan, "vm-message");
08129             else
08130                res = ast_play_and_wait(chan, "vm-messages");
08131          }
08132             
08133       }
08134       if (!res && vms->oldmessages) {
08135          res = say_and_wait(chan, vms->oldmessages, chan->language);
08136          if (!res) {
08137             if (vms->oldmessages == 1)
08138                res = ast_play_and_wait(chan, "vm-Olds");
08139             else
08140                res = ast_play_and_wait(chan, "vm-Old");
08141          }
08142          if (!res) {
08143             if (vms->oldmessages == 1)
08144                res = ast_play_and_wait(chan, "vm-message");
08145             else
08146                res = ast_play_and_wait(chan, "vm-messages");
08147          }
08148       }
08149       if (!res) {
08150          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08151             res = ast_play_and_wait(chan, "vm-no");
08152             if (!res)
08153                res = ast_play_and_wait(chan, "vm-messages");
08154          }
08155       }
08156    }
08157    return res;
08158 }
08159 
08160 /* PORTUGUESE syntax */
08161 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08162 {
08163    /* Introduce messages they have */
08164    int res;
08165    res = ast_play_and_wait(chan, "vm-youhave");
08166    if (!res) {
08167       if (vms->newmessages) {
08168          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08169          if (!res) {
08170             if ((vms->newmessages == 1)) {
08171                res = ast_play_and_wait(chan, "vm-message");
08172                if (!res)
08173                   res = ast_play_and_wait(chan, "vm-INBOXs");
08174             } else {
08175                res = ast_play_and_wait(chan, "vm-messages");
08176                if (!res)
08177                   res = ast_play_and_wait(chan, "vm-INBOX");
08178             }
08179          }
08180          if (vms->oldmessages && !res)
08181             res = ast_play_and_wait(chan, "vm-and");
08182       }
08183       if (!res && vms->oldmessages) {
08184          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08185          if (!res) {
08186             if (vms->oldmessages == 1) {
08187                res = ast_play_and_wait(chan, "vm-message");
08188                if (!res)
08189                   res = ast_play_and_wait(chan, "vm-Olds");
08190             } else {
08191                res = ast_play_and_wait(chan, "vm-messages");
08192                if (!res)
08193                   res = ast_play_and_wait(chan, "vm-Old");
08194             }
08195          }
08196       }
08197       if (!res) {
08198          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08199             res = ast_play_and_wait(chan, "vm-no");
08200             if (!res)
08201                res = ast_play_and_wait(chan, "vm-messages");
08202          }
08203       }
08204    }
08205    return res;
08206 }
08207 
08208 
08209 /* CZECH syntax */
08210 /* in czech there must be declension of word new and message
08211  * czech        : english        : czech      : english
08212  * --------------------------------------------------------
08213  * vm-youhave   : you have 
08214  * vm-novou     : one new        : vm-zpravu  : message
08215  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08216  * vm-novych    : 5-infinite new : vm-zprav   : messages
08217  * vm-starou   : one old
08218  * vm-stare     : 2-4 old 
08219  * vm-starych   : 5-infinite old
08220  * jednu        : one   - falling 4. 
08221  * vm-no        : no  ( no messages )
08222  */
08223 
08224 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08225 {
08226    int res;
08227    res = ast_play_and_wait(chan, "vm-youhave");
08228    if (!res) {
08229       if (vms->newmessages) {
08230          if (vms->newmessages == 1) {
08231             res = ast_play_and_wait(chan, "digits/jednu");
08232          } else {
08233             res = say_and_wait(chan, vms->newmessages, chan->language);
08234          }
08235          if (!res) {
08236             if ((vms->newmessages == 1))
08237                res = ast_play_and_wait(chan, "vm-novou");
08238             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08239                res = ast_play_and_wait(chan, "vm-nove");
08240             if (vms->newmessages > 4)
08241                res = ast_play_and_wait(chan, "vm-novych");
08242          }
08243          if (vms->oldmessages && !res)
08244             res = ast_play_and_wait(chan, "vm-and");
08245          else if (!res) {
08246             if ((vms->newmessages == 1))
08247                res = ast_play_and_wait(chan, "vm-zpravu");
08248             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08249                res = ast_play_and_wait(chan, "vm-zpravy");
08250             if (vms->newmessages > 4)
08251                res = ast_play_and_wait(chan, "vm-zprav");
08252          }
08253       }
08254       if (!res && vms->oldmessages) {
08255          res = say_and_wait(chan, vms->oldmessages, chan->language);
08256          if (!res) {
08257             if ((vms->oldmessages == 1))
08258                res = ast_play_and_wait(chan, "vm-starou");
08259             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08260                res = ast_play_and_wait(chan, "vm-stare");
08261             if (vms->oldmessages > 4)
08262                res = ast_play_and_wait(chan, "vm-starych");
08263          }
08264          if (!res) {
08265             if ((vms->oldmessages == 1))
08266                res = ast_play_and_wait(chan, "vm-zpravu");
08267             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08268                res = ast_play_and_wait(chan, "vm-zpravy");
08269             if (vms->oldmessages > 4)
08270                res = ast_play_and_wait(chan, "vm-zprav");
08271          }
08272       }
08273       if (!res) {
08274          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08275             res = ast_play_and_wait(chan, "vm-no");
08276             if (!res)
08277                res = ast_play_and_wait(chan, "vm-zpravy");
08278          }
08279       }
08280    }
08281    return res;
08282 }
08283 
08284 /* CHINESE (Taiwan) syntax */
08285 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08286 {
08287    int res;
08288    /* Introduce messages they have */
08289    res = ast_play_and_wait(chan, "vm-you");
08290 
08291    if (!res && vms->newmessages) {
08292       res = ast_play_and_wait(chan, "vm-have");
08293       if (!res)
08294          res = say_and_wait(chan, vms->newmessages, chan->language);
08295       if (!res)
08296          res = ast_play_and_wait(chan, "vm-tong");
08297       if (!res)
08298          res = ast_play_and_wait(chan, "vm-INBOX");
08299       if (vms->oldmessages && !res)
08300          res = ast_play_and_wait(chan, "vm-and");
08301       else if (!res) 
08302          res = ast_play_and_wait(chan, "vm-messages");
08303    }
08304    if (!res && vms->oldmessages) {
08305       res = ast_play_and_wait(chan, "vm-have");
08306       if (!res)
08307          res = say_and_wait(chan, vms->oldmessages, chan->language);
08308       if (!res)
08309          res = ast_play_and_wait(chan, "vm-tong");
08310       if (!res)
08311          res = ast_play_and_wait(chan, "vm-Old");
08312       if (!res)
08313          res = ast_play_and_wait(chan, "vm-messages");
08314    }
08315    if (!res && !vms->oldmessages && !vms->newmessages) {
08316       res = ast_play_and_wait(chan, "vm-haveno");
08317       if (!res)
08318          res = ast_play_and_wait(chan, "vm-messages");
08319    }
08320    return res;
08321 }
08322 
08323 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08324 {
08325    char prefile[256];
08326    
08327    /* Notify the user that the temp greeting is set and give them the option to remove it */
08328    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08329    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08330       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08331       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08332          ast_play_and_wait(chan, "vm-tempgreetactive");
08333       }
08334       DISPOSE(prefile, -1);
08335    }
08336 
08337    /* Play voicemail intro - syntax is different for different languages */
08338    if (0) {
08339    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08340       return vm_intro_cs(chan, vms);
08341    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08342       static int deprecation_warning = 0;
08343       if (deprecation_warning++ % 10 == 0) {
08344          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08345       }
08346       return vm_intro_cs(chan, vms);
08347    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08348       return vm_intro_de(chan, vms);
08349    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08350       return vm_intro_es(chan, vms);
08351    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08352       return vm_intro_fr(chan, vms);
08353    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08354       return vm_intro_gr(chan, vms);
08355    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08356       return vm_intro_he(chan, vms);
08357    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08358       return vm_intro_it(chan, vms);
08359    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08360       return vm_intro_nl(chan, vms);
08361    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08362       return vm_intro_no(chan, vms);
08363    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08364       return vm_intro_pl(chan, vms);
08365    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08366       return vm_intro_pt_BR(chan, vms);
08367    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08368       return vm_intro_pt(chan, vms);
08369    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08370       return vm_intro_multilang(chan, vms, "n");
08371    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08372       return vm_intro_se(chan, vms);
08373    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08374       return vm_intro_multilang(chan, vms, "n");
08375    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08376       return vm_intro_zh(chan, vms);
08377    } else {                                             /* Default to ENGLISH */
08378       return vm_intro_en(chan, vms);
08379    }
08380 }
08381 
08382 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08383 {
08384    int res = 0;
08385    /* Play instructions and wait for new command */
08386    while (!res) {
08387       if (vms->starting) {
08388          if (vms->lastmsg > -1) {
08389             if (skipadvanced)
08390                res = ast_play_and_wait(chan, "vm-onefor-full");
08391             else
08392                res = ast_play_and_wait(chan, "vm-onefor");
08393             if (!res)
08394                res = vm_play_folder_name(chan, vms->vmbox);
08395          }
08396          if (!res) {
08397             if (skipadvanced)
08398                res = ast_play_and_wait(chan, "vm-opts-full");
08399             else
08400                res = ast_play_and_wait(chan, "vm-opts");
08401          }
08402       } else {
08403          /* Added for additional help */
08404          if (skipadvanced) {
08405             res = ast_play_and_wait(chan, "vm-onefor-full");
08406             if (!res)
08407                res = vm_play_folder_name(chan, vms->vmbox);
08408             res = ast_play_and_wait(chan, "vm-opts-full");
08409          }
08410          /* Logic:
08411           * If the current message is not the first OR
08412           * if we're listening to the first new message and there are
08413           * also urgent messages, then prompt for navigation to the
08414           * previous message
08415           */
08416          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08417             res = ast_play_and_wait(chan, "vm-prev");
08418          }
08419          if (!res && !skipadvanced)
08420             res = ast_play_and_wait(chan, "vm-advopts");
08421          if (!res)
08422             res = ast_play_and_wait(chan, "vm-repeat");
08423          /* Logic:
08424           * If we're not listening to the last message OR
08425           * we're listening to the last urgent message and there are
08426           * also new non-urgent messages, then prompt for navigation
08427           * to the next message
08428           */
08429          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08430             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08431             res = ast_play_and_wait(chan, "vm-next");
08432          }
08433          if (!res) {
08434             if (!vms->deleted[vms->curmsg])
08435                res = ast_play_and_wait(chan, "vm-delete");
08436             else
08437                res = ast_play_and_wait(chan, "vm-undelete");
08438             if (!res)
08439                res = ast_play_and_wait(chan, "vm-toforward");
08440             if (!res)
08441                res = ast_play_and_wait(chan, "vm-savemessage");
08442          }
08443       }
08444       if (!res) {
08445          res = ast_play_and_wait(chan, "vm-helpexit");
08446       }
08447       if (!res)
08448          res = ast_waitfordigit(chan, 6000);
08449       if (!res) {
08450          vms->repeats++;
08451          if (vms->repeats > 2) {
08452             res = 't';
08453          }
08454       }
08455    }
08456    return res;
08457 }
08458 
08459 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08460 {
08461    int res = 0;
08462    /* Play instructions and wait for new command */
08463    while (!res) {
08464       if (vms->lastmsg > -1) {
08465          res = ast_play_and_wait(chan, "vm-listen");
08466          if (!res)
08467             res = vm_play_folder_name(chan, vms->vmbox);
08468          if (!res)
08469             res = ast_play_and_wait(chan, "press");
08470          if (!res)
08471             res = ast_play_and_wait(chan, "digits/1");
08472       }
08473       if (!res)
08474          res = ast_play_and_wait(chan, "vm-opts");
08475       if (!res) {
08476          vms->starting = 0;
08477          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08478       }
08479    }
08480    return res;
08481 }
08482 
08483 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08484 {
08485    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08486       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08487    } else {             /* Default to ENGLISH */
08488       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08489    }
08490 }
08491 
08492 
08493 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08494 {
08495    int cmd = 0;
08496    int duration = 0;
08497    int tries = 0;
08498    char newpassword[80] = "";
08499    char newpassword2[80] = "";
08500    char prefile[PATH_MAX] = "";
08501    unsigned char buf[256];
08502    int bytes=0;
08503 
08504    if (ast_adsi_available(chan)) {
08505       bytes += adsi_logo(buf + bytes);
08506       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08507       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08508       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08509       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08510       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08511    }
08512 
08513    /* First, have the user change their password 
08514       so they won't get here again */
08515    for (;;) {
08516       newpassword[1] = '\0';
08517       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08518       if (cmd == '#')
08519          newpassword[0] = '\0';
08520       if (cmd < 0 || cmd == 't' || cmd == '#')
08521          return cmd;
08522       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08523       if (cmd < 0 || cmd == 't' || cmd == '#')
08524          return cmd;
08525       cmd = check_password(vmu, newpassword); /* perform password validation */
08526       if (cmd != 0) {
08527          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08528          cmd = ast_play_and_wait(chan, vm_invalid_password);
08529       } else {
08530          newpassword2[1] = '\0';
08531          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08532          if (cmd == '#')
08533             newpassword2[0] = '\0';
08534          if (cmd < 0 || cmd == 't' || cmd == '#')
08535             return cmd;
08536          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08537          if (cmd < 0 || cmd == 't' || cmd == '#')
08538             return cmd;
08539          if (!strcmp(newpassword, newpassword2))
08540             break;
08541          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08542          cmd = ast_play_and_wait(chan, vm_mismatch);
08543       }
08544       if (++tries == 3)
08545          return -1;
08546       if (cmd != 0) {
08547          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08548       }
08549    }
08550    if (pwdchange & PWDCHANGE_INTERNAL)
08551       vm_change_password(vmu, newpassword);
08552    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08553       vm_change_password_shell(vmu, newpassword);
08554 
08555    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08556    cmd = ast_play_and_wait(chan, vm_passchanged);
08557 
08558    /* If forcename is set, have the user record their name */  
08559    if (ast_test_flag(vmu, VM_FORCENAME)) {
08560       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08561       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08562          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08563          if (cmd < 0 || cmd == 't' || cmd == '#')
08564             return cmd;
08565       }
08566    }
08567 
08568    /* If forcegreetings is set, have the user record their greetings */
08569    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08570       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08571       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08572          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08573          if (cmd < 0 || cmd == 't' || cmd == '#')
08574             return cmd;
08575       }
08576 
08577       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08578       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08579          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08580          if (cmd < 0 || cmd == 't' || cmd == '#')
08581             return cmd;
08582       }
08583    }
08584 
08585    return cmd;
08586 }
08587 
08588 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08589 {
08590    int cmd = 0;
08591    int retries = 0;
08592    int duration = 0;
08593    char newpassword[80] = "";
08594    char newpassword2[80] = "";
08595    char prefile[PATH_MAX] = "";
08596    unsigned char buf[256];
08597    int bytes=0;
08598 
08599    if (ast_adsi_available(chan)) {
08600       bytes += adsi_logo(buf + bytes);
08601       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08602       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08603       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08604       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08605       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08606    }
08607    while ((cmd >= 0) && (cmd != 't')) {
08608       if (cmd)
08609          retries = 0;
08610       switch (cmd) {
08611       case '1': /* Record your unavailable message */
08612          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08613          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08614          break;
08615       case '2':  /* Record your busy message */
08616          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08617          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08618          break;
08619       case '3': /* Record greeting */
08620          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08621          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08622          break;
08623       case '4':  /* manage the temporary greeting */
08624          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08625          break;
08626       case '5': /* change password */
08627          if (vmu->password[0] == '-') {
08628             cmd = ast_play_and_wait(chan, "vm-no");
08629             break;
08630          }
08631          newpassword[1] = '\0';
08632          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08633          if (cmd == '#')
08634             newpassword[0] = '\0';
08635          else {
08636             if (cmd < 0)
08637                break;
08638             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08639                break;
08640             }
08641          }
08642          cmd = check_password(vmu, newpassword); /* perform password validation */
08643          if (cmd != 0) {
08644             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08645             cmd = ast_play_and_wait(chan, vm_invalid_password);
08646             if (!cmd) {
08647                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08648             }
08649             break;
08650          }
08651          newpassword2[1] = '\0';
08652          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08653          if (cmd == '#')
08654             newpassword2[0] = '\0';
08655          else {
08656             if (cmd < 0)
08657                break;
08658 
08659             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08660                break;
08661             }
08662          }
08663          if (strcmp(newpassword, newpassword2)) {
08664             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08665             cmd = ast_play_and_wait(chan, vm_mismatch);
08666             if (!cmd) {
08667                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08668             }
08669             break;
08670          }
08671          if (pwdchange & PWDCHANGE_INTERNAL)
08672             vm_change_password(vmu, newpassword);
08673          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08674             vm_change_password_shell(vmu, newpassword);
08675 
08676          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08677          cmd = ast_play_and_wait(chan, vm_passchanged);
08678          break;
08679       case '*': 
08680          cmd = 't';
08681          break;
08682       default: 
08683          cmd = 0;
08684          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08685          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08686          if (ast_fileexists(prefile, NULL, NULL)) {
08687             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08688          }
08689          DISPOSE(prefile, -1);
08690          if (!cmd) {
08691             cmd = ast_play_and_wait(chan, "vm-options");
08692          }
08693          if (!cmd) {
08694             cmd = ast_waitfordigit(chan,6000);
08695          }
08696          if (!cmd) {
08697             retries++;
08698          }
08699          if (retries > 3) {
08700             cmd = 't';
08701          }
08702       }
08703    }
08704    if (cmd == 't')
08705       cmd = 0;
08706    return cmd;
08707 }
08708 
08709 /*!
08710  * \brief The handler for 'record a temporary greeting'. 
08711  * \param chan
08712  * \param vmu
08713  * \param vms
08714  * \param fmtc
08715  * \param record_gain
08716  *
08717  * This is option 4 from the mailbox options menu.
08718  * This function manages the following promptings:
08719  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08720  * 2: remove (delete) the temporary greeting.
08721  * *: return to the main menu.
08722  *
08723  * \return zero on success, -1 on error.
08724  */
08725 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08726 {
08727    int cmd = 0;
08728    int retries = 0;
08729    int duration = 0;
08730    char prefile[PATH_MAX] = "";
08731    unsigned char buf[256];
08732    int bytes = 0;
08733 
08734    if (ast_adsi_available(chan)) {
08735       bytes += adsi_logo(buf + bytes);
08736       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08737       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08738       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08739       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08740       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08741    }
08742 
08743    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08744    while ((cmd >= 0) && (cmd != 't')) {
08745       if (cmd)
08746          retries = 0;
08747       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08748       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08749          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08750          cmd = 't';  
08751       } else {
08752          switch (cmd) {
08753          case '1':
08754             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08755             break;
08756          case '2':
08757             DELETE(prefile, -1, prefile, vmu);
08758             ast_play_and_wait(chan, "vm-tempremoved");
08759             cmd = 't';  
08760             break;
08761          case '*': 
08762             cmd = 't';
08763             break;
08764          default:
08765             cmd = ast_play_and_wait(chan,
08766                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08767                   "vm-tempgreeting2" : "vm-tempgreeting");
08768             if (!cmd)
08769                cmd = ast_waitfordigit(chan,6000);
08770             if (!cmd)
08771                retries++;
08772             if (retries > 3)
08773                cmd = 't';
08774          }
08775       }
08776       DISPOSE(prefile, -1);
08777    }
08778    if (cmd == 't')
08779       cmd = 0;
08780    return cmd;
08781 }
08782 
08783 /*!
08784  * \brief Greek syntax for 'You have N messages' greeting.
08785  * \param chan
08786  * \param vms
08787  * \param vmu
08788  *
08789  * \return zero on success, -1 on error.
08790  */   
08791 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08792 {
08793    int cmd=0;
08794 
08795    if (vms->lastmsg > -1) {
08796       cmd = play_message(chan, vmu, vms);
08797    } else {
08798       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08799       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08800          if (!cmd) {
08801             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08802             cmd = ast_play_and_wait(chan, vms->fn);
08803          }
08804          if (!cmd)
08805             cmd = ast_play_and_wait(chan, "vm-messages");
08806       } else {
08807          if (!cmd)
08808             cmd = ast_play_and_wait(chan, "vm-messages");
08809          if (!cmd) {
08810             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08811             cmd = ast_play_and_wait(chan, vms->fn);
08812          }
08813       }
08814    } 
08815    return cmd;
08816 }
08817 
08818 /* Hebrew Syntax */
08819 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08820 {
08821    int cmd = 0;
08822 
08823    if (vms->lastmsg > -1) {
08824       cmd = play_message(chan, vmu, vms);
08825    } else {
08826       if (!strcasecmp(vms->fn, "INBOX")) {
08827          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08828       } else {
08829          cmd = ast_play_and_wait(chan, "vm-nomessages");
08830       }
08831    }
08832    return cmd;
08833 }
08834 
08835 /*! 
08836  * \brief Default English syntax for 'You have N messages' greeting.
08837  * \param chan
08838  * \param vms
08839  * \param vmu
08840  *
08841  * \return zero on success, -1 on error.
08842  */
08843 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08844 {
08845    int cmd=0;
08846 
08847    if (vms->lastmsg > -1) {
08848       cmd = play_message(chan, vmu, vms);
08849    } else {
08850       cmd = ast_play_and_wait(chan, "vm-youhave");
08851       if (!cmd) 
08852          cmd = ast_play_and_wait(chan, "vm-no");
08853       if (!cmd) {
08854          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08855          cmd = ast_play_and_wait(chan, vms->fn);
08856       }
08857       if (!cmd)
08858          cmd = ast_play_and_wait(chan, "vm-messages");
08859    }
08860    return cmd;
08861 }
08862 
08863 /*! 
08864  *\brief Italian syntax for 'You have N messages' greeting.
08865  * \param chan
08866  * \param vms
08867  * \param vmu
08868  *
08869  * \return zero on success, -1 on error.
08870  */
08871 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08872 {
08873    int cmd=0;
08874 
08875    if (vms->lastmsg > -1) {
08876       cmd = play_message(chan, vmu, vms);
08877    } else {
08878       cmd = ast_play_and_wait(chan, "vm-no");
08879       if (!cmd)
08880          cmd = ast_play_and_wait(chan, "vm-message");
08881       if (!cmd) {
08882          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08883          cmd = ast_play_and_wait(chan, vms->fn);
08884       }
08885    }
08886    return cmd;
08887 }
08888 
08889 /*! 
08890  * \brief Spanish syntax for 'You have N messages' greeting.
08891  * \param chan
08892  * \param vms
08893  * \param vmu
08894  *
08895  * \return zero on success, -1 on error.
08896  */
08897 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08898 {
08899    int cmd=0;
08900 
08901    if (vms->lastmsg > -1) {
08902       cmd = play_message(chan, vmu, vms);
08903    } else {
08904       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08905       if (!cmd)
08906          cmd = ast_play_and_wait(chan, "vm-messages");
08907       if (!cmd) {
08908          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08909          cmd = ast_play_and_wait(chan, vms->fn);
08910       }
08911    }
08912    return cmd;
08913 }
08914 
08915 /*! 
08916  * \brief Portuguese syntax for 'You have N messages' greeting.
08917  * \param chan
08918  * \param vms
08919  * \param vmu
08920  *
08921  * \return zero on success, -1 on error.
08922  */
08923 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08924 {
08925    int cmd=0;
08926 
08927    if (vms->lastmsg > -1) {
08928       cmd = play_message(chan, vmu, vms);
08929    } else {
08930       cmd = ast_play_and_wait(chan, "vm-no");
08931       if (!cmd) {
08932          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08933          cmd = ast_play_and_wait(chan, vms->fn);
08934       }
08935       if (!cmd)
08936          cmd = ast_play_and_wait(chan, "vm-messages");
08937    }
08938    return cmd;
08939 }
08940 
08941 /*! 
08942  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08943  * \param chan
08944  * \param vms
08945  * \param vmu
08946  *
08947  * \return zero on success, -1 on error.
08948  */
08949 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08950 {
08951    int cmd=0;
08952 
08953    if (vms->lastmsg > -1) {
08954       cmd = play_message(chan, vmu, vms);
08955    } else {
08956       cmd = ast_play_and_wait(chan, "vm-you");
08957       if (!cmd) 
08958          cmd = ast_play_and_wait(chan, "vm-haveno");
08959       if (!cmd)
08960          cmd = ast_play_and_wait(chan, "vm-messages");
08961       if (!cmd) {
08962          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08963          cmd = ast_play_and_wait(chan, vms->fn);
08964       }
08965    }
08966    return cmd;
08967 }
08968 
08969 /*!
08970  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
08971  * \param chan The channel for the current user. We read the language property from this.
08972  * \param vms passed into the language-specific vm_browse_messages function.
08973  * \param vmu passed into the language-specific vm_browse_messages function.
08974  * 
08975  * The method to be invoked is determined by the value of language code property in the user's channel.
08976  * The default (when unable to match) is to use english.
08977  *
08978  * \return zero on success, -1 on error.
08979  */
08980 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08981 {
08982    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
08983       return vm_browse_messages_es(chan, vms, vmu);
08984    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
08985       return vm_browse_messages_gr(chan, vms, vmu);
08986    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
08987       return vm_browse_messages_he(chan, vms, vmu);
08988    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
08989       return vm_browse_messages_it(chan, vms, vmu);
08990    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
08991       return vm_browse_messages_pt(chan, vms, vmu);
08992    } else if (!strncasecmp(chan->language, "zh", 2)) {
08993       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
08994    } else {                                             /* Default to English syntax */
08995       return vm_browse_messages_en(chan, vms, vmu);
08996    }
08997 }
08998 
08999 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09000          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09001          int skipuser, int max_logins, int silent)
09002 {
09003    int useadsi=0, valid=0, logretries=0;
09004    char password[AST_MAX_EXTENSION]="", *passptr;
09005    struct ast_vm_user vmus, *vmu = NULL;
09006 
09007    /* If ADSI is supported, setup login screen */
09008    adsi_begin(chan, &useadsi);
09009    if (!skipuser && useadsi)
09010       adsi_login(chan);
09011    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09012       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09013       return -1;
09014    }
09015    
09016    /* Authenticate them and get their mailbox/password */
09017    
09018    while (!valid && (logretries < max_logins)) {
09019       /* Prompt for, and read in the username */
09020       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09021          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09022          return -1;
09023       }
09024       if (ast_strlen_zero(mailbox)) {
09025          if (chan->cid.cid_num) {
09026             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
09027          } else {
09028             ast_verb(3,"Username not entered\n");  
09029             return -1;
09030          }
09031       }
09032       if (useadsi)
09033          adsi_password(chan);
09034 
09035       if (!ast_strlen_zero(prefix)) {
09036          char fullusername[80] = "";
09037          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09038          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09039          ast_copy_string(mailbox, fullusername, mailbox_size);
09040       }
09041 
09042       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
09043       vmu = find_user(&vmus, context, mailbox);
09044       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09045          /* saved password is blank, so don't bother asking */
09046          password[0] = '\0';
09047       } else {
09048          if (ast_streamfile(chan, vm_password, chan->language)) {
09049             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09050             return -1;
09051          }
09052          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09053             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09054             return -1;
09055          }
09056       }
09057 
09058       if (vmu) {
09059          passptr = vmu->password;
09060          if (passptr[0] == '-') passptr++;
09061       }
09062       if (vmu && !strcmp(passptr, password))
09063          valid++;
09064       else {
09065          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09066          if (!ast_strlen_zero(prefix))
09067             mailbox[0] = '\0';
09068       }
09069       logretries++;
09070       if (!valid) {
09071          if (skipuser || logretries >= max_logins) {
09072             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09073                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09074                return -1;
09075             }
09076          } else {
09077             if (useadsi)
09078                adsi_login(chan);
09079             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09080                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09081                return -1;
09082             }
09083          }
09084          if (ast_waitstream(chan, "")) /* Channel is hung up */
09085             return -1;
09086       }
09087    }
09088    if (!valid && (logretries >= max_logins)) {
09089       ast_stopstream(chan);
09090       ast_play_and_wait(chan, "vm-goodbye");
09091       return -1;
09092    }
09093    if (vmu && !skipuser) {
09094       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09095    }
09096    return 0;
09097 }
09098 
09099 static int vm_execmain(struct ast_channel *chan, void *data)
09100 {
09101    /* XXX This is, admittedly, some pretty horrendous code.  For some
09102       reason it just seemed a lot easier to do with GOTO's.  I feel
09103       like I'm back in my GWBASIC days. XXX */
09104    int res=-1;
09105    int cmd=0;
09106    int valid = 0;
09107    char prefixstr[80] ="";
09108    char ext_context[256]="";
09109    int box;
09110    int useadsi = 0;
09111    int skipuser = 0;
09112    struct vm_state vms;
09113    struct ast_vm_user *vmu = NULL, vmus;
09114    char *context=NULL;
09115    int silentexit = 0;
09116    struct ast_flags flags = { 0 };
09117    signed char record_gain = 0;
09118    int play_auto = 0;
09119    int play_folder = 0;
09120    int in_urgent = 0;
09121 #ifdef IMAP_STORAGE
09122    int deleted = 0;
09123 #endif
09124 
09125    /* Add the vm_state to the active list and keep it active */
09126    memset(&vms, 0, sizeof(vms));
09127 
09128    vms.lastmsg = -1;
09129 
09130    memset(&vmus, 0, sizeof(vmus));
09131 
09132    if (chan->_state != AST_STATE_UP) {
09133       ast_debug(1, "Before ast_answer\n");
09134       ast_answer(chan);
09135    }
09136 
09137    if (!ast_strlen_zero(data)) {
09138       char *opts[OPT_ARG_ARRAY_SIZE];
09139       char *parse;
09140       AST_DECLARE_APP_ARGS(args,
09141          AST_APP_ARG(argv0);
09142          AST_APP_ARG(argv1);
09143       );
09144 
09145       parse = ast_strdupa(data);
09146 
09147       AST_STANDARD_APP_ARGS(args, parse);
09148 
09149       if (args.argc == 2) {
09150          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09151             return -1;
09152          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09153             int gain;
09154             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09155                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09156                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09157                   return -1;
09158                } else {
09159                   record_gain = (signed char) gain;
09160                }
09161             } else {
09162                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09163             }
09164          }
09165          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09166             play_auto = 1;
09167             if (opts[OPT_ARG_PLAYFOLDER]) {
09168                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09169                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09170                }
09171             } else {
09172                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09173             }  
09174             if ( play_folder > 9 || play_folder < 0) {
09175                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09176                play_folder = 0;
09177             }
09178          }
09179       } else {
09180          /* old style options parsing */
09181          while (*(args.argv0)) {
09182             if (*(args.argv0) == 's')
09183                ast_set_flag(&flags, OPT_SILENT);
09184             else if (*(args.argv0) == 'p')
09185                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09186             else 
09187                break;
09188             (args.argv0)++;
09189          }
09190 
09191       }
09192 
09193       valid = ast_test_flag(&flags, OPT_SILENT);
09194 
09195       if ((context = strchr(args.argv0, '@')))
09196          *context++ = '\0';
09197 
09198       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09199          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09200       else
09201          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09202 
09203       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09204          skipuser++;
09205       else
09206          valid = 0;
09207    }
09208 
09209    if (!valid)
09210       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09211 
09212    ast_debug(1, "After vm_authenticate\n");
09213    if (!res) {
09214       valid = 1;
09215       if (!skipuser)
09216          vmu = &vmus;
09217    } else {
09218       res = 0;
09219    }
09220 
09221    /* If ADSI is supported, setup login screen */
09222    adsi_begin(chan, &useadsi);
09223 
09224    if (!valid) {
09225       goto out;
09226    }
09227 
09228 #ifdef IMAP_STORAGE
09229    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09230    pthread_setspecific(ts_vmstate.key, &vms);
09231 
09232    vms.interactive = 1;
09233    vms.updated = 1;
09234    if (vmu)
09235       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09236    vmstate_insert(&vms);
09237    init_vm_state(&vms);
09238 #endif
09239    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09240       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09241       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09242       return -1;
09243    }
09244    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09245       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09246       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09247       return -1;
09248    }
09249    
09250    /* Set language from config to override channel language */
09251    if (!ast_strlen_zero(vmu->language))
09252       ast_string_field_set(chan, language, vmu->language);
09253 
09254    /* Retrieve urgent, old and new message counts */
09255    ast_debug(1, "Before open_mailbox\n");
09256    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09257    if (res == ERROR_LOCK_PATH)
09258       goto out;
09259    vms.oldmessages = vms.lastmsg + 1;
09260    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09261    /* check INBOX */
09262    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09263    if (res == ERROR_LOCK_PATH)
09264       goto out;
09265    vms.newmessages = vms.lastmsg + 1;
09266    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09267    /* Start in Urgent */
09268    in_urgent = 1;
09269    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09270    if (res == ERROR_LOCK_PATH)
09271       goto out;
09272    vms.urgentmessages = vms.lastmsg + 1;
09273    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09274 
09275    /* Select proper mailbox FIRST!! */
09276    if (play_auto) {
09277       if (vms.urgentmessages) {
09278          in_urgent = 1;
09279          res = open_mailbox(&vms, vmu, 11);
09280       } else {
09281          in_urgent = 0;
09282          res = open_mailbox(&vms, vmu, play_folder);
09283       }
09284       if (res == ERROR_LOCK_PATH)
09285          goto out;
09286 
09287       /* If there are no new messages, inform the user and hangup */
09288       if (vms.lastmsg == -1) {
09289          in_urgent = 0;
09290          cmd = vm_browse_messages(chan, &vms, vmu);
09291          res = 0;
09292          goto out;
09293       }
09294    } else {
09295       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09296          /* If we only have old messages start here */
09297          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09298          in_urgent = 0;
09299          play_folder = 1;
09300          if (res == ERROR_LOCK_PATH)
09301             goto out;
09302       } else if (!vms.urgentmessages && vms.newmessages) {
09303          /* If we have new messages but none are urgent */
09304          in_urgent = 0;
09305          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09306          if (res == ERROR_LOCK_PATH)
09307             goto out;
09308       }
09309    }
09310 
09311    if (useadsi)
09312       adsi_status(chan, &vms);
09313    res = 0;
09314 
09315    /* Check to see if this is a new user */
09316    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09317       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09318       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09319          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09320       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09321       if ((cmd == 't') || (cmd == '#')) {
09322          /* Timeout */
09323          res = 0;
09324          goto out;
09325       } else if (cmd < 0) {
09326          /* Hangup */
09327          res = -1;
09328          goto out;
09329       }
09330    }
09331 #ifdef IMAP_STORAGE
09332       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09333       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09334          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09335          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09336       }
09337       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09338       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09339          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09340          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09341       }
09342 #endif
09343    if (play_auto) {
09344       cmd = '1';
09345    } else {
09346       cmd = vm_intro(chan, vmu, &vms);
09347    }
09348 
09349    vms.repeats = 0;
09350    vms.starting = 1;
09351    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09352       /* Run main menu */
09353       switch (cmd) {
09354       case '1': /* First message */
09355          vms.curmsg = 0;
09356          /* Fall through */
09357       case '5': /* Play current message */
09358          cmd = vm_browse_messages(chan, &vms, vmu);
09359          break;
09360       case '2': /* Change folders */
09361          if (useadsi)
09362             adsi_folders(chan, 0, "Change to folder...");
09363          cmd = get_folder2(chan, "vm-changeto", 0);
09364          if (cmd == '#') {
09365             cmd = 0;
09366          } else if (cmd > 0) {
09367             cmd = cmd - '0';
09368             res = close_mailbox(&vms, vmu);
09369             if (res == ERROR_LOCK_PATH)
09370                goto out;
09371             /* If folder is not urgent, set in_urgent to zero! */
09372             if (cmd != 11) in_urgent = 0;
09373             res = open_mailbox(&vms, vmu, cmd);
09374             if (res == ERROR_LOCK_PATH)
09375                goto out;
09376             play_folder = cmd;
09377             cmd = 0;
09378          }
09379          if (useadsi)
09380             adsi_status2(chan, &vms);
09381             
09382          if (!cmd)
09383             cmd = vm_play_folder_name(chan, vms.vmbox);
09384 
09385          vms.starting = 1;
09386          break;
09387       case '3': /* Advanced options */
09388          cmd = 0;
09389          vms.repeats = 0;
09390          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09391             switch (cmd) {
09392             case '1': /* Reply */
09393                if (vms.lastmsg > -1 && !vms.starting) {
09394                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09395                   if (cmd == ERROR_LOCK_PATH) {
09396                      res = cmd;
09397                      goto out;
09398                   }
09399                } else
09400                   cmd = ast_play_and_wait(chan, "vm-sorry");
09401                cmd = 't';
09402                break;
09403             case '2': /* Callback */
09404                if (!vms.starting)
09405                   ast_verb(3, "Callback Requested\n");
09406                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09407                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09408                   if (cmd == 9) {
09409                      silentexit = 1;
09410                      goto out;
09411                   } else if (cmd == ERROR_LOCK_PATH) {
09412                      res = cmd;
09413                      goto out;
09414                   }
09415                } else 
09416                   cmd = ast_play_and_wait(chan, "vm-sorry");
09417                cmd = 't';
09418                break;
09419             case '3': /* Envelope */
09420                if (vms.lastmsg > -1 && !vms.starting) {
09421                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09422                   if (cmd == ERROR_LOCK_PATH) {
09423                      res = cmd;
09424                      goto out;
09425                   }
09426                } else
09427                   cmd = ast_play_and_wait(chan, "vm-sorry");
09428                cmd = 't';
09429                break;
09430             case '4': /* Dialout */
09431                if (!ast_strlen_zero(vmu->dialout)) {
09432                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09433                   if (cmd == 9) {
09434                      silentexit = 1;
09435                      goto out;
09436                   }
09437                } else 
09438                   cmd = ast_play_and_wait(chan, "vm-sorry");
09439                cmd = 't';
09440                break;
09441 
09442             case '5': /* Leave VoiceMail */
09443                if (ast_test_flag(vmu, VM_SVMAIL)) {
09444                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09445                   if (cmd == ERROR_LOCK_PATH) {
09446                      res = cmd;
09447                      ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
09448                      goto out;
09449                   }
09450                } else
09451                   cmd = ast_play_and_wait(chan,"vm-sorry");
09452                cmd='t';
09453                break;
09454                
09455             case '*': /* Return to main menu */
09456                cmd = 't';
09457                break;
09458 
09459             default:
09460                cmd = 0;
09461                if (!vms.starting) {
09462                   cmd = ast_play_and_wait(chan, "vm-toreply");
09463                }
09464                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09465                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09466                }
09467                if (!cmd && !vms.starting) {
09468                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09469                }
09470                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09471                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09472                }
09473                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09474                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09475                if (!cmd)
09476                   cmd = ast_play_and_wait(chan, "vm-starmain");
09477                if (!cmd)
09478                   cmd = ast_waitfordigit(chan,6000);
09479                if (!cmd)
09480                   vms.repeats++;
09481                if (vms.repeats > 3)
09482                   cmd = 't';
09483             }
09484          }
09485          if (cmd == 't') {
09486             cmd = 0;
09487             vms.repeats = 0;
09488          }
09489          break;
09490       case '4': /* Go to the previous message */
09491          if (vms.curmsg > 0) {
09492             vms.curmsg--;
09493             cmd = play_message(chan, vmu, &vms);
09494          } else {
09495             /* Check if we were listening to new
09496                messages.  If so, go to Urgent messages
09497                instead of saying "no more messages"
09498             */
09499             if (in_urgent == 0 && vms.urgentmessages > 0) {
09500                /* Check for Urgent messages */
09501                in_urgent = 1;
09502                res = close_mailbox(&vms, vmu);
09503                if (res == ERROR_LOCK_PATH)
09504                   goto out;
09505                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09506                if (res == ERROR_LOCK_PATH)
09507                   goto out;
09508                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09509                vms.curmsg = vms.lastmsg;
09510                if (vms.lastmsg < 0)
09511                   cmd = ast_play_and_wait(chan, "vm-nomore");
09512             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09513                vms.curmsg = vms.lastmsg;
09514                cmd = play_message(chan, vmu, &vms);
09515             } else {
09516                cmd = ast_play_and_wait(chan, "vm-nomore");
09517             }
09518          }
09519          break;
09520       case '6': /* Go to the next message */
09521          if (vms.curmsg < vms.lastmsg) {
09522             vms.curmsg++;
09523             cmd = play_message(chan, vmu, &vms);
09524          } else {
09525             if (in_urgent && vms.newmessages > 0) {
09526                /* Check if we were listening to urgent
09527                 * messages.  If so, go to regular new messages
09528                 * instead of saying "no more messages"
09529                 */
09530                in_urgent = 0;
09531                res = close_mailbox(&vms, vmu);
09532                if (res == ERROR_LOCK_PATH)
09533                   goto out;
09534                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09535                if (res == ERROR_LOCK_PATH)
09536                   goto out;
09537                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09538                vms.curmsg = -1;
09539                if (vms.lastmsg < 0) {
09540                   cmd = ast_play_and_wait(chan, "vm-nomore");
09541                }
09542             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09543                vms.curmsg = 0;
09544                cmd = play_message(chan, vmu, &vms);
09545             } else {
09546                cmd = ast_play_and_wait(chan, "vm-nomore");
09547             }
09548          }
09549          break;
09550       case '7': /* Delete the current message */
09551          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09552             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09553             if (useadsi)
09554                adsi_delete(chan, &vms);
09555             if (vms.deleted[vms.curmsg]) {
09556                if (play_folder == 0) {
09557                   if (in_urgent) {
09558                      vms.urgentmessages--;
09559                   } else {
09560                      vms.newmessages--;
09561                   }
09562                }
09563                else if (play_folder == 1)
09564                   vms.oldmessages--;
09565                cmd = ast_play_and_wait(chan, "vm-deleted");
09566             } else {
09567                if (play_folder == 0) {
09568                   if (in_urgent) {
09569                      vms.urgentmessages++;
09570                   } else {
09571                      vms.newmessages++;
09572                   }
09573                }
09574                else if (play_folder == 1)
09575                   vms.oldmessages++;
09576                cmd = ast_play_and_wait(chan, "vm-undeleted");
09577             }
09578             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09579                if (vms.curmsg < vms.lastmsg) {
09580                   vms.curmsg++;
09581                   cmd = play_message(chan, vmu, &vms);
09582                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09583                   vms.curmsg = 0;
09584                   cmd = play_message(chan, vmu, &vms);
09585                } else {
09586                   /* Check if we were listening to urgent
09587                      messages.  If so, go to regular new messages
09588                      instead of saying "no more messages"
09589                   */
09590                   if (in_urgent == 1) {
09591                      /* Check for new messages */
09592                      in_urgent = 0;
09593                      res = close_mailbox(&vms, vmu);
09594                      if (res == ERROR_LOCK_PATH)
09595                         goto out;
09596                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09597                      if (res == ERROR_LOCK_PATH)
09598                         goto out;
09599                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09600                      vms.curmsg = -1;
09601                      if (vms.lastmsg < 0)
09602                         cmd = ast_play_and_wait(chan, "vm-nomore");
09603                   } else {
09604                      cmd = ast_play_and_wait(chan, "vm-nomore");
09605                   }
09606                }
09607             }
09608          } else /* Delete not valid if we haven't selected a message */
09609             cmd = 0;
09610 #ifdef IMAP_STORAGE
09611          deleted = 1;
09612 #endif
09613          break;
09614    
09615       case '8': /* Forward the current messgae */
09616          if (vms.lastmsg > -1) {
09617             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09618             if (cmd == ERROR_LOCK_PATH) {
09619                res = cmd;
09620                goto out;
09621             }
09622          } else {
09623             /* Check if we were listening to urgent
09624                messages.  If so, go to regular new messages
09625                instead of saying "no more messages"
09626             */
09627             if (in_urgent == 1 && vms.newmessages > 0) {
09628                /* Check for new messages */
09629                in_urgent = 0;
09630                res = close_mailbox(&vms, vmu);
09631                if (res == ERROR_LOCK_PATH)
09632                   goto out;
09633                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09634                if (res == ERROR_LOCK_PATH)
09635                   goto out;
09636                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09637                vms.curmsg = -1;
09638                if (vms.lastmsg < 0)
09639                   cmd = ast_play_and_wait(chan, "vm-nomore");
09640             } else {
09641                cmd = ast_play_and_wait(chan, "vm-nomore");
09642             }
09643          }
09644          break;
09645       case '9': /* Save message to folder */
09646          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09647             /* No message selected */
09648             cmd = 0;
09649             break;
09650          }
09651          if (useadsi)
09652             adsi_folders(chan, 1, "Save to folder...");
09653          cmd = get_folder2(chan, "vm-savefolder", 1);
09654          box = 0; /* Shut up compiler */
09655          if (cmd == '#') {
09656             cmd = 0;
09657             break;
09658          } else if (cmd > 0) {
09659             box = cmd = cmd - '0';
09660             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09661             if (cmd == ERROR_LOCK_PATH) {
09662                res = cmd;
09663                goto out;
09664 #ifndef IMAP_STORAGE
09665             } else if (!cmd) {
09666                vms.deleted[vms.curmsg] = 1;
09667 #endif
09668             } else {
09669                vms.deleted[vms.curmsg] = 0;
09670                vms.heard[vms.curmsg] = 0;
09671             }
09672          }
09673          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09674          if (useadsi)
09675             adsi_message(chan, &vms);
09676          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09677          if (!cmd) {
09678             cmd = ast_play_and_wait(chan, "vm-message");
09679             if (!cmd) 
09680                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09681             if (!cmd)
09682                cmd = ast_play_and_wait(chan, "vm-savedto");
09683             if (!cmd)
09684                cmd = vm_play_folder_name(chan, vms.fn);
09685          } else {
09686             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09687          }
09688          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09689             if (vms.curmsg < vms.lastmsg) {
09690                vms.curmsg++;
09691                cmd = play_message(chan, vmu, &vms);
09692             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09693                vms.curmsg = 0;
09694                cmd = play_message(chan, vmu, &vms);
09695             } else {
09696                /* Check if we were listening to urgent
09697                   messages.  If so, go to regular new messages
09698                   instead of saying "no more messages"
09699                */
09700                if (in_urgent == 1 && vms.newmessages > 0) {
09701                   /* Check for new messages */
09702                   in_urgent = 0;
09703                   res = close_mailbox(&vms, vmu);
09704                   if (res == ERROR_LOCK_PATH)
09705                      goto out;
09706                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09707                   if (res == ERROR_LOCK_PATH)
09708                      goto out;
09709                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09710                   vms.curmsg = -1;
09711                   if (vms.lastmsg < 0)
09712                      cmd = ast_play_and_wait(chan, "vm-nomore");
09713                } else {
09714                   cmd = ast_play_and_wait(chan, "vm-nomore");
09715                }
09716             }
09717          }
09718          break;
09719       case '*': /* Help */
09720          if (!vms.starting) {
09721             cmd = ast_play_and_wait(chan, "vm-onefor");
09722             if (!strncasecmp(chan->language, "he", 2)) {
09723                cmd = ast_play_and_wait(chan, "vm-for");
09724             }
09725             if (!cmd)
09726                cmd = vm_play_folder_name(chan, vms.vmbox);
09727             if (!cmd)
09728                cmd = ast_play_and_wait(chan, "vm-opts");
09729             if (!cmd)
09730                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09731          } else
09732             cmd = 0;
09733          break;
09734       case '0': /* Mailbox options */
09735          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09736          if (useadsi)
09737             adsi_status(chan, &vms);
09738          break;
09739       default: /* Nothing */
09740          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09741          break;
09742       }
09743    }
09744    if ((cmd == 't') || (cmd == '#')) {
09745       /* Timeout */
09746       res = 0;
09747    } else {
09748       /* Hangup */
09749       res = -1;
09750    }
09751 
09752 out:
09753    if (res > -1) {
09754       ast_stopstream(chan);
09755       adsi_goodbye(chan);
09756       if (valid) {
09757          if (silentexit)
09758             res = ast_play_and_wait(chan, "vm-dialout");
09759          else 
09760             res = ast_play_and_wait(chan, "vm-goodbye");
09761          if (res > 0)
09762             res = 0;
09763       }
09764       if (useadsi)
09765          ast_adsi_unload_session(chan);
09766    }
09767    if (vmu)
09768       close_mailbox(&vms, vmu);
09769    if (valid) {
09770       int new = 0, old = 0, urgent = 0;
09771       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09772       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09773       /* Urgent flag not passwd to externnotify here */
09774       run_externnotify(vmu->context, vmu->mailbox, NULL);
09775       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09776       queue_mwi_event(ext_context, urgent, new, old);
09777    }
09778 #ifdef IMAP_STORAGE
09779    /* expunge message - use UID Expunge if supported on IMAP server*/
09780    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09781    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09782       ast_mutex_lock(&vms.lock);
09783 #ifdef HAVE_IMAP_TK2006
09784       if (LEVELUIDPLUS (vms.mailstream)) {
09785          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09786       } else 
09787 #endif
09788          mail_expunge(vms.mailstream);
09789       ast_mutex_unlock(&vms.lock);
09790    }
09791    /*  before we delete the state, we should copy pertinent info
09792     *  back to the persistent model */
09793    if (vmu) {
09794       vmstate_delete(&vms);
09795    }
09796 #endif
09797    if (vmu)
09798       free_user(vmu);
09799    if (vms.deleted)
09800       ast_free(vms.deleted);
09801    if (vms.heard)
09802       ast_free(vms.heard);
09803 
09804 #ifdef IMAP_STORAGE
09805    pthread_setspecific(ts_vmstate.key, NULL);
09806 #endif
09807    return res;
09808 }
09809 
09810 static int vm_exec(struct ast_channel *chan, void *data)
09811 {
09812    int res = 0;
09813    char *tmp;
09814    struct leave_vm_options leave_options;
09815    struct ast_flags flags = { 0 };
09816    char *opts[OPT_ARG_ARRAY_SIZE];
09817    AST_DECLARE_APP_ARGS(args,
09818       AST_APP_ARG(argv0);
09819       AST_APP_ARG(argv1);
09820    );
09821    
09822    memset(&leave_options, 0, sizeof(leave_options));
09823 
09824    if (chan->_state != AST_STATE_UP)
09825       ast_answer(chan);
09826 
09827    if (!ast_strlen_zero(data)) {
09828       tmp = ast_strdupa(data);
09829       AST_STANDARD_APP_ARGS(args, tmp);
09830       if (args.argc == 2) {
09831          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09832             return -1;
09833          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09834          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09835             int gain;
09836 
09837             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09838                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09839                return -1;
09840             } else {
09841                leave_options.record_gain = (signed char) gain;
09842             }
09843          }
09844          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09845             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09846                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09847          }
09848       }
09849    } else {
09850       char temp[256];
09851       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09852       if (res < 0)
09853          return res;
09854       if (ast_strlen_zero(temp))
09855          return 0;
09856       args.argv0 = ast_strdupa(temp);
09857    }
09858 
09859    res = leave_voicemail(chan, args.argv0, &leave_options);
09860 
09861    if (res == ERROR_LOCK_PATH) {
09862       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09863       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09864       res = 0;
09865    }
09866 
09867    return res;
09868 }
09869 
09870 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09871 {
09872    struct ast_vm_user *vmu;
09873 
09874    AST_LIST_TRAVERSE(&users, vmu, list) {
09875       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09876          if (strcasecmp(vmu->context, context)) {
09877             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09878                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09879                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09880                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09881          }
09882          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09883          return NULL;
09884       }
09885       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09886          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09887          return NULL;
09888       }
09889    }
09890    
09891    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09892       return NULL;
09893    
09894    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09895    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09896 
09897    AST_LIST_INSERT_TAIL(&users, vmu, list);
09898    
09899    return vmu;
09900 }
09901 
09902 static int append_mailbox(const char *context, const char *box, const char *data)
09903 {
09904    /* Assumes lock is already held */
09905    char *tmp;
09906    char *stringp;
09907    char *s;
09908    struct ast_vm_user *vmu;
09909    char *mailbox_full;
09910    int new = 0, old = 0, urgent = 0;
09911 
09912    tmp = ast_strdupa(data);
09913 
09914    if (!(vmu = find_or_create(context, box)))
09915       return -1;
09916    
09917    populate_defaults(vmu);
09918 
09919    stringp = tmp;
09920    if ((s = strsep(&stringp, ","))) 
09921       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09922    if (stringp && (s = strsep(&stringp, ","))) 
09923       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09924    if (stringp && (s = strsep(&stringp, ","))) 
09925       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09926    if (stringp && (s = strsep(&stringp, ","))) 
09927       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09928    if (stringp && (s = strsep(&stringp, ","))) 
09929       apply_options(vmu, s);
09930 
09931    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09932    strcpy(mailbox_full, box);
09933    strcat(mailbox_full, "@");
09934    strcat(mailbox_full, context);
09935 
09936    inboxcount2(mailbox_full, &urgent, &new, &old);
09937    queue_mwi_event(mailbox_full, urgent, new, old);
09938 
09939    return 0;
09940 }
09941 
09942 static int vm_box_exists(struct ast_channel *chan, void *data) 
09943 {
09944    struct ast_vm_user svm;
09945    char *context, *box;
09946    AST_DECLARE_APP_ARGS(args,
09947       AST_APP_ARG(mbox);
09948       AST_APP_ARG(options);
09949    );
09950    static int dep_warning = 0;
09951 
09952    if (ast_strlen_zero(data)) {
09953       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09954       return -1;
09955    }
09956 
09957    if (!dep_warning) {
09958       dep_warning = 1;
09959       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
09960    }
09961 
09962    box = ast_strdupa(data);
09963 
09964    AST_STANDARD_APP_ARGS(args, box);
09965 
09966    if (args.options) {
09967    }
09968 
09969    if ((context = strchr(args.mbox, '@'))) {
09970       *context = '\0';
09971       context++;
09972    }
09973 
09974    if (find_user(&svm, context, args.mbox)) {
09975       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
09976    } else
09977       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
09978 
09979    return 0;
09980 }
09981 
09982 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
09983 {
09984    struct ast_vm_user svm;
09985    AST_DECLARE_APP_ARGS(arg,
09986       AST_APP_ARG(mbox);
09987       AST_APP_ARG(context);
09988    );
09989 
09990    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
09991 
09992    if (ast_strlen_zero(arg.mbox)) {
09993       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
09994       return -1;
09995    }
09996 
09997    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
09998    return 0;
09999 }
10000 
10001 static struct ast_custom_function mailbox_exists_acf = {
10002    .name = "MAILBOX_EXISTS",
10003    .read = acf_mailbox_exists,
10004 };
10005 
10006 static int vmauthenticate(struct ast_channel *chan, void *data)
10007 {
10008    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
10009    struct ast_vm_user vmus;
10010    char *options = NULL;
10011    int silent = 0, skipuser = 0;
10012    int res = -1;
10013    
10014    if (s) {
10015       s = ast_strdupa(s);
10016       user = strsep(&s, ",");
10017       options = strsep(&s, ",");
10018       if (user) {
10019          s = user;
10020          user = strsep(&s, "@");
10021          context = strsep(&s, "");
10022          if (!ast_strlen_zero(user))
10023             skipuser++;
10024          ast_copy_string(mailbox, user, sizeof(mailbox));
10025       }
10026    }
10027 
10028    if (options) {
10029       silent = (strchr(options, 's')) != NULL;
10030    }
10031 
10032    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10033       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10034       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10035       ast_play_and_wait(chan, "auth-thankyou");
10036       res = 0;
10037    }
10038 
10039    return res;
10040 }
10041 
10042 static char *show_users_realtime(int fd, const char *context)
10043 {
10044    struct ast_config *cfg;
10045    const char *cat = NULL;
10046 
10047    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10048       "context", context, SENTINEL))) {
10049       return CLI_FAILURE;
10050    }
10051 
10052    ast_cli(fd,
10053       "\n"
10054       "=============================================================\n"
10055       "=== Configured Voicemail Users ==============================\n"
10056       "=============================================================\n"
10057       "===\n");
10058 
10059    while ((cat = ast_category_browse(cfg, cat))) {
10060       struct ast_variable *var = NULL;
10061       ast_cli(fd,
10062          "=== Mailbox ...\n"
10063          "===\n");
10064       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10065          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10066       ast_cli(fd,
10067          "===\n"
10068          "=== ---------------------------------------------------------\n"
10069          "===\n");
10070    }
10071 
10072    ast_cli(fd,
10073       "=============================================================\n"
10074       "\n");
10075 
10076    ast_config_destroy(cfg);
10077 
10078    return CLI_SUCCESS;
10079 }
10080 
10081 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10082 {
10083    int which = 0;
10084    int wordlen;
10085    struct ast_vm_user *vmu;
10086    const char *context = "";
10087 
10088    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10089    if (pos > 4)
10090       return NULL;
10091    if (pos == 3)
10092       return (state == 0) ? ast_strdup("for") : NULL;
10093    wordlen = strlen(word);
10094    AST_LIST_TRAVERSE(&users, vmu, list) {
10095       if (!strncasecmp(word, vmu->context, wordlen)) {
10096          if (context && strcmp(context, vmu->context) && ++which > state)
10097             return ast_strdup(vmu->context);
10098          /* ignore repeated contexts ? */
10099          context = vmu->context;
10100       }
10101    }
10102    return NULL;
10103 }
10104 
10105 /*! \brief Show a list of voicemail users in the CLI */
10106 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10107 {
10108    struct ast_vm_user *vmu;
10109 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10110    const char *context = NULL;
10111    int users_counter = 0;
10112 
10113    switch (cmd) {
10114    case CLI_INIT:
10115       e->command = "voicemail show users";
10116       e->usage =
10117          "Usage: voicemail show users [for <context>]\n"
10118          "       Lists all mailboxes currently set up\n";
10119       return NULL;
10120    case CLI_GENERATE:
10121       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10122    }  
10123 
10124    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10125       return CLI_SHOWUSAGE;
10126    if (a->argc == 5) {
10127       if (strcmp(a->argv[3],"for"))
10128          return CLI_SHOWUSAGE;
10129       context = a->argv[4];
10130    }
10131 
10132    if (ast_check_realtime("voicemail")) {
10133       if (!context) {
10134          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10135          return CLI_SHOWUSAGE;
10136       }
10137       return show_users_realtime(a->fd, context);
10138    }
10139 
10140    AST_LIST_LOCK(&users);
10141    if (AST_LIST_EMPTY(&users)) {
10142       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10143       AST_LIST_UNLOCK(&users);
10144       return CLI_FAILURE;
10145    }
10146    if (a->argc == 3)
10147       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10148    else {
10149       int count = 0;
10150       AST_LIST_TRAVERSE(&users, vmu, list) {
10151          if (!strcmp(context, vmu->context))
10152             count++;
10153       }
10154       if (count) {
10155          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10156       } else {
10157          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10158          AST_LIST_UNLOCK(&users);
10159          return CLI_FAILURE;
10160       }
10161    }
10162    AST_LIST_TRAVERSE(&users, vmu, list) {
10163       int newmsgs = 0, oldmsgs = 0;
10164       char count[12], tmp[256] = "";
10165 
10166       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10167          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10168          inboxcount(tmp, &newmsgs, &oldmsgs);
10169          snprintf(count, sizeof(count), "%d", newmsgs);
10170          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10171          users_counter++;
10172       }
10173    }
10174    AST_LIST_UNLOCK(&users);
10175    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10176    return CLI_SUCCESS;
10177 }
10178 
10179 /*! \brief Show a list of voicemail zones in the CLI */
10180 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10181 {
10182    struct vm_zone *zone;
10183 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10184    char *res = CLI_SUCCESS;
10185 
10186    switch (cmd) {
10187    case CLI_INIT:
10188       e->command = "voicemail show zones";
10189       e->usage =
10190          "Usage: voicemail show zones\n"
10191          "       Lists zone message formats\n";
10192       return NULL;
10193    case CLI_GENERATE:
10194       return NULL;
10195    }
10196 
10197    if (a->argc != 3)
10198       return CLI_SHOWUSAGE;
10199 
10200    AST_LIST_LOCK(&zones);
10201    if (!AST_LIST_EMPTY(&zones)) {
10202       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10203       AST_LIST_TRAVERSE(&zones, zone, list) {
10204          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10205       }
10206    } else {
10207       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10208       res = CLI_FAILURE;
10209    }
10210    AST_LIST_UNLOCK(&zones);
10211 
10212    return res;
10213 }
10214 
10215 /*! \brief Reload voicemail configuration from the CLI */
10216 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10217 {
10218    switch (cmd) {
10219    case CLI_INIT:
10220       e->command = "voicemail reload";
10221       e->usage =
10222          "Usage: voicemail reload\n"
10223          "       Reload voicemail configuration\n";
10224       return NULL;
10225    case CLI_GENERATE:
10226       return NULL;
10227    }
10228 
10229    if (a->argc != 2)
10230       return CLI_SHOWUSAGE;
10231 
10232    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10233    load_config(1);
10234    
10235    return CLI_SUCCESS;
10236 }
10237 
10238 static struct ast_cli_entry cli_voicemail[] = {
10239    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10240    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10241    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10242 };
10243 
10244 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10245 {
10246    int new = 0, old = 0, urgent = 0;
10247 
10248    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10249 
10250    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10251       mwi_sub->old_urgent = urgent;
10252       mwi_sub->old_new = new;
10253       mwi_sub->old_old = old;
10254       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10255    }
10256 }
10257 
10258 static void poll_subscribed_mailboxes(void)
10259 {
10260    struct mwi_sub *mwi_sub;
10261 
10262    AST_RWLIST_RDLOCK(&mwi_subs);
10263    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10264       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10265          poll_subscribed_mailbox(mwi_sub);
10266       }
10267    }
10268    AST_RWLIST_UNLOCK(&mwi_subs);
10269 }
10270 
10271 static void *mb_poll_thread(void *data)
10272 {
10273    while (poll_thread_run) {
10274       struct timespec ts = { 0, };
10275       struct timeval wait;
10276 
10277       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10278       ts.tv_sec = wait.tv_sec;
10279       ts.tv_nsec = wait.tv_usec * 1000;
10280 
10281       ast_mutex_lock(&poll_lock);
10282       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10283       ast_mutex_unlock(&poll_lock);
10284 
10285       if (!poll_thread_run)
10286          break;
10287 
10288       poll_subscribed_mailboxes();
10289    }
10290 
10291    return NULL;
10292 }
10293 
10294 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10295 {
10296    ast_free(mwi_sub);
10297 }
10298 
10299 static int handle_unsubscribe(void *datap)
10300 {
10301    struct mwi_sub *mwi_sub;
10302    uint32_t *uniqueid = datap;
10303    
10304    AST_RWLIST_WRLOCK(&mwi_subs);
10305    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10306       if (mwi_sub->uniqueid == *uniqueid) {
10307          AST_LIST_REMOVE_CURRENT(entry);
10308          break;
10309       }
10310    }
10311    AST_RWLIST_TRAVERSE_SAFE_END
10312    AST_RWLIST_UNLOCK(&mwi_subs);
10313 
10314    if (mwi_sub)
10315       mwi_sub_destroy(mwi_sub);
10316 
10317    ast_free(uniqueid);  
10318    return 0;
10319 }
10320 
10321 static int handle_subscribe(void *datap)
10322 {
10323    unsigned int len;
10324    struct mwi_sub *mwi_sub;
10325    struct mwi_sub_task *p = datap;
10326 
10327    len = sizeof(*mwi_sub);
10328    if (!ast_strlen_zero(p->mailbox))
10329       len += strlen(p->mailbox);
10330 
10331    if (!ast_strlen_zero(p->context))
10332       len += strlen(p->context) + 1; /* Allow for seperator */
10333 
10334    if (!(mwi_sub = ast_calloc(1, len)))
10335       return -1;
10336 
10337    mwi_sub->uniqueid = p->uniqueid;
10338    if (!ast_strlen_zero(p->mailbox))
10339       strcpy(mwi_sub->mailbox, p->mailbox);
10340 
10341    if (!ast_strlen_zero(p->context)) {
10342       strcat(mwi_sub->mailbox, "@");
10343       strcat(mwi_sub->mailbox, p->context);
10344    }
10345 
10346    AST_RWLIST_WRLOCK(&mwi_subs);
10347    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10348    AST_RWLIST_UNLOCK(&mwi_subs);
10349    ast_free((void *) p->mailbox);
10350    ast_free((void *) p->context);
10351    ast_free(p);
10352    poll_subscribed_mailbox(mwi_sub);
10353    return 0;
10354 }
10355 
10356 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10357 {
10358    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10359    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10360       return;
10361 
10362    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10363       return;
10364 
10365    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10366    *uniqueid = u;
10367    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10368       ast_free(uniqueid);
10369    }
10370 }
10371 
10372 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10373 {
10374    struct mwi_sub_task *mwist;
10375    
10376    if (ast_event_get_type(event) != AST_EVENT_SUB)
10377       return;
10378 
10379    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10380       return;
10381 
10382    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10383       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10384       return;
10385    }
10386    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10387    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10388    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10389    
10390    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10391       ast_free(mwist);
10392    }
10393 }
10394 
10395 static void start_poll_thread(void)
10396 {
10397    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10398       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10399       AST_EVENT_IE_END);
10400 
10401    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10402       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10403       AST_EVENT_IE_END);
10404 
10405    if (mwi_sub_sub)
10406       ast_event_report_subs(mwi_sub_sub);
10407 
10408    poll_thread_run = 1;
10409 
10410    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10411 }
10412 
10413 static void stop_poll_thread(void)
10414 {
10415    poll_thread_run = 0;
10416 
10417    if (mwi_sub_sub) {
10418       ast_event_unsubscribe(mwi_sub_sub);
10419       mwi_sub_sub = NULL;
10420    }
10421 
10422    if (mwi_unsub_sub) {
10423       ast_event_unsubscribe(mwi_unsub_sub);
10424       mwi_unsub_sub = NULL;
10425    }
10426 
10427    ast_mutex_lock(&poll_lock);
10428    ast_cond_signal(&poll_cond);
10429    ast_mutex_unlock(&poll_lock);
10430 
10431    pthread_join(poll_thread, NULL);
10432 
10433    poll_thread = AST_PTHREADT_NULL;
10434 }
10435 
10436 /*! \brief Manager list voicemail users command */
10437 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10438 {
10439    struct ast_vm_user *vmu = NULL;
10440    const char *id = astman_get_header(m, "ActionID");
10441    char actionid[128] = "";
10442 
10443    if (!ast_strlen_zero(id))
10444       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10445 
10446    AST_LIST_LOCK(&users);
10447 
10448    if (AST_LIST_EMPTY(&users)) {
10449       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10450       AST_LIST_UNLOCK(&users);
10451       return RESULT_SUCCESS;
10452    }
10453    
10454    astman_send_ack(s, m, "Voicemail user list will follow");
10455    
10456    AST_LIST_TRAVERSE(&users, vmu, list) {
10457       char dirname[256];
10458 
10459 #ifdef IMAP_STORAGE
10460       int new, old;
10461       inboxcount(vmu->mailbox, &new, &old);
10462 #endif
10463       
10464       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10465       astman_append(s,
10466          "%s"
10467          "Event: VoicemailUserEntry\r\n"
10468          "VMContext: %s\r\n"
10469          "VoiceMailbox: %s\r\n"
10470          "Fullname: %s\r\n"
10471          "Email: %s\r\n"
10472          "Pager: %s\r\n"
10473          "ServerEmail: %s\r\n"
10474          "MailCommand: %s\r\n"
10475          "Language: %s\r\n"
10476          "TimeZone: %s\r\n"
10477          "Callback: %s\r\n"
10478          "Dialout: %s\r\n"
10479          "UniqueID: %s\r\n"
10480          "ExitContext: %s\r\n"
10481          "SayDurationMinimum: %d\r\n"
10482          "SayEnvelope: %s\r\n"
10483          "SayCID: %s\r\n"
10484          "AttachMessage: %s\r\n"
10485          "AttachmentFormat: %s\r\n"
10486          "DeleteMessage: %s\r\n"
10487          "VolumeGain: %.2f\r\n"
10488          "CanReview: %s\r\n"
10489          "CallOperator: %s\r\n"
10490          "MaxMessageCount: %d\r\n"
10491          "MaxMessageLength: %d\r\n"
10492          "NewMessageCount: %d\r\n"
10493 #ifdef IMAP_STORAGE
10494          "OldMessageCount: %d\r\n"
10495          "IMAPUser: %s\r\n"
10496 #endif
10497          "\r\n",
10498          actionid,
10499          vmu->context,
10500          vmu->mailbox,
10501          vmu->fullname,
10502          vmu->email,
10503          vmu->pager,
10504          vmu->serveremail,
10505          vmu->mailcmd,
10506          vmu->language,
10507          vmu->zonetag,
10508          vmu->callback,
10509          vmu->dialout,
10510          vmu->uniqueid,
10511          vmu->exit,
10512          vmu->saydurationm,
10513          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10514          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10515          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10516          vmu->attachfmt,
10517          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10518          vmu->volgain,
10519          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10520          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10521          vmu->maxmsg,
10522          vmu->maxsecs,
10523 #ifdef IMAP_STORAGE
10524          new, old, vmu->imapuser
10525 #else
10526          count_messages(vmu, dirname)
10527 #endif
10528          );
10529    }     
10530    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10531 
10532    AST_LIST_UNLOCK(&users);
10533 
10534    return RESULT_SUCCESS;
10535 }
10536 
10537 /*! \brief Free the users structure. */
10538 static void free_vm_users(void) 
10539 {
10540    struct ast_vm_user *current;
10541    AST_LIST_LOCK(&users);
10542    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10543       ast_set_flag(current, VM_ALLOCED);
10544       free_user(current);
10545    }
10546    AST_LIST_UNLOCK(&users);
10547 }
10548 
10549 /*! \brief Free the zones structure. */
10550 static void free_vm_zones(void)
10551 {
10552    struct vm_zone *zcur;
10553    AST_LIST_LOCK(&zones);
10554    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10555       free_zone(zcur);
10556    AST_LIST_UNLOCK(&zones);
10557 }
10558 
10559 static const char *substitute_escapes(const char *value)
10560 {
10561    char *current;
10562 
10563    /* Add 16 for fudge factor */
10564    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10565 
10566    ast_str_reset(str);
10567    
10568    /* Substitute strings \r, \n, and \t into the appropriate characters */
10569    for (current = (char *) value; *current; current++) {
10570       if (*current == '\\') {
10571          current++;
10572          if (!*current) {
10573             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10574             break;
10575          }
10576          switch (*current) {
10577          case 'r':
10578             ast_str_append(&str, 0, "\r");
10579             break;
10580          case 'n':
10581 #ifdef IMAP_STORAGE
10582             if (!str->used || str->str[str->used - 1] != '\r') {
10583                ast_str_append(&str, 0, "\r");
10584             }
10585 #endif
10586             ast_str_append(&str, 0, "\n");
10587             break;
10588          case 't':
10589             ast_str_append(&str, 0, "\t");
10590             break;
10591          default:
10592             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10593             break;
10594          }
10595       } else {
10596          ast_str_append(&str, 0, "%c", *current);
10597       }
10598    }
10599 
10600    return ast_str_buffer(str);
10601 }
10602 
10603 static int load_config(int reload)
10604 {
10605    struct ast_vm_user *current;
10606    struct ast_config *cfg, *ucfg;
10607    char *cat;
10608    struct ast_variable *var;
10609    const char *val;
10610    char *q, *stringp, *tmp;
10611    int x;
10612    int tmpadsi[4];
10613    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10614 
10615    ast_unload_realtime("voicemail");
10616    ast_unload_realtime("voicemail_data");
10617 
10618    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10619       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10620          return 0;
10621       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10622          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10623          ucfg = NULL;
10624       }
10625       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10626       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10627          ast_config_destroy(ucfg);
10628          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10629          return 0;
10630       }
10631    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10632       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
10633       return 0;
10634    } else {
10635       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10636       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10637          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
10638          ucfg = NULL;
10639       }
10640    }
10641 #ifdef IMAP_STORAGE
10642    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10643 #endif
10644    /* set audio control prompts */
10645    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10646    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10647    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10648    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10649    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10650 
10651    /* Free all the users structure */  
10652    free_vm_users();
10653 
10654    /* Free all the zones structure */
10655    free_vm_zones();
10656 
10657    AST_LIST_LOCK(&users);  
10658 
10659    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10660    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10661 
10662    if (cfg) {
10663       /* General settings */
10664 
10665       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10666          val = "default";
10667       ast_copy_string(userscontext, val, sizeof(userscontext));
10668       /* Attach voice message to mail message ? */
10669       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10670          val = "yes";
10671       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10672 
10673       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10674          val = "no";
10675       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10676 
10677       volgain = 0.0;
10678       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10679          sscanf(val, "%30lf", &volgain);
10680 
10681 #ifdef ODBC_STORAGE
10682       strcpy(odbc_database, "asterisk");
10683       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10684          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10685       }
10686       strcpy(odbc_table, "voicemessages");
10687       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10688          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10689       }
10690 #endif      
10691       /* Mail command */
10692       strcpy(mailcmd, SENDMAIL);
10693       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10694          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10695 
10696       maxsilence = 0;
10697       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10698          maxsilence = atoi(val);
10699          if (maxsilence > 0)
10700             maxsilence *= 1000;
10701       }
10702       
10703       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10704          maxmsg = MAXMSG;
10705       } else {
10706          maxmsg = atoi(val);
10707          if (maxmsg <= 0) {
10708             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10709             maxmsg = MAXMSG;
10710          } else if (maxmsg > MAXMSGLIMIT) {
10711             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10712             maxmsg = MAXMSGLIMIT;
10713          }
10714       }
10715 
10716       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10717          maxdeletedmsg = 0;
10718       } else {
10719          if (sscanf(val, "%30d", &x) == 1)
10720             maxdeletedmsg = x;
10721          else if (ast_true(val))
10722             maxdeletedmsg = MAXMSG;
10723          else
10724             maxdeletedmsg = 0;
10725 
10726          if (maxdeletedmsg < 0) {
10727             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10728             maxdeletedmsg = MAXMSG;
10729          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10730             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10731             maxdeletedmsg = MAXMSGLIMIT;
10732          }
10733       }
10734 
10735       /* Load date format config for voicemail mail */
10736       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10737          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10738       }
10739 
10740       /* External password changing command */
10741       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10742          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10743          pwdchange = PWDCHANGE_EXTERNAL;
10744       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10745          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10746          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10747       }
10748  
10749       /* External password validation command */
10750       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10751          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10752          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10753       }
10754 
10755 #ifdef IMAP_STORAGE
10756       /* IMAP server address */
10757       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10758          ast_copy_string(imapserver, val, sizeof(imapserver));
10759       } else {
10760          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10761       }
10762       /* IMAP server port */
10763       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10764          ast_copy_string(imapport, val, sizeof(imapport));
10765       } else {
10766          ast_copy_string(imapport,"143", sizeof(imapport));
10767       }
10768       /* IMAP server flags */
10769       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10770          ast_copy_string(imapflags, val, sizeof(imapflags));
10771       }
10772       /* IMAP server master username */
10773       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10774          ast_copy_string(authuser, val, sizeof(authuser));
10775       }
10776       /* IMAP server master password */
10777       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10778          ast_copy_string(authpassword, val, sizeof(authpassword));
10779       }
10780       /* Expunge on exit */
10781       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10782          if (ast_false(val))
10783             expungeonhangup = 0;
10784          else
10785             expungeonhangup = 1;
10786       } else {
10787          expungeonhangup = 1;
10788       }
10789       /* IMAP voicemail folder */
10790       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10791          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10792       } else {
10793          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10794       }
10795       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10796          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10797       }
10798       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10799          imapgreetings = ast_true(val);
10800       } else {
10801          imapgreetings = 0;
10802       }
10803       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10804          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10805       } else {
10806          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10807       }
10808 
10809       /* There is some very unorthodox casting done here. This is due
10810        * to the way c-client handles the argument passed in. It expects a 
10811        * void pointer and casts the pointer directly to a long without
10812        * first dereferencing it. */
10813       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10814          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10815       } else {
10816          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10817       }
10818 
10819       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10820          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10821       } else {
10822          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10823       }
10824 
10825       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10826          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10827       } else {
10828          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10829       }
10830 
10831       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10832          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10833       } else {
10834          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10835       }
10836 
10837       /* Increment configuration version */
10838       imapversion++;
10839 #endif
10840       /* External voicemail notify application */
10841       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10842          ast_copy_string(externnotify, val, sizeof(externnotify));
10843          ast_debug(1, "found externnotify: %s\n", externnotify);
10844       } else {
10845          externnotify[0] = '\0';
10846       }
10847 
10848       /* SMDI voicemail notification */
10849       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10850          ast_debug(1, "Enabled SMDI voicemail notification\n");
10851          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10852             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10853          } else {
10854             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10855             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10856          }
10857          if (!smdi_iface) {
10858             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10859          } 
10860       }
10861 
10862       /* Silence treshold */
10863       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10864       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10865          silencethreshold = atoi(val);
10866       
10867       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10868          val = ASTERISK_USERNAME;
10869       ast_copy_string(serveremail, val, sizeof(serveremail));
10870       
10871       vmmaxsecs = 0;
10872       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10873          if (sscanf(val, "%30d", &x) == 1) {
10874             vmmaxsecs = x;
10875          } else {
10876             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10877          }
10878       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10879          static int maxmessage_deprecate = 0;
10880          if (maxmessage_deprecate == 0) {
10881             maxmessage_deprecate = 1;
10882             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10883          }
10884          if (sscanf(val, "%30d", &x) == 1) {
10885             vmmaxsecs = x;
10886          } else {
10887             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10888          }
10889       }
10890 
10891       vmminsecs = 0;
10892       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10893          if (sscanf(val, "%30d", &x) == 1) {
10894             vmminsecs = x;
10895             if (maxsilence / 1000 >= vmminsecs) {
10896                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10897             }
10898          } else {
10899             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10900          }
10901       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10902          static int maxmessage_deprecate = 0;
10903          if (maxmessage_deprecate == 0) {
10904             maxmessage_deprecate = 1;
10905             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10906          }
10907          if (sscanf(val, "%30d", &x) == 1) {
10908             vmminsecs = x;
10909             if (maxsilence / 1000 >= vmminsecs) {
10910                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10911             }
10912          } else {
10913             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10914          }
10915       }
10916 
10917       val = ast_variable_retrieve(cfg, "general", "format");
10918       if (!val) {
10919          val = "wav";   
10920       } else {
10921          tmp = ast_strdupa(val);
10922          val = ast_format_str_reduce(tmp);
10923          if (!val) {
10924             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10925             val = "wav";
10926          }
10927       }
10928       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10929 
10930       skipms = 3000;
10931       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10932          if (sscanf(val, "%30d", &x) == 1) {
10933             maxgreet = x;
10934          } else {
10935             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10936          }
10937       }
10938 
10939       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10940          if (sscanf(val, "%30d", &x) == 1) {
10941             skipms = x;
10942          } else {
10943             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10944          }
10945       }
10946 
10947       maxlogins = 3;
10948       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10949          if (sscanf(val, "%30d", &x) == 1) {
10950             maxlogins = x;
10951          } else {
10952             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10953          }
10954       }
10955 
10956       minpassword = MINPASSWORD;
10957       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10958          if (sscanf(val, "%30d", &x) == 1) {
10959             minpassword = x;
10960          } else {
10961             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
10962          }
10963       }
10964 
10965       /* Force new user to record name ? */
10966       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
10967          val = "no";
10968       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10969 
10970       /* Force new user to record greetings ? */
10971       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
10972          val = "no";
10973       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10974 
10975       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10976          ast_debug(1, "VM_CID Internal context string: %s\n", val);
10977          stringp = ast_strdupa(val);
10978          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10979             if (!ast_strlen_zero(stringp)) {
10980                q = strsep(&stringp, ",");
10981                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10982                   q++;
10983                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10984                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10985             } else {
10986                cidinternalcontexts[x][0] = '\0';
10987             }
10988          }
10989       }
10990       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10991          ast_debug(1,"VM Review Option disabled globally\n");
10992          val = "no";
10993       }
10994       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
10995 
10996       /* Temporary greeting reminder */
10997       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10998          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10999          val = "no";
11000       } else {
11001          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11002       }
11003       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11004       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11005          ast_debug(1, "VM next message wrap disabled globally\n");
11006          val = "no";
11007       }
11008       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11009 
11010       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11011          ast_debug(1,"VM Operator break disabled globally\n");
11012          val = "no";
11013       }
11014       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11015 
11016       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11017          ast_debug(1,"VM CID Info before msg disabled globally\n");
11018          val = "no";
11019       } 
11020       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11021 
11022       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
11023          ast_debug(1,"Send Voicemail msg disabled globally\n");
11024          val = "no";
11025       }
11026       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11027    
11028       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11029          ast_debug(1,"ENVELOPE before msg enabled globally\n");
11030          val = "yes";
11031       }
11032       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11033 
11034       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11035          ast_debug(1,"Move Heard enabled globally\n");
11036          val = "yes";
11037       }
11038       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11039 
11040       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11041          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11042          val = "no";
11043       }
11044       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11045 
11046       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11047          ast_debug(1,"Duration info before msg enabled globally\n");
11048          val = "yes";
11049       }
11050       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11051 
11052       saydurationminfo = 2;
11053       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11054          if (sscanf(val, "%30d", &x) == 1) {
11055             saydurationminfo = x;
11056          } else {
11057             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11058          }
11059       }
11060 
11061       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11062          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
11063          val = "no";
11064       }
11065       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11066 
11067       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11068          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11069          ast_debug(1, "found dialout context: %s\n", dialcontext);
11070       } else {
11071          dialcontext[0] = '\0';  
11072       }
11073       
11074       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11075          ast_copy_string(callcontext, val, sizeof(callcontext));
11076          ast_debug(1, "found callback context: %s\n", callcontext);
11077       } else {
11078          callcontext[0] = '\0';
11079       }
11080 
11081       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11082          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11083          ast_debug(1, "found operator context: %s\n", exitcontext);
11084       } else {
11085          exitcontext[0] = '\0';
11086       }
11087       
11088       /* load password sounds configuration */
11089       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11090          ast_copy_string(vm_password, val, sizeof(vm_password));
11091       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11092          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11093       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11094          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11095       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11096          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11097       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11098          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11099       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11100          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11101       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11102          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11103       }
11104       /* load configurable audio prompts */
11105       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11106          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11107       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11108          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11109       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11110          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11111       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11112          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11113       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11114          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11115 
11116       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11117          val = "no";
11118       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11119 
11120       poll_freq = DEFAULT_POLL_FREQ;
11121       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11122          if (sscanf(val, "%30u", &poll_freq) != 1) {
11123             poll_freq = DEFAULT_POLL_FREQ;
11124             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11125          }
11126       }
11127 
11128       poll_mailboxes = 0;
11129       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11130          poll_mailboxes = ast_true(val);
11131 
11132       if (ucfg) { 
11133          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11134             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11135                continue;
11136             if ((current = find_or_create(userscontext, cat))) {
11137                populate_defaults(current);
11138                apply_options_full(current, ast_variable_browse(ucfg, cat));
11139                ast_copy_string(current->context, userscontext, sizeof(current->context));
11140             }
11141          }
11142          ast_config_destroy(ucfg);
11143       }
11144       cat = ast_category_browse(cfg, NULL);
11145       while (cat) {
11146          if (strcasecmp(cat, "general")) {
11147             var = ast_variable_browse(cfg, cat);
11148             if (strcasecmp(cat, "zonemessages")) {
11149                /* Process mailboxes in this context */
11150                while (var) {
11151                   append_mailbox(cat, var->name, var->value);
11152                   var = var->next;
11153                }
11154             } else {
11155                /* Timezones in this context */
11156                while (var) {
11157                   struct vm_zone *z;
11158                   if ((z = ast_malloc(sizeof(*z)))) {
11159                      char *msg_format, *tzone;
11160                      msg_format = ast_strdupa(var->value);
11161                      tzone = strsep(&msg_format, "|");
11162                      if (msg_format) {
11163                         ast_copy_string(z->name, var->name, sizeof(z->name));
11164                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11165                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11166                         AST_LIST_LOCK(&zones);
11167                         AST_LIST_INSERT_HEAD(&zones, z, list);
11168                         AST_LIST_UNLOCK(&zones);
11169                      } else {
11170                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11171                         ast_free(z);
11172                      }
11173                   } else {
11174                      AST_LIST_UNLOCK(&users);
11175                      ast_config_destroy(cfg);
11176                      return -1;
11177                   }
11178                   var = var->next;
11179                }
11180             }
11181          }
11182          cat = ast_category_browse(cfg, cat);
11183       }
11184       memset(fromstring, 0, sizeof(fromstring));
11185       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11186       strcpy(charset, "ISO-8859-1");
11187       if (emailbody) {
11188          ast_free(emailbody);
11189          emailbody = NULL;
11190       }
11191       if (emailsubject) {
11192          ast_free(emailsubject);
11193          emailsubject = NULL;
11194       }
11195       if (pagerbody) {
11196          ast_free(pagerbody);
11197          pagerbody = NULL;
11198       }
11199       if (pagersubject) {
11200          ast_free(pagersubject);
11201          pagersubject = NULL;
11202       }
11203       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11204          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11205       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11206          ast_copy_string(fromstring, val, sizeof(fromstring));
11207       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11208          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11209       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11210          ast_copy_string(charset, val, sizeof(charset));
11211       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11212          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11213          for (x = 0; x < 4; x++) {
11214             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11215          }
11216       }
11217       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11218          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11219          for (x = 0; x < 4; x++) {
11220             memcpy(&adsisec[x], &tmpadsi[x], 1);
11221          }
11222       }
11223       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11224          if (atoi(val)) {
11225             adsiver = atoi(val);
11226          }
11227       }
11228       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11229          ast_copy_string(zonetag, val, sizeof(zonetag));
11230       }
11231       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11232          emailsubject = ast_strdup(val);
11233       }
11234       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11235          emailbody = ast_strdup(substitute_escapes(val));
11236       }
11237       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11238          pagersubject = ast_strdup(val);
11239       }
11240       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11241          pagerbody = ast_strdup(substitute_escapes(val));
11242       }
11243       AST_LIST_UNLOCK(&users);
11244       ast_config_destroy(cfg);
11245 
11246       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11247          start_poll_thread();
11248       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11249          stop_poll_thread();;
11250 
11251       return 0;
11252    } else {
11253       AST_LIST_UNLOCK(&users);
11254       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11255       if (ucfg)
11256          ast_config_destroy(ucfg);
11257       return 0;
11258    }
11259 }
11260 
11261 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11262 {
11263    int res = -1;
11264    char dir[PATH_MAX];
11265    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11266    ast_debug(2, "About to try retrieving name file %s\n", dir);
11267    RETRIEVE(dir, -1, mailbox, context);
11268    if (ast_fileexists(dir, NULL, NULL)) {
11269       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11270    }
11271    DISPOSE(dir, -1);
11272    return res;
11273 }
11274 
11275 static int reload(void)
11276 {
11277    return load_config(1);
11278 }
11279 
11280 static int unload_module(void)
11281 {
11282    int res;
11283 
11284    res = ast_unregister_application(app);
11285    res |= ast_unregister_application(app2);
11286    res |= ast_unregister_application(app3);
11287    res |= ast_unregister_application(app4);
11288    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11289    res |= ast_manager_unregister("VoicemailUsersList");
11290    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11291    ast_uninstall_vm_functions();
11292    ao2_ref(inprocess_container, -1);
11293 
11294    if (poll_thread != AST_PTHREADT_NULL)
11295       stop_poll_thread();
11296 
11297    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11298    ast_unload_realtime("voicemail");
11299    ast_unload_realtime("voicemail_data");
11300 
11301    free_vm_users();
11302    free_vm_zones();
11303    return res;
11304 }
11305 
11306 static int load_module(void)
11307 {
11308    int res;
11309    my_umask = umask(0);
11310    umask(my_umask);
11311 
11312    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11313       return AST_MODULE_LOAD_DECLINE;
11314    }
11315 
11316    /* compute the location of the voicemail spool directory */
11317    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11318    
11319    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11320       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11321    }
11322 
11323    if ((res = load_config(0)))
11324       return res;
11325 
11326    res = ast_register_application_xml(app, vm_exec);
11327    res |= ast_register_application_xml(app2, vm_execmain);
11328    res |= ast_register_application_xml(app3, vm_box_exists);
11329    res |= ast_register_application_xml(app4, vmauthenticate);
11330    res |= ast_custom_function_register(&mailbox_exists_acf);
11331    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11332    if (res)
11333       return res;
11334 
11335    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11336 
11337    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11338    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11339    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11340 
11341    return res;
11342 }
11343 
11344 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11345 {
11346    int cmd = 0;
11347    char destination[80] = "";
11348    int retries = 0;
11349 
11350    if (!num) {
11351       ast_verb(3, "Destination number will be entered manually\n");
11352       while (retries < 3 && cmd != 't') {
11353          destination[1] = '\0';
11354          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11355          if (!cmd)
11356             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11357          if (!cmd)
11358             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11359          if (!cmd) {
11360             cmd = ast_waitfordigit(chan, 6000);
11361             if (cmd)
11362                destination[0] = cmd;
11363          }
11364          if (!cmd) {
11365             retries++;
11366          } else {
11367 
11368             if (cmd < 0)
11369                return 0;
11370             if (cmd == '*') {
11371                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11372                return 0;
11373             }
11374             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11375                retries++;
11376             else
11377                cmd = 't';
11378          }
11379       }
11380       if (retries >= 3) {
11381          return 0;
11382       }
11383       
11384    } else {
11385       if (option_verbose > 2)
11386          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11387       ast_copy_string(destination, num, sizeof(destination));
11388    }
11389 
11390    if (!ast_strlen_zero(destination)) {
11391       if (destination[strlen(destination) -1 ] == '*')
11392          return 0; 
11393       if (option_verbose > 2)
11394          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11395       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11396       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11397       chan->priority = 0;
11398       return 9;
11399    }
11400    return 0;
11401 }
11402 
11403 /*!
11404  * \brief The advanced options within a message.
11405  * \param chan
11406  * \param vmu 
11407  * \param vms
11408  * \param msg
11409  * \param option
11410  * \param record_gain
11411  *
11412  * Provides handling for the play message envelope, call the person back, or reply to message. 
11413  *
11414  * \return zero on success, -1 on error.
11415  */
11416 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)
11417 {
11418    int res = 0;
11419    char filename[PATH_MAX];
11420    struct ast_config *msg_cfg = NULL;
11421    const char *origtime, *context;
11422    char *name, *num;
11423    int retries = 0;
11424    char *cid;
11425    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11426 
11427    vms->starting = 0; 
11428 
11429    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11430 
11431    /* Retrieve info from VM attribute file */
11432    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11433    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11434    msg_cfg = ast_config_load(filename, config_flags);
11435    DISPOSE(vms->curdir, vms->curmsg);
11436    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11437       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11438       return 0;
11439    }
11440 
11441    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11442       ast_config_destroy(msg_cfg);
11443       return 0;
11444    }
11445 
11446    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11447 
11448    context = ast_variable_retrieve(msg_cfg, "message", "context");
11449    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11450       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11451    switch (option) {
11452    case 3: /* Play message envelope */
11453       if (!res)
11454          res = play_message_datetime(chan, vmu, origtime, filename);
11455       if (!res)
11456          res = play_message_callerid(chan, vms, cid, context, 0);
11457 
11458       res = 't';
11459       break;
11460 
11461    case 2:  /* Call back */
11462 
11463       if (ast_strlen_zero(cid))
11464          break;
11465 
11466       ast_callerid_parse(cid, &name, &num);
11467       while ((res > -1) && (res != 't')) {
11468          switch (res) {
11469          case '1':
11470             if (num) {
11471                /* Dial the CID number */
11472                res = dialout(chan, vmu, num, vmu->callback);
11473                if (res) {
11474                   ast_config_destroy(msg_cfg);
11475                   return 9;
11476                }
11477             } else {
11478                res = '2';
11479             }
11480             break;
11481 
11482          case '2':
11483             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11484             if (!ast_strlen_zero(vmu->dialout)) {
11485                res = dialout(chan, vmu, NULL, vmu->dialout);
11486                if (res) {
11487                   ast_config_destroy(msg_cfg);
11488                   return 9;
11489                }
11490             } else {
11491                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11492                res = ast_play_and_wait(chan, "vm-sorry");
11493             }
11494             ast_config_destroy(msg_cfg);
11495             return res;
11496          case '*':
11497             res = 't';
11498             break;
11499          case '3':
11500          case '4':
11501          case '5':
11502          case '6':
11503          case '7':
11504          case '8':
11505          case '9':
11506          case '0':
11507 
11508             res = ast_play_and_wait(chan, "vm-sorry");
11509             retries++;
11510             break;
11511          default:
11512             if (num) {
11513                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11514                res = ast_play_and_wait(chan, "vm-num-i-have");
11515                if (!res)
11516                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11517                if (!res)
11518                   res = ast_play_and_wait(chan, "vm-tocallnum");
11519                /* Only prompt for a caller-specified number if there is a dialout context specified */
11520                if (!ast_strlen_zero(vmu->dialout)) {
11521                   if (!res)
11522                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11523                }
11524             } else {
11525                res = ast_play_and_wait(chan, "vm-nonumber");
11526                if (!ast_strlen_zero(vmu->dialout)) {
11527                   if (!res)
11528                      res = ast_play_and_wait(chan, "vm-toenternumber");
11529                }
11530             }
11531             if (!res)
11532                res = ast_play_and_wait(chan, "vm-star-cancel");
11533             if (!res)
11534                res = ast_waitfordigit(chan, 6000);
11535             if (!res) {
11536                retries++;
11537                if (retries > 3)
11538                   res = 't';
11539             }
11540             break; 
11541             
11542          }
11543          if (res == 't')
11544             res = 0;
11545          else if (res == '*')
11546             res = -1;
11547       }
11548       break;
11549       
11550    case 1:  /* Reply */
11551       /* Send reply directly to sender */
11552       if (ast_strlen_zero(cid))
11553          break;
11554 
11555       ast_callerid_parse(cid, &name, &num);
11556       if (!num) {
11557          ast_verb(3, "No CID number available, no reply sent\n");
11558          if (!res)
11559             res = ast_play_and_wait(chan, "vm-nonumber");
11560          ast_config_destroy(msg_cfg);
11561          return res;
11562       } else {
11563          struct ast_vm_user vmu2;
11564          if (find_user(&vmu2, vmu->context, num)) {
11565             struct leave_vm_options leave_options;
11566             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11567             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11568 
11569             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11570             
11571             memset(&leave_options, 0, sizeof(leave_options));
11572             leave_options.record_gain = record_gain;
11573             res = leave_voicemail(chan, mailbox, &leave_options);
11574             if (!res)
11575                res = 't';
11576             ast_config_destroy(msg_cfg);
11577             return res;
11578          } else {
11579             /* Sender has no mailbox, can't reply */
11580             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11581             ast_play_and_wait(chan, "vm-nobox");
11582             res = 't';
11583             ast_config_destroy(msg_cfg);
11584             return res;
11585          }
11586       } 
11587       res = 0;
11588 
11589       break;
11590    }
11591 
11592 #ifndef IMAP_STORAGE
11593    ast_config_destroy(msg_cfg);
11594 
11595    if (!res) {
11596       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11597       vms->heard[msg] = 1;
11598       res = wait_file(chan, vms, vms->fn);
11599    }
11600 #endif
11601    return res;
11602 }
11603 
11604 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11605          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11606          signed char record_gain, struct vm_state *vms, char *flag)
11607 {
11608    /* Record message & let caller review or re-record it, or set options if applicable */
11609    int res = 0;
11610    int cmd = 0;
11611    int max_attempts = 3;
11612    int attempts = 0;
11613    int recorded = 0;
11614    int msg_exists = 0;
11615    signed char zero_gain = 0;
11616    char tempfile[PATH_MAX];
11617    char *acceptdtmf = "#";
11618    char *canceldtmf = "";
11619 
11620    /* Note that urgent and private are for flagging messages as such in the future */
11621 
11622    /* barf if no pointer passed to store duration in */
11623    if (duration == NULL) {
11624       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11625       return -1;
11626    }
11627 
11628    if (!outsidecaller)
11629       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11630    else
11631       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11632 
11633    cmd = '3';  /* Want to start by recording */
11634 
11635    while ((cmd >= 0) && (cmd != 't')) {
11636       switch (cmd) {
11637       case '1':
11638          if (!msg_exists) {
11639             /* In this case, 1 is to record a message */
11640             cmd = '3';
11641             break;
11642          } else {
11643             /* Otherwise 1 is to save the existing message */
11644             ast_verb(3, "Saving message as is\n");
11645             if (!outsidecaller) 
11646                ast_filerename(tempfile, recordfile, NULL);
11647             ast_stream_and_wait(chan, "vm-msgsaved", "");
11648             if (!outsidecaller) {
11649                /* Saves to IMAP server only if imapgreeting=yes */
11650                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11651                DISPOSE(recordfile, -1);
11652             }
11653             cmd = 't';
11654             return res;
11655          }
11656       case '2':
11657          /* Review */
11658          ast_verb(3, "Reviewing the message\n");
11659          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11660          break;
11661       case '3':
11662          msg_exists = 0;
11663          /* Record */
11664          if (recorded == 1) 
11665             ast_verb(3, "Re-recording the message\n");
11666          else  
11667             ast_verb(3, "Recording the message\n");
11668          
11669          if (recorded && outsidecaller) {
11670             cmd = ast_play_and_wait(chan, INTRO);
11671             cmd = ast_play_and_wait(chan, "beep");
11672          }
11673          recorded = 1;
11674          /* 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 */
11675          if (record_gain)
11676             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11677          if (ast_test_flag(vmu, VM_OPERATOR))
11678             canceldtmf = "0";
11679          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11680          if (record_gain)
11681             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11682          if (cmd == -1) {
11683             /* User has hung up, no options to give */
11684             if (!outsidecaller) {
11685                /* user was recording a greeting and they hung up, so let's delete the recording. */
11686                ast_filedelete(tempfile, NULL);
11687             }     
11688             return cmd;
11689          }
11690          if (cmd == '0') {
11691             break;
11692          } else if (cmd == '*') {
11693             break;
11694 #if 0
11695          } else if (vmu->review && (*duration < 5)) {
11696             /* Message is too short */
11697             ast_verb(3, "Message too short\n");
11698             cmd = ast_play_and_wait(chan, "vm-tooshort");
11699             cmd = ast_filedelete(tempfile, NULL);
11700             break;
11701          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11702             /* Message is all silence */
11703             ast_verb(3, "Nothing recorded\n");
11704             cmd = ast_filedelete(tempfile, NULL);
11705             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11706             if (!cmd)
11707                cmd = ast_play_and_wait(chan, "vm-speakup");
11708             break;
11709 #endif
11710          } else {
11711             /* If all is well, a message exists */
11712             msg_exists = 1;
11713             cmd = 0;
11714          }
11715          break;
11716       case '4':
11717          if (outsidecaller) {  /* only mark vm messages */
11718             /* Mark Urgent */
11719             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11720                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11721                ast_debug(1000, "This message is too urgent!\n");
11722                res = ast_play_and_wait(chan, "vm-marked-urgent");
11723                strcpy(flag, "Urgent");
11724             } else if (flag) {
11725                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11726                res = ast_play_and_wait(chan, "vm-urgent-removed");
11727                strcpy(flag, "");
11728             } else {
11729                ast_play_and_wait(chan, "vm-sorry");
11730             }
11731             cmd = 0;
11732          } else {
11733             cmd = ast_play_and_wait(chan, "vm-sorry");
11734          }
11735          break;
11736       case '5':
11737       case '6':
11738       case '7':
11739       case '8':
11740       case '9':
11741       case '*':
11742       case '#':
11743          cmd = ast_play_and_wait(chan, "vm-sorry");
11744          break;
11745 #if 0 
11746 /*  XXX Commented out for the moment because of the dangers of deleting
11747     a message while recording (can put the message numbers out of sync) */
11748       case '*':
11749          /* Cancel recording, delete message, offer to take another message*/
11750          cmd = ast_play_and_wait(chan, "vm-deleted");
11751          cmd = ast_filedelete(tempfile, NULL);
11752          if (outsidecaller) {
11753             res = vm_exec(chan, NULL);
11754             return res;
11755          }
11756          else
11757             return 1;
11758 #endif
11759       case '0':
11760          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11761             cmd = ast_play_and_wait(chan, "vm-sorry");
11762             break;
11763          }
11764          if (msg_exists || recorded) {
11765             cmd = ast_play_and_wait(chan, "vm-saveoper");
11766             if (!cmd)
11767                cmd = ast_waitfordigit(chan, 3000);
11768             if (cmd == '1') {
11769                ast_play_and_wait(chan, "vm-msgsaved");
11770                cmd = '0';
11771             } else if (cmd == '4') {
11772                if (flag) {
11773                   ast_play_and_wait(chan, "vm-marked-urgent");
11774                   strcpy(flag, "Urgent");
11775                }
11776                ast_play_and_wait(chan, "vm-msgsaved");
11777                cmd = '0';
11778             } else {
11779                ast_play_and_wait(chan, "vm-deleted");
11780                DELETE(recordfile, -1, recordfile, vmu);
11781                cmd = '0';
11782             }
11783          }
11784          return cmd;
11785       default:
11786          /* If the caller is an ouside caller, and the review option is enabled,
11787             allow them to review the message, but let the owner of the box review
11788             their OGM's */
11789          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11790             return cmd;
11791          if (msg_exists) {
11792             cmd = ast_play_and_wait(chan, "vm-review");
11793             if (!cmd && outsidecaller) {
11794                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11795                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11796                } else if (flag) {
11797                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11798                }
11799             }
11800          } else {
11801             cmd = ast_play_and_wait(chan, "vm-torerecord");
11802             if (!cmd)
11803                cmd = ast_waitfordigit(chan, 600);
11804          }
11805          
11806          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11807             cmd = ast_play_and_wait(chan, "vm-reachoper");
11808             if (!cmd)
11809                cmd = ast_waitfordigit(chan, 600);
11810          }
11811 #if 0
11812          if (!cmd)
11813             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11814 #endif
11815          if (!cmd)
11816             cmd = ast_waitfordigit(chan, 6000);
11817          if (!cmd) {
11818             attempts++;
11819          }
11820          if (attempts > max_attempts) {
11821             cmd = 't';
11822          }
11823       }
11824    }
11825    if (outsidecaller)
11826       ast_play_and_wait(chan, "vm-goodbye");
11827    if (cmd == 't')
11828       cmd = 0;
11829    return cmd;
11830 }
11831 
11832 /* This is a workaround so that menuselect displays a proper description
11833  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11834  */
11835 
11836 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11837       .load = load_module,
11838       .unload = unload_module,
11839       .reload = reload,
11840       );

Generated by  doxygen 1.6.2