Mon Jun 27 16:50:48 2011

Asterisk developer's documentation


app_voicemail_imapstorage.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 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043  ***/
00044 
00045 #include "asterisk.h"
00046 
00047 #ifdef IMAP_STORAGE
00048 #include <ctype.h>
00049 #include <signal.h>
00050 #include <pwd.h>
00051 #ifdef USE_SYSTEM_IMAP
00052 #include <imap/c-client.h>
00053 #include <imap/imap4r1.h>
00054 #include <imap/linkage.h>
00055 #elif defined (USE_SYSTEM_CCLIENT)
00056 #include <c-client/c-client.h>
00057 #include <c-client/imap4r1.h>
00058 #include <c-client/linkage.h>
00059 #else
00060 #include "c-client.h"
00061 #include "imap4r1.h"
00062 #include "linkage.h"
00063 #endif
00064 #endif
00065 
00066 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 306967 $")
00067 
00068 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00069 #include <sys/time.h>
00070 #include <sys/stat.h>
00071 #include <sys/mman.h>
00072 #include <time.h>
00073 #include <dirent.h>
00074 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00075 #include <sys/wait.h>
00076 #endif
00077 
00078 #include "asterisk/logger.h"
00079 #include "asterisk/lock.h"
00080 #include "asterisk/file.h"
00081 #include "asterisk/channel.h"
00082 #include "asterisk/pbx.h"
00083 #include "asterisk/config.h"
00084 #include "asterisk/say.h"
00085 #include "asterisk/module.h"
00086 #include "asterisk/adsi.h"
00087 #include "asterisk/app.h"
00088 #include "asterisk/manager.h"
00089 #include "asterisk/dsp.h"
00090 #include "asterisk/localtime.h"
00091 #include "asterisk/cli.h"
00092 #include "asterisk/utils.h"
00093 #include "asterisk/stringfields.h"
00094 #include "asterisk/smdi.h"
00095 #include "asterisk/astobj2.h"
00096 #include "asterisk/event.h"
00097 #include "asterisk/taskprocessor.h"
00098 #include "asterisk/test.h"
00099 
00100 #ifdef ODBC_STORAGE
00101 #include "asterisk/res_odbc.h"
00102 #endif
00103 
00104 #ifdef IMAP_STORAGE
00105 #include "asterisk/threadstorage.h"
00106 #endif
00107 
00108 /*** DOCUMENTATION
00109    <application name="VoiceMail" language="en_US">
00110       <synopsis>
00111          Leave a Voicemail message.
00112       </synopsis>
00113       <syntax>
00114          <parameter name="mailboxs" argsep="&amp;" required="true">
00115             <argument name="mailbox1" argsep="@" required="true">
00116                <argument name="mailbox" required="true" />
00117                <argument name="context" />
00118             </argument>
00119             <argument name="mailbox2" argsep="@" multiple="true">
00120                <argument name="mailbox" required="true" />
00121                <argument name="context" />
00122             </argument>
00123          </parameter>
00124          <parameter name="options">
00125             <optionlist>
00126                <option name="b">
00127                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00128                </option>
00129                <option name="d">
00130                   <argument name="c" />
00131                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00132                   if played during the greeting. Context defaults to the current context.</para>
00133                </option>
00134                <option name="g">
00135                   <argument name="#" required="true" />
00136                   <para>Use the specified amount of gain when recording the voicemail
00137                   message. The units are whole-number decibels (dB). Only works on supported
00138                   technologies, which is DAHDI only.</para>
00139                </option>
00140                <option name="s">
00141                   <para>Skip the playback of instructions for leaving a message to the
00142                   calling party.</para>
00143                </option>
00144                <option name="u">
00145                   <para>Play the <literal>unavailable</literal> greeting.</para>
00146                </option>
00147                <option name="U">
00148                   <para>Mark message as <literal>URGENT</literal>.</para>
00149                </option>
00150                <option name="P">
00151                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00152                </option>
00153             </optionlist>
00154          </parameter>
00155       </syntax>
00156       <description>
00157          <para>This application allows the calling party to leave a message for the specified
00158          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00159          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00160          exist.</para>
00161          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00162          <enumlist>
00163             <enum name="0">
00164                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00165             </enum>
00166             <enum name="*">
00167                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00168             </enum>
00169          </enumlist>
00170          <para>This application will set the following channel variable upon completion:</para>
00171          <variablelist>
00172             <variable name="VMSTATUS">
00173                <para>This indicates the status of the execution of the VoiceMail application.</para>
00174                <value name="SUCCESS" />
00175                <value name="USEREXIT" />
00176                <value name="FAILED" />
00177             </variable>
00178          </variablelist>
00179       </description>
00180    </application>
00181    <application name="VoiceMailMain" language="en_US">
00182       <synopsis>
00183          Check Voicemail messages.
00184       </synopsis>
00185       <syntax>
00186          <parameter name="mailbox" required="true" argsep="@">
00187             <argument name="mailbox" />
00188             <argument name="context" />
00189          </parameter>
00190          <parameter name="options">
00191             <optionlist>
00192                <option name="p">
00193                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00194                   the mailbox that is entered by the caller.</para>
00195                </option>
00196                <option name="g">
00197                   <argument name="#" required="true" />
00198                   <para>Use the specified amount of gain when recording a voicemail message.
00199                   The units are whole-number decibels (dB).</para>
00200                </option>
00201                <option name="s">
00202                   <para>Skip checking the passcode for the mailbox.</para>
00203                </option>
00204                <option name="a">
00205                   <argument name="folder" required="true" />
00206                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00207                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00208                   <enumlist>
00209                      <enum name="0"><para>INBOX</para></enum>
00210                      <enum name="1"><para>Old</para></enum>
00211                      <enum name="2"><para>Work</para></enum>
00212                      <enum name="3"><para>Family</para></enum>
00213                      <enum name="4"><para>Friends</para></enum>
00214                      <enum name="5"><para>Cust1</para></enum>
00215                      <enum name="6"><para>Cust2</para></enum>
00216                      <enum name="7"><para>Cust3</para></enum>
00217                      <enum name="8"><para>Cust4</para></enum>
00218                      <enum name="9"><para>Cust5</para></enum>
00219                   </enumlist>
00220                </option>
00221             </optionlist>
00222          </parameter>
00223       </syntax>
00224       <description>
00225          <para>This application allows the calling party to check voicemail messages. A specific
00226          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00227          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00228          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00229          <literal>default</literal> context will be used.</para>
00230          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00231          or Password, and the extension exists:</para>
00232          <enumlist>
00233             <enum name="*">
00234                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00235             </enum>
00236          </enumlist>
00237       </description>
00238    </application>
00239    <application name="MailboxExists" language="en_US">
00240       <synopsis>
00241          Check to see if Voicemail mailbox exists.
00242       </synopsis>
00243       <syntax>
00244          <parameter name="mailbox" required="true" argsep="@">
00245             <argument name="mailbox" required="true" />
00246             <argument name="context" />
00247          </parameter>
00248          <parameter name="options">
00249             <para>None options.</para>
00250          </parameter>
00251       </syntax>
00252       <description>
00253          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00254          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00255          will be used.</para>
00256          <para>This application will set the following channel variable upon completion:</para>
00257          <variablelist>
00258             <variable name="VMBOXEXISTSSTATUS">
00259                <para>This will contain the status of the execution of the MailboxExists application.
00260                Possible values include:</para>
00261                <value name="SUCCESS" />
00262                <value name="FAILED" />
00263             </variable>
00264          </variablelist>
00265       </description>
00266    </application>
00267    <application name="VMAuthenticate" language="en_US">
00268       <synopsis>
00269          Authenticate with Voicemail passwords.
00270       </synopsis>
00271       <syntax>
00272          <parameter name="mailbox" required="true" argsep="@">
00273             <argument name="mailbox" />
00274             <argument name="context" />
00275          </parameter>
00276          <parameter name="options">
00277             <optionlist>
00278                <option name="s">
00279                   <para>Skip playing the initial prompts.</para>
00280                </option>
00281             </optionlist>
00282          </parameter>
00283       </syntax>
00284       <description>
00285          <para>This application behaves the same way as the Authenticate application, but the passwords
00286          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00287          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00288          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00289          mailbox.</para>
00290          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00291          or Password, and the extension exists:</para>
00292          <enumlist>
00293             <enum name="*">
00294                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00295             </enum>
00296          </enumlist>
00297       </description>
00298    </application>
00299    <application name="VMSayName" language="en_US">
00300       <synopsis>
00301          Play the name of a voicemail user
00302       </synopsis>
00303       <syntax>
00304          <parameter name="mailbox" required="true" argsep="@">
00305             <argument name="mailbox" />
00306             <argument name="context" />
00307          </parameter>
00308       </syntax>
00309       <description>
00310          <para>This application will say the recorded name of the voicemail user specified as the
00311          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00312       </description>
00313    </application>
00314    <function name="MAILBOX_EXISTS" language="en_US">
00315       <synopsis>
00316          Tell if a mailbox is configured.
00317       </synopsis>
00318       <syntax argsep="@">
00319          <parameter name="mailbox" required="true" />
00320          <parameter name="context" />
00321       </syntax>
00322       <description>
00323          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00324          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00325          context.</para>
00326       </description>
00327    </function>
00328    <manager name="VoicemailUsersList" language="en_US">
00329       <synopsis>
00330          List All Voicemail User Information.
00331       </synopsis>
00332       <syntax>
00333          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00334       </syntax>
00335       <description>
00336       </description>
00337    </manager>
00338  ***/
00339 
00340 #ifdef IMAP_STORAGE
00341 static char imapserver[48];
00342 static char imapport[8];
00343 static char imapflags[128];
00344 static char imapfolder[64];
00345 static char imapparentfolder[64] = "\0";
00346 static char greetingfolder[64];
00347 static char authuser[32];
00348 static char authpassword[42];
00349 static int imapversion = 1;
00350 
00351 static int expungeonhangup = 1;
00352 static int imapgreetings = 0;
00353 static char delimiter = '\0';
00354 
00355 struct vm_state;
00356 struct ast_vm_user;
00357 
00358 AST_THREADSTORAGE(ts_vmstate);
00359 
00360 /* Forward declarations for IMAP */
00361 static int init_mailstream(struct vm_state *vms, int box);
00362 static void write_file(char *filename, char *buffer, unsigned long len);
00363 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00364 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00365 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00366 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00367 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00368 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00369 static void vmstate_insert(struct vm_state *vms);
00370 static void vmstate_delete(struct vm_state *vms);
00371 static void set_update(MAILSTREAM * stream);
00372 static void init_vm_state(struct vm_state *vms);
00373 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00374 static void get_mailbox_delimiter(MAILSTREAM *stream);
00375 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00376 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00377 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);
00378 static void update_messages_by_imapuser(const char *user, unsigned long number);
00379 static int vm_delete(char *file);
00380 
00381 static int imap_remove_file (char *dir, int msgnum);
00382 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00383 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00384 static void check_quota(struct vm_state *vms, char *mailbox);
00385 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00386 struct vmstate {
00387    struct vm_state *vms;
00388    AST_LIST_ENTRY(vmstate) list;
00389 };
00390 
00391 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00392 
00393 #endif
00394 
00395 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00396 
00397 #define COMMAND_TIMEOUT 5000
00398 /* Don't modify these here; set your umask at runtime instead */
00399 #define  VOICEMAIL_DIR_MODE   0777
00400 #define  VOICEMAIL_FILE_MODE  0666
00401 #define  CHUNKSIZE   65536
00402 
00403 #define VOICEMAIL_CONFIG "voicemail.conf"
00404 #define ASTERISK_USERNAME "asterisk"
00405 
00406 /* Define fast-forward, pause, restart, and reverse keys
00407    while listening to a voicemail message - these are
00408    strings, not characters */
00409 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00410 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00411 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00412 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00413 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00414 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00415 
00416 /* Default mail command to mail voicemail. Change it with the
00417     mailcmd= command in voicemail.conf */
00418 #define SENDMAIL "/usr/sbin/sendmail -t"
00419 
00420 #define INTRO "vm-intro"
00421 
00422 #define MAXMSG 100
00423 #define MAXMSGLIMIT 9999
00424 
00425 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00426 
00427 #define BASELINELEN 72
00428 #define BASEMAXINLINE 256
00429 #ifdef IMAP_STORAGE
00430 #define ENDL "\r\n"
00431 #else
00432 #define ENDL "\n"
00433 #endif
00434 
00435 #define MAX_DATETIME_FORMAT   512
00436 #define MAX_NUM_CID_CONTEXTS 10
00437 
00438 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00439 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00440 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00441 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00442 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00443 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00444 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00445 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00446 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00447 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00448 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00449 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00450 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00451 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00452 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00453 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00454 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00455 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00456 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00457 #define ERROR_LOCK_PATH  -100
00458 #define OPERATOR_EXIT     300
00459 
00460 
00461 enum vm_box {
00462    NEW_FOLDER,
00463    OLD_FOLDER,
00464    WORK_FOLDER,
00465    FAMILY_FOLDER,
00466    FRIENDS_FOLDER,
00467    GREETINGS_FOLDER
00468 };
00469 
00470 enum vm_option_flags {
00471    OPT_SILENT =           (1 << 0),
00472    OPT_BUSY_GREETING =    (1 << 1),
00473    OPT_UNAVAIL_GREETING = (1 << 2),
00474    OPT_RECORDGAIN =       (1 << 3),
00475    OPT_PREPEND_MAILBOX =  (1 << 4),
00476    OPT_AUTOPLAY =         (1 << 6),
00477    OPT_DTMFEXIT =         (1 << 7),
00478    OPT_MESSAGE_Urgent =   (1 << 8),
00479    OPT_MESSAGE_PRIORITY = (1 << 9)
00480 };
00481 
00482 enum vm_option_args {
00483    OPT_ARG_RECORDGAIN = 0,
00484    OPT_ARG_PLAYFOLDER = 1,
00485    OPT_ARG_DTMFEXIT   = 2,
00486    /* This *must* be the last value in this enum! */
00487    OPT_ARG_ARRAY_SIZE = 3,
00488 };
00489 
00490 enum vm_passwordlocation {
00491    OPT_PWLOC_VOICEMAILCONF = 0,
00492    OPT_PWLOC_SPOOLDIR      = 1,
00493    OPT_PWLOC_USERSCONF     = 2,
00494 };
00495 
00496 AST_APP_OPTIONS(vm_app_options, {
00497    AST_APP_OPTION('s', OPT_SILENT),
00498    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00499    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00500    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00501    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00502    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00503    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00504    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00505    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00506 });
00507 
00508 static int load_config(int reload);
00509 
00510 /*! \page vmlang Voicemail Language Syntaxes Supported
00511 
00512    \par Syntaxes supported, not really language codes.
00513    \arg \b en    - English
00514    \arg \b de    - German
00515    \arg \b es    - Spanish
00516    \arg \b fr    - French
00517    \arg \b it    - Italian
00518    \arg \b nl    - Dutch
00519    \arg \b pt    - Portuguese
00520    \arg \b pt_BR - Portuguese (Brazil)
00521    \arg \b gr    - Greek
00522    \arg \b no    - Norwegian
00523    \arg \b se    - Swedish
00524    \arg \b tw    - Chinese (Taiwan)
00525    \arg \b ua - Ukrainian
00526 
00527 German requires the following additional soundfile:
00528 \arg \b 1F  einE (feminine)
00529 
00530 Spanish requires the following additional soundfile:
00531 \arg \b 1M      un (masculine)
00532 
00533 Dutch, Portuguese & Spanish require the following additional soundfiles:
00534 \arg \b vm-INBOXs singular of 'new'
00535 \arg \b vm-Olds      singular of 'old/heard/read'
00536 
00537 NB these are plural:
00538 \arg \b vm-INBOX  nieuwe (nl)
00539 \arg \b vm-Old    oude (nl)
00540 
00541 Polish uses:
00542 \arg \b vm-new-a  'new', feminine singular accusative
00543 \arg \b vm-new-e  'new', feminine plural accusative
00544 \arg \b vm-new-ych   'new', feminine plural genitive
00545 \arg \b vm-old-a  'old', feminine singular accusative
00546 \arg \b vm-old-e  'old', feminine plural accusative
00547 \arg \b vm-old-ych   'old', feminine plural genitive
00548 \arg \b digits/1-a   'one', not always same as 'digits/1'
00549 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00550 
00551 Swedish uses:
00552 \arg \b vm-nytt      singular of 'new'
00553 \arg \b vm-nya    plural of 'new'
00554 \arg \b vm-gammalt   singular of 'old'
00555 \arg \b vm-gamla  plural of 'old'
00556 \arg \b digits/ett   'one', not always same as 'digits/1'
00557 
00558 Norwegian uses:
00559 \arg \b vm-ny     singular of 'new'
00560 \arg \b vm-nye    plural of 'new'
00561 \arg \b vm-gammel singular of 'old'
00562 \arg \b vm-gamle  plural of 'old'
00563 
00564 Dutch also uses:
00565 \arg \b nl-om     'at'?
00566 
00567 Spanish also uses:
00568 \arg \b vm-youhaveno
00569 
00570 Italian requires the following additional soundfile:
00571 
00572 For vm_intro_it:
00573 \arg \b vm-nuovo  new
00574 \arg \b vm-nuovi  new plural
00575 \arg \b vm-vecchio   old
00576 \arg \b vm-vecchi old plural
00577 
00578 Chinese (Taiwan) requires the following additional soundfile:
00579 \arg \b vm-tong      A class-word for call (tong1)
00580 \arg \b vm-ri     A class-word for day (ri4)
00581 \arg \b vm-you    You (ni3)
00582 \arg \b vm-haveno   Have no (mei2 you3)
00583 \arg \b vm-have     Have (you3)
00584 \arg \b vm-listen   To listen (yao4 ting1)
00585 
00586 
00587 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00588 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00589 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00590 
00591 */
00592 
00593 struct baseio {
00594    int iocp;
00595    int iolen;
00596    int linelength;
00597    int ateof;
00598    unsigned char iobuf[BASEMAXINLINE];
00599 };
00600 
00601 /*! Structure for linked list of users 
00602  * Use ast_vm_user_destroy() to free one of these structures. */
00603 struct ast_vm_user {
00604    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00605    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00606    char password[80];               /*!< Secret pin code, numbers only */
00607    char fullname[80];               /*!< Full name, for directory app */
00608    char email[80];                  /*!< E-mail address */
00609    char *emailsubject;              /*!< E-mail subject */
00610    char *emailbody;                 /*!< E-mail body */
00611    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00612    char serveremail[80];            /*!< From: Mail address */
00613    char mailcmd[160];               /*!< Configurable mail command */
00614    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00615    char zonetag[80];                /*!< Time zone */
00616    char locale[20];                 /*!< The locale (for presentation of date/time) */
00617    char callback[80];
00618    char dialout[80];
00619    char uniqueid[80];               /*!< Unique integer identifier */
00620    char exit[80];
00621    char attachfmt[20];              /*!< Attachment format */
00622    unsigned int flags;              /*!< VM_ flags */ 
00623    int saydurationm;
00624    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00625    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00626    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00627    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00628    int passwordlocation;            /*!< Storage location of the password */
00629 #ifdef IMAP_STORAGE
00630    char imapuser[80];               /*!< IMAP server login */
00631    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00632    char imapfolder[64];             /*!< IMAP voicemail folder */
00633    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00634    int imapversion;                 /*!< If configuration changes, use the new values */
00635 #endif
00636    double volgain;                  /*!< Volume gain for voicemails sent via email */
00637    AST_LIST_ENTRY(ast_vm_user) list;
00638 };
00639 
00640 /*! Voicemail time zones */
00641 struct vm_zone {
00642    AST_LIST_ENTRY(vm_zone) list;
00643    char name[80];
00644    char timezone[80];
00645    char msg_format[512];
00646 };
00647 
00648 #define VMSTATE_MAX_MSG_ARRAY 256
00649 
00650 /*! Voicemail mailbox state */
00651 struct vm_state {
00652    char curbox[80];
00653    char username[80];
00654    char context[80];
00655    char curdir[PATH_MAX];
00656    char vmbox[PATH_MAX];
00657    char fn[PATH_MAX];
00658    char intro[PATH_MAX];
00659    int *deleted;
00660    int *heard;
00661    int dh_arraysize; /* used for deleted / heard allocation */
00662    int curmsg;
00663    int lastmsg;
00664    int newmessages;
00665    int oldmessages;
00666    int urgentmessages;
00667    int starting;
00668    int repeats;
00669 #ifdef IMAP_STORAGE
00670    ast_mutex_t lock;
00671    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00672    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00673    MAILSTREAM *mailstream;
00674    int vmArrayIndex;
00675    char imapuser[80];                   /*!< IMAP server login */
00676    char imapfolder[64];                 /*!< IMAP voicemail folder */
00677    int imapversion;
00678    int interactive;
00679    char introfn[PATH_MAX];              /*!< Name of prepended file */
00680    unsigned int quota_limit;
00681    unsigned int quota_usage;
00682    struct vm_state *persist_vms;
00683 #endif
00684 };
00685 
00686 #ifdef ODBC_STORAGE
00687 static char odbc_database[80];
00688 static char odbc_table[80];
00689 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00690 #define DISPOSE(a,b) remove_file(a,b)
00691 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00692 #define EXISTS(a,b,c,d) (message_exists(a,b))
00693 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00694 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00695 #define DELETE(a,b,c,d) (delete_file(a,b))
00696 #else
00697 #ifdef IMAP_STORAGE
00698 #define DISPOSE(a,b) (imap_remove_file(a,b))
00699 #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))
00700 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00701 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00702 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00703 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00704 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00705 #else
00706 #define RETRIEVE(a,b,c,d)
00707 #define DISPOSE(a,b)
00708 #define STORE(a,b,c,d,e,f,g,h,i,j)
00709 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00710 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00711 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00712 #define DELETE(a,b,c,d) (vm_delete(c))
00713 #endif
00714 #endif
00715 
00716 static char VM_SPOOL_DIR[PATH_MAX];
00717 
00718 static char ext_pass_cmd[128];
00719 static char ext_pass_check_cmd[128];
00720 
00721 static int my_umask;
00722 
00723 #define PWDCHANGE_INTERNAL (1 << 1)
00724 #define PWDCHANGE_EXTERNAL (1 << 2)
00725 static int pwdchange = PWDCHANGE_INTERNAL;
00726 
00727 #ifdef ODBC_STORAGE
00728 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00729 #else
00730 # ifdef IMAP_STORAGE
00731 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00732 # else
00733 # define tdesc "Comedian Mail (Voicemail System)"
00734 # endif
00735 #endif
00736 
00737 static char userscontext[AST_MAX_EXTENSION] = "default";
00738 
00739 static char *addesc = "Comedian Mail";
00740 
00741 /* Leave a message */
00742 static char *app = "VoiceMail";
00743 
00744 /* Check mail, control, etc */
00745 static char *app2 = "VoiceMailMain";
00746 
00747 static char *app3 = "MailboxExists";
00748 static char *app4 = "VMAuthenticate";
00749 
00750 static char *sayname_app = "VMSayName";
00751 
00752 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00753 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00754 static char zonetag[80];
00755 static char locale[20];
00756 static int maxsilence;
00757 static int maxmsg;
00758 static int maxdeletedmsg;
00759 static int silencethreshold = 128;
00760 static char serveremail[80];
00761 static char mailcmd[160];  /* Configurable mail cmd */
00762 static char externnotify[160]; 
00763 static struct ast_smdi_interface *smdi_iface = NULL;
00764 static char vmfmts[80];
00765 static double volgain;
00766 static int vmminsecs;
00767 static int vmmaxsecs;
00768 static int maxgreet;
00769 static int skipms;
00770 static int maxlogins;
00771 static int minpassword;
00772 static int passwordlocation;
00773 
00774 /*! Poll mailboxes for changes since there is something external to
00775  *  app_voicemail that may change them. */
00776 static unsigned int poll_mailboxes;
00777 
00778 /*! Polling frequency */
00779 static unsigned int poll_freq;
00780 /*! By default, poll every 30 seconds */
00781 #define DEFAULT_POLL_FREQ 30
00782 
00783 AST_MUTEX_DEFINE_STATIC(poll_lock);
00784 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00785 static pthread_t poll_thread = AST_PTHREADT_NULL;
00786 static unsigned char poll_thread_run;
00787 
00788 /*! Subscription to ... MWI event subscriptions */
00789 static struct ast_event_sub *mwi_sub_sub;
00790 /*! Subscription to ... MWI event un-subscriptions */
00791 static struct ast_event_sub *mwi_unsub_sub;
00792 
00793 /*!
00794  * \brief An MWI subscription
00795  *
00796  * This is so we can keep track of which mailboxes are subscribed to.
00797  * This way, we know which mailboxes to poll when the pollmailboxes
00798  * option is being used.
00799  */
00800 struct mwi_sub {
00801    AST_RWLIST_ENTRY(mwi_sub) entry;
00802    int old_urgent;
00803    int old_new;
00804    int old_old;
00805    uint32_t uniqueid;
00806    char mailbox[1];
00807 };
00808 
00809 struct mwi_sub_task {
00810    const char *mailbox;
00811    const char *context;
00812    uint32_t uniqueid;
00813 };
00814 
00815 static struct ast_taskprocessor *mwi_subscription_tps;
00816 
00817 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00818 
00819 /* custom audio control prompts for voicemail playback */
00820 static char listen_control_forward_key[12];
00821 static char listen_control_reverse_key[12];
00822 static char listen_control_pause_key[12];
00823 static char listen_control_restart_key[12];
00824 static char listen_control_stop_key[12];
00825 
00826 /* custom password sounds */
00827 static char vm_password[80] = "vm-password";
00828 static char vm_newpassword[80] = "vm-newpassword";
00829 static char vm_passchanged[80] = "vm-passchanged";
00830 static char vm_reenterpassword[80] = "vm-reenterpassword";
00831 static char vm_mismatch[80] = "vm-mismatch";
00832 static char vm_invalid_password[80] = "vm-invalid-password";
00833 static char vm_pls_try_again[80] = "vm-pls-try-again";
00834 
00835 static struct ast_flags globalflags = {0};
00836 
00837 static int saydurationminfo;
00838 
00839 static char dialcontext[AST_MAX_CONTEXT] = "";
00840 static char callcontext[AST_MAX_CONTEXT] = "";
00841 static char exitcontext[AST_MAX_CONTEXT] = "";
00842 
00843 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00844 
00845 
00846 static char *emailbody = NULL;
00847 static char *emailsubject = NULL;
00848 static char *pagerbody = NULL;
00849 static char *pagersubject = NULL;
00850 static char fromstring[100];
00851 static char pagerfromstring[100];
00852 static char charset[32] = "ISO-8859-1";
00853 
00854 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00855 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00856 static int adsiver = 1;
00857 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00858 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00859 
00860 /* Forward declarations - generic */
00861 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00862 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);
00863 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00864 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00865          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00866          signed char record_gain, struct vm_state *vms, char *flag);
00867 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00868 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00869 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);
00870 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);
00871 static void apply_options(struct ast_vm_user *vmu, const char *options);
00872 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);
00873 static int is_valid_dtmf(const char *key);
00874 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00875 static int write_password_to_file(const char *secretfn, const char *password);
00876 
00877 struct ao2_container *inprocess_container;
00878 
00879 struct inprocess {
00880    int count;
00881    char *context;
00882    char mailbox[0];
00883 };
00884 
00885 static int inprocess_hash_fn(const void *obj, const int flags)
00886 {
00887    const struct inprocess *i = obj;
00888    return atoi(i->mailbox);
00889 }
00890 
00891 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00892 {
00893    struct inprocess *i = obj, *j = arg;
00894    if (strcmp(i->mailbox, j->mailbox)) {
00895       return 0;
00896    }
00897    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00898 }
00899 
00900 static int inprocess_count(const char *context, const char *mailbox, int delta)
00901 {
00902    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00903    arg->context = arg->mailbox + strlen(mailbox) + 1;
00904    strcpy(arg->mailbox, mailbox); /* SAFE */
00905    strcpy(arg->context, context); /* SAFE */
00906    ao2_lock(inprocess_container);
00907    if ((i = ao2_find(inprocess_container, arg, 0))) {
00908       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00909       ao2_unlock(inprocess_container);
00910       ao2_ref(i, -1);
00911       return ret;
00912    }
00913    if (delta < 0) {
00914       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00915    }
00916    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00917       ao2_unlock(inprocess_container);
00918       return 0;
00919    }
00920    i->context = i->mailbox + strlen(mailbox) + 1;
00921    strcpy(i->mailbox, mailbox); /* SAFE */
00922    strcpy(i->context, context); /* SAFE */
00923    i->count = delta;
00924    ao2_link(inprocess_container, i);
00925    ao2_unlock(inprocess_container);
00926    ao2_ref(i, -1);
00927    return 0;
00928 }
00929 
00930 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00931 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00932 #endif
00933 
00934 /*!
00935  * \brief Strips control and non 7-bit clean characters from input string.
00936  *
00937  * \note To map control and none 7-bit characters to a 7-bit clean characters
00938  *  please use ast_str_encode_mine().
00939  */
00940 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00941 {
00942    char *bufptr = buf;
00943    for (; *input; input++) {
00944       if (*input < 32) {
00945          continue;
00946       }
00947       *bufptr++ = *input;
00948       if (bufptr == buf + buflen - 1) {
00949          break;
00950       }
00951    }
00952    *bufptr = '\0';
00953    return buf;
00954 }
00955 
00956 
00957 /*!
00958  * \brief Sets default voicemail system options to a voicemail user.
00959  *
00960  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00961  * - all the globalflags
00962  * - the saydurationminfo
00963  * - the callcontext
00964  * - the dialcontext
00965  * - the exitcontext
00966  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00967  * - volume gain.
00968  */
00969 static void populate_defaults(struct ast_vm_user *vmu)
00970 {
00971    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00972    vmu->passwordlocation = passwordlocation;
00973    if (saydurationminfo) {
00974       vmu->saydurationm = saydurationminfo;
00975    }
00976    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00977    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00978    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00979    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00980    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
00981    if (vmminsecs) {
00982       vmu->minsecs = vmminsecs;
00983    }
00984    if (vmmaxsecs) {
00985       vmu->maxsecs = vmmaxsecs;
00986    }
00987    if (maxmsg) {
00988       vmu->maxmsg = maxmsg;
00989    }
00990    if (maxdeletedmsg) {
00991       vmu->maxdeletedmsg = maxdeletedmsg;
00992    }
00993    vmu->volgain = volgain;
00994    vmu->emailsubject = NULL;
00995    vmu->emailbody = NULL;
00996 #ifdef IMAP_STORAGE
00997    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
00998 #endif
00999 }
01000 
01001 /*!
01002  * \brief Sets a a specific property value.
01003  * \param vmu The voicemail user object to work with.
01004  * \param var The name of the property to be set.
01005  * \param value The value to be set to the property.
01006  * 
01007  * The property name must be one of the understood properties. See the source for details.
01008  */
01009 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01010 {
01011    int x;
01012    if (!strcasecmp(var, "attach")) {
01013       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01014    } else if (!strcasecmp(var, "attachfmt")) {
01015       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01016    } else if (!strcasecmp(var, "serveremail")) {
01017       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01018    } else if (!strcasecmp(var, "language")) {
01019       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01020    } else if (!strcasecmp(var, "tz")) {
01021       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01022    } else if (!strcasecmp(var, "locale")) {
01023       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01024 #ifdef IMAP_STORAGE
01025    } else if (!strcasecmp(var, "imapuser")) {
01026       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01027       vmu->imapversion = imapversion;
01028    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01029       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01030       vmu->imapversion = imapversion;
01031    } else if (!strcasecmp(var, "imapfolder")) {
01032       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01033    } else if (!strcasecmp(var, "imapvmshareid")) {
01034       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01035       vmu->imapversion = imapversion;
01036 #endif
01037    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01038       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01039    } else if (!strcasecmp(var, "saycid")){
01040       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01041    } else if (!strcasecmp(var, "sendvoicemail")){
01042       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01043    } else if (!strcasecmp(var, "review")){
01044       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01045    } else if (!strcasecmp(var, "tempgreetwarn")){
01046       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01047    } else if (!strcasecmp(var, "messagewrap")){
01048       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01049    } else if (!strcasecmp(var, "operator")) {
01050       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01051    } else if (!strcasecmp(var, "envelope")){
01052       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01053    } else if (!strcasecmp(var, "moveheard")){
01054       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01055    } else if (!strcasecmp(var, "sayduration")){
01056       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01057    } else if (!strcasecmp(var, "saydurationm")){
01058       if (sscanf(value, "%30d", &x) == 1) {
01059          vmu->saydurationm = x;
01060       } else {
01061          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01062       }
01063    } else if (!strcasecmp(var, "forcename")){
01064       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01065    } else if (!strcasecmp(var, "forcegreetings")){
01066       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01067    } else if (!strcasecmp(var, "callback")) {
01068       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01069    } else if (!strcasecmp(var, "dialout")) {
01070       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01071    } else if (!strcasecmp(var, "exitcontext")) {
01072       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01073    } else if (!strcasecmp(var, "minsecs")) {
01074       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01075          vmu->minsecs = x;
01076       } else {
01077          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01078          vmu->minsecs = vmminsecs;
01079       }
01080    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01081       vmu->maxsecs = atoi(value);
01082       if (vmu->maxsecs <= 0) {
01083          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01084          vmu->maxsecs = vmmaxsecs;
01085       } else {
01086          vmu->maxsecs = atoi(value);
01087       }
01088       if (!strcasecmp(var, "maxmessage"))
01089          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01090    } else if (!strcasecmp(var, "maxmsg")) {
01091       vmu->maxmsg = atoi(value);
01092       /* Accept maxmsg=0 (Greetings only voicemail) */
01093       if (vmu->maxmsg < 0) {
01094          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01095          vmu->maxmsg = MAXMSG;
01096       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01097          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01098          vmu->maxmsg = MAXMSGLIMIT;
01099       }
01100    } else if (!strcasecmp(var, "nextaftercmd")) {
01101       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01102    } else if (!strcasecmp(var, "backupdeleted")) {
01103       if (sscanf(value, "%30d", &x) == 1)
01104          vmu->maxdeletedmsg = x;
01105       else if (ast_true(value))
01106          vmu->maxdeletedmsg = MAXMSG;
01107       else
01108          vmu->maxdeletedmsg = 0;
01109 
01110       if (vmu->maxdeletedmsg < 0) {
01111          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01112          vmu->maxdeletedmsg = MAXMSG;
01113       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01114          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01115          vmu->maxdeletedmsg = MAXMSGLIMIT;
01116       }
01117    } else if (!strcasecmp(var, "volgain")) {
01118       sscanf(value, "%30lf", &vmu->volgain);
01119    } else if (!strcasecmp(var, "passwordlocation")) {
01120       if (!strcasecmp(value, "spooldir")) {
01121          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01122       } else {
01123          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01124       }
01125    } else if (!strcasecmp(var, "options")) {
01126       apply_options(vmu, value);
01127    }
01128 }
01129 
01130 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01131 {
01132    int fds[2], pid = 0;
01133 
01134    memset(buf, 0, len);
01135 
01136    if (pipe(fds)) {
01137       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01138    } else {
01139       /* good to go*/
01140       pid = ast_safe_fork(0);
01141 
01142       if (pid < 0) {
01143          /* ok maybe not */
01144          close(fds[0]);
01145          close(fds[1]);
01146          snprintf(buf, len, "FAILURE: Fork failed");
01147       } else if (pid) {
01148          /* parent */
01149          close(fds[1]);
01150          if (read(fds[0], buf, len) < 0) {
01151             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01152          }
01153          close(fds[0]);
01154       } else {
01155          /*  child */
01156          AST_DECLARE_APP_ARGS(arg,
01157             AST_APP_ARG(v)[20];
01158          );
01159          char *mycmd = ast_strdupa(command);
01160 
01161          close(fds[0]);
01162          dup2(fds[1], STDOUT_FILENO);
01163          close(fds[1]);
01164          ast_close_fds_above_n(STDOUT_FILENO);
01165 
01166          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01167 
01168          execv(arg.v[0], arg.v); 
01169          printf("FAILURE: %s", strerror(errno));
01170          _exit(0);
01171       }
01172    }
01173    return buf;
01174 }
01175 
01176 /*!
01177  * \brief Check that password meets minimum required length
01178  * \param vmu The voicemail user to change the password for.
01179  * \param password The password string to check
01180  *
01181  * \return zero on ok, 1 on not ok.
01182  */
01183 static int check_password(struct ast_vm_user *vmu, char *password)
01184 {
01185    /* check minimum length */
01186    if (strlen(password) < minpassword)
01187       return 1;
01188    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01189       char cmd[255], buf[255];
01190 
01191       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01192 
01193       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01194       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01195          ast_debug(5, "Result: %s\n", buf);
01196          if (!strncasecmp(buf, "VALID", 5)) {
01197             ast_debug(3, "Passed password check: '%s'\n", buf);
01198             return 0;
01199          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01200             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01201             return 0;
01202          } else {
01203             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01204             return 1;
01205          }
01206       }
01207    }
01208    return 0;
01209 }
01210 
01211 /*! 
01212  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01213  * \param vmu The voicemail user to change the password for.
01214  * \param password The new value to be set to the password for this user.
01215  * 
01216  * This only works if there is a realtime engine configured.
01217  * This is called from the (top level) vm_change_password.
01218  *
01219  * \return zero on success, -1 on error.
01220  */
01221 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01222 {
01223    int res = -1;
01224    if (!strcmp(vmu->password, password)) {
01225       /* No change (but an update would return 0 rows updated, so we opt out here) */
01226       return 0;
01227    }
01228 
01229    if (strlen(password) > 10) {
01230       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01231    }
01232    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01233       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01234       res = 0;
01235    }
01236    return res;
01237 }
01238 
01239 /*!
01240  * \brief Destructively Parse options and apply.
01241  */
01242 static void apply_options(struct ast_vm_user *vmu, const char *options)
01243 {  
01244    char *stringp;
01245    char *s;
01246    char *var, *value;
01247    stringp = ast_strdupa(options);
01248    while ((s = strsep(&stringp, "|"))) {
01249       value = s;
01250       if ((var = strsep(&value, "=")) && value) {
01251          apply_option(vmu, var, value);
01252       }
01253    }  
01254 }
01255 
01256 /*!
01257  * \brief Loads the options specific to a voicemail user.
01258  * 
01259  * This is called when a vm_user structure is being set up, such as from load_options.
01260  */
01261 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01262 {
01263    for (; var; var = var->next) {
01264       if (!strcasecmp(var->name, "vmsecret")) {
01265          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01266       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01267          if (ast_strlen_zero(retval->password))
01268             ast_copy_string(retval->password, var->value, sizeof(retval->password));
01269       } else if (!strcasecmp(var->name, "uniqueid")) {
01270          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01271       } else if (!strcasecmp(var->name, "pager")) {
01272          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01273       } else if (!strcasecmp(var->name, "email")) {
01274          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01275       } else if (!strcasecmp(var->name, "fullname")) {
01276          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01277       } else if (!strcasecmp(var->name, "context")) {
01278          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01279       } else if (!strcasecmp(var->name, "emailsubject")) {
01280          retval->emailsubject = ast_strdup(var->value);
01281       } else if (!strcasecmp(var->name, "emailbody")) {
01282          retval->emailbody = ast_strdup(var->value);
01283 #ifdef IMAP_STORAGE
01284       } else if (!strcasecmp(var->name, "imapuser")) {
01285          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01286          retval->imapversion = imapversion;
01287       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01288          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01289          retval->imapversion = imapversion;
01290       } else if (!strcasecmp(var->name, "imapfolder")) {
01291          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01292       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01293          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01294          retval->imapversion = imapversion;
01295 #endif
01296       } else
01297          apply_option(retval, var->name, var->value);
01298    }
01299 }
01300 
01301 /*!
01302  * \brief Determines if a DTMF key entered is valid.
01303  * \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.
01304  *
01305  * Tests the character entered against the set of valid DTMF characters. 
01306  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01307  */
01308 static int is_valid_dtmf(const char *key)
01309 {
01310    int i;
01311    char *local_key = ast_strdupa(key);
01312 
01313    for (i = 0; i < strlen(key); ++i) {
01314       if (!strchr(VALID_DTMF, *local_key)) {
01315          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01316          return 0;
01317       }
01318       local_key++;
01319    }
01320    return 1;
01321 }
01322 
01323 /*!
01324  * \brief Finds a voicemail user from the realtime engine.
01325  * \param ivm
01326  * \param context
01327  * \param mailbox
01328  *
01329  * 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.
01330  *
01331  * \return The ast_vm_user structure for the user that was found.
01332  */
01333 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01334 {
01335    struct ast_variable *var;
01336    struct ast_vm_user *retval;
01337 
01338    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01339       if (!ivm)
01340          ast_set_flag(retval, VM_ALLOCED);   
01341       else
01342          memset(retval, 0, sizeof(*retval));
01343       if (mailbox) 
01344          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01345       populate_defaults(retval);
01346       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01347          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01348       else
01349          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01350       if (var) {
01351          apply_options_full(retval, var);
01352          ast_variables_destroy(var);
01353       } else { 
01354          if (!ivm) 
01355             ast_free(retval);
01356          retval = NULL;
01357       }  
01358    } 
01359    return retval;
01360 }
01361 
01362 /*!
01363  * \brief Finds a voicemail user from the users file or the realtime engine.
01364  * \param ivm
01365  * \param context
01366  * \param mailbox
01367  * 
01368  * \return The ast_vm_user structure for the user that was found.
01369  */
01370 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01371 {
01372    /* This function could be made to generate one from a database, too */
01373    struct ast_vm_user *vmu = NULL, *cur;
01374    AST_LIST_LOCK(&users);
01375 
01376    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01377       context = "default";
01378 
01379    AST_LIST_TRAVERSE(&users, cur, list) {
01380 #ifdef IMAP_STORAGE
01381       if (cur->imapversion != imapversion) {
01382          continue;
01383       }
01384 #endif
01385       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01386          break;
01387       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01388          break;
01389    }
01390    if (cur) {
01391       /* Make a copy, so that on a reload, we have no race */
01392       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01393          memcpy(vmu, cur, sizeof(*vmu));
01394          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01395          AST_LIST_NEXT(vmu, list) = NULL;
01396       }
01397    } else
01398       vmu = find_user_realtime(ivm, context, mailbox);
01399    AST_LIST_UNLOCK(&users);
01400    return vmu;
01401 }
01402 
01403 /*!
01404  * \brief Resets a user password to a specified password.
01405  * \param context
01406  * \param mailbox
01407  * \param newpass
01408  *
01409  * This does the actual change password work, called by the vm_change_password() function.
01410  *
01411  * \return zero on success, -1 on error.
01412  */
01413 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01414 {
01415    /* This function could be made to generate one from a database, too */
01416    struct ast_vm_user *cur;
01417    int res = -1;
01418    AST_LIST_LOCK(&users);
01419    AST_LIST_TRAVERSE(&users, cur, list) {
01420       if ((!context || !strcasecmp(context, cur->context)) &&
01421          (!strcasecmp(mailbox, cur->mailbox)))
01422             break;
01423    }
01424    if (cur) {
01425       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01426       res = 0;
01427    }
01428    AST_LIST_UNLOCK(&users);
01429    return res;
01430 }
01431 
01432 /*! 
01433  * \brief The handler for the change password option.
01434  * \param vmu The voicemail user to work with.
01435  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01436  * 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.
01437  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01438  */
01439 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01440 {
01441    struct ast_config   *cfg = NULL;
01442    struct ast_variable *var = NULL;
01443    struct ast_category *cat = NULL;
01444    char *category = NULL, *value = NULL, *new = NULL;
01445    const char *tmp = NULL;
01446    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01447    char secretfn[PATH_MAX] = "";
01448    int found = 0;
01449 
01450    if (!change_password_realtime(vmu, newpassword))
01451       return;
01452 
01453    /* check if we should store the secret in the spool directory next to the messages */
01454    switch (vmu->passwordlocation) {
01455    case OPT_PWLOC_SPOOLDIR:
01456       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01457       if (write_password_to_file(secretfn, newpassword) == 0) {
01458          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01459          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01460          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01461          break;
01462       } else {
01463          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01464       }
01465       /* Fall-through */
01466    case OPT_PWLOC_VOICEMAILCONF:
01467       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01468          while ((category = ast_category_browse(cfg, category))) {
01469             if (!strcasecmp(category, vmu->context)) {
01470                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01471                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01472                   break;
01473                }
01474                value = strstr(tmp, ",");
01475                if (!value) {
01476                   new = alloca(strlen(newpassword)+1);
01477                   sprintf(new, "%s", newpassword);
01478                } else {
01479                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01480                   sprintf(new, "%s%s", newpassword, value);
01481                }
01482                if (!(cat = ast_category_get(cfg, category))) {
01483                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01484                   break;
01485                }
01486                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01487                found = 1;
01488             }
01489          }
01490          /* save the results */
01491          if (found) {
01492             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01493             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01494             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01495             break;
01496          }
01497       }
01498       /* Fall-through */
01499    case OPT_PWLOC_USERSCONF:
01500       /* check users.conf and update the password stored for the mailbox */
01501       /* if no vmsecret entry exists create one. */
01502       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01503          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01504          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01505             ast_debug(4, "users.conf: %s\n", category);
01506             if (!strcasecmp(category, vmu->mailbox)) {
01507                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01508                   ast_debug(3, "looks like we need to make vmsecret!\n");
01509                   var = ast_variable_new("vmsecret", newpassword, "");
01510                } else {
01511                   var = NULL;
01512                }
01513                new = alloca(strlen(newpassword) + 1);
01514                sprintf(new, "%s", newpassword);
01515                if (!(cat = ast_category_get(cfg, category))) {
01516                   ast_debug(4, "failed to get category!\n");
01517                   ast_free(var);
01518                   break;
01519                }
01520                if (!var) {
01521                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01522                } else {
01523                   ast_variable_append(cat, var);
01524                }
01525                found = 1;
01526                break;
01527             }
01528          }
01529          /* save the results and clean things up */
01530          if (found) {
01531             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01532             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01533             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01534          }
01535       }
01536    }
01537 }
01538 
01539 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01540 {
01541    char buf[255];
01542    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01543    if (!ast_safe_system(buf)) {
01544       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01545       /* Reset the password in memory, too */
01546       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01547    }
01548 }
01549 
01550 /*! 
01551  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01552  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01553  * \param len The length of the path string that was written out.
01554  * \param context
01555  * \param ext 
01556  * \param folder 
01557  * 
01558  * The path is constructed as 
01559  *    VM_SPOOL_DIRcontext/ext/folder
01560  *
01561  * \return zero on success, -1 on error.
01562  */
01563 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01564 {
01565    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01566 }
01567 
01568 /*! 
01569  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01570  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01571  * \param len The length of the path string that was written out.
01572  * \param dir 
01573  * \param num 
01574  * 
01575  * The path is constructed as 
01576  *    VM_SPOOL_DIRcontext/ext/folder
01577  *
01578  * \return zero on success, -1 on error.
01579  */
01580 static int make_file(char *dest, const int len, const char *dir, const int num)
01581 {
01582    return snprintf(dest, len, "%s/msg%04d", dir, num);
01583 }
01584 
01585 /* same as mkstemp, but return a FILE * */
01586 static FILE *vm_mkftemp(char *template)
01587 {
01588    FILE *p = NULL;
01589    int pfd = mkstemp(template);
01590    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01591    if (pfd > -1) {
01592       p = fdopen(pfd, "w+");
01593       if (!p) {
01594          close(pfd);
01595          pfd = -1;
01596       }
01597    }
01598    return p;
01599 }
01600 
01601 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01602  * \param dest    String. base directory.
01603  * \param len     Length of dest.
01604  * \param context String. Ignored if is null or empty string.
01605  * \param ext     String. Ignored if is null or empty string.
01606  * \param folder  String. Ignored if is null or empty string. 
01607  * \return -1 on failure, 0 on success.
01608  */
01609 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01610 {
01611    mode_t   mode = VOICEMAIL_DIR_MODE;
01612    int res;
01613 
01614    make_dir(dest, len, context, ext, folder);
01615    if ((res = ast_mkdir(dest, mode))) {
01616       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01617       return -1;
01618    }
01619    return 0;
01620 }
01621 
01622 static const char * const mailbox_folders[] = {
01623 #ifdef IMAP_STORAGE
01624    imapfolder,
01625 #else
01626    "INBOX",
01627 #endif
01628    "Old",
01629    "Work",
01630    "Family",
01631    "Friends",
01632    "Cust1",
01633    "Cust2",
01634    "Cust3",
01635    "Cust4",
01636    "Cust5",
01637    "Deleted",
01638    "Urgent",
01639 };
01640 
01641 static const char *mbox(struct ast_vm_user *vmu, int id)
01642 {
01643 #ifdef IMAP_STORAGE
01644    if (vmu && id == 0) {
01645       return vmu->imapfolder;
01646    }
01647 #endif
01648    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01649 }
01650 
01651 static int get_folder_by_name(const char *name)
01652 {
01653    size_t i;
01654 
01655    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01656       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01657          return i;
01658       }
01659    }
01660 
01661    return -1;
01662 }
01663 
01664 static void free_user(struct ast_vm_user *vmu)
01665 {
01666    if (ast_test_flag(vmu, VM_ALLOCED)) {
01667       if (vmu->emailbody != NULL) {
01668          ast_free(vmu->emailbody);
01669          vmu->emailbody = NULL;
01670       }
01671       if (vmu->emailsubject != NULL) {
01672          ast_free(vmu->emailsubject);
01673          vmu->emailsubject = NULL;
01674       }
01675       ast_free(vmu);
01676    }
01677 }
01678 
01679 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01680 
01681    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01682    if (!vms->dh_arraysize) {
01683       /* initial allocation */
01684       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01685          return -1;
01686       }
01687       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01688          return -1;
01689       }
01690       vms->dh_arraysize = arraysize;
01691    } else if (vms->dh_arraysize < arraysize) {
01692       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01693          return -1;
01694       }
01695       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01696          return -1;
01697       }
01698       memset(vms->deleted, 0, arraysize * sizeof(int));
01699       memset(vms->heard, 0, arraysize * sizeof(int));
01700       vms->dh_arraysize = arraysize;
01701    }
01702 
01703    return 0;
01704 }
01705 
01706 /* All IMAP-specific functions should go in this block. This
01707  * keeps them from being spread out all over the code */
01708 #ifdef IMAP_STORAGE
01709 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01710 {
01711    char arg[10];
01712    struct vm_state *vms;
01713    unsigned long messageNum;
01714 
01715    /* If greetings aren't stored in IMAP, just delete the file */
01716    if (msgnum < 0 && !imapgreetings) {
01717       ast_filedelete(file, NULL);
01718       return;
01719    }
01720 
01721    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01722       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);
01723       return;
01724    }
01725 
01726    /* find real message number based on msgnum */
01727    /* this may be an index into vms->msgArray based on the msgnum. */
01728    messageNum = vms->msgArray[msgnum];
01729    if (messageNum == 0) {
01730       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01731       return;
01732    }
01733    if (option_debug > 2)
01734       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01735    /* delete message */
01736    snprintf (arg, sizeof(arg), "%lu", messageNum);
01737    ast_mutex_lock(&vms->lock);
01738    mail_setflag (vms->mailstream, arg, "\\DELETED");
01739    mail_expunge(vms->mailstream);
01740    ast_mutex_unlock(&vms->lock);
01741 }
01742 
01743 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01744 {
01745    struct vm_state *vms_p;
01746    char *file, *filename;
01747    char *attachment;
01748    int ret = 0, i;
01749    BODY *body;
01750 
01751    /* This function is only used for retrieval of IMAP greetings
01752     * regular messages are not retrieved this way, nor are greetings
01753     * if they are stored locally*/
01754    if (msgnum > -1 || !imapgreetings) {
01755       return 0;
01756    } else {
01757       file = strrchr(ast_strdupa(dir), '/');
01758       if (file)
01759          *file++ = '\0';
01760       else {
01761          ast_debug (1, "Failed to procure file name from directory passed.\n");
01762          return -1;
01763       }
01764    }
01765 
01766    /* check if someone is accessing this box right now... */
01767    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01768       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01769       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01770       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01771       * that's all we need to do.
01772       */
01773       if (!(vms_p = create_vm_state_from_user(vmu))) {
01774          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01775          return -1;
01776       }
01777    }
01778    
01779    /* Greetings will never have a prepended message */
01780    *vms_p->introfn = '\0';
01781 
01782    ast_mutex_lock(&vms_p->lock);
01783    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01784    if (!vms_p->mailstream) {
01785       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01786       ast_mutex_unlock(&vms_p->lock);
01787       return -1;
01788    }
01789 
01790    /*XXX Yuck, this could probably be done a lot better */
01791    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01792       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01793       /* We have the body, now we extract the file name of the first attachment. */
01794       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01795          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01796       } else {
01797          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01798          ast_mutex_unlock(&vms_p->lock);
01799          return -1;
01800       }
01801       filename = strsep(&attachment, ".");
01802       if (!strcmp(filename, file)) {
01803          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01804          vms_p->msgArray[vms_p->curmsg] = i + 1;
01805          save_body(body, vms_p, "2", attachment, 0);
01806          ast_mutex_unlock(&vms_p->lock);
01807          return 0;
01808       }
01809    }
01810    ast_mutex_unlock(&vms_p->lock);
01811 
01812    return -1;
01813 }
01814 
01815 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01816 {
01817    BODY *body;
01818    char *header_content;
01819    char *attachedfilefmt;
01820    char buf[80];
01821    struct vm_state *vms;
01822    char text_file[PATH_MAX];
01823    FILE *text_file_ptr;
01824    int res = 0;
01825    struct ast_vm_user *vmu;
01826 
01827    if (!(vmu = find_user(NULL, context, mailbox))) {
01828       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01829       return -1;
01830    }
01831    
01832    if (msgnum < 0) {
01833       if (imapgreetings) {
01834          res = imap_retrieve_greeting(dir, msgnum, vmu);
01835          goto exit;
01836       } else {
01837          res = 0;
01838          goto exit;
01839       }
01840    }
01841 
01842    /* Before anything can happen, we need a vm_state so that we can
01843     * actually access the imap server through the vms->mailstream
01844     */
01845    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01846       /* This should not happen. If it does, then I guess we'd
01847        * need to create the vm_state, extract which mailbox to
01848        * open, and then set up the msgArray so that the correct
01849        * IMAP message could be accessed. If I have seen correctly
01850        * though, the vms should be obtainable from the vmstates list
01851        * and should have its msgArray properly set up.
01852        */
01853       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01854       res = -1;
01855       goto exit;
01856    }
01857    
01858    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01859    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01860 
01861    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01862    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01863       res = 0;
01864       goto exit;
01865    }
01866 
01867    if (option_debug > 2)
01868       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01869    if (vms->msgArray[msgnum] == 0) {
01870       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01871       res = -1;
01872       goto exit;
01873    }
01874 
01875    /* This will only work for new messages... */
01876    ast_mutex_lock(&vms->lock);
01877    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01878    ast_mutex_unlock(&vms->lock);
01879    /* empty string means no valid header */
01880    if (ast_strlen_zero(header_content)) {
01881       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01882       res = -1;
01883       goto exit;
01884    }
01885 
01886    ast_mutex_lock(&vms->lock);
01887    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01888    ast_mutex_unlock(&vms->lock);
01889 
01890    /* We have the body, now we extract the file name of the first attachment. */
01891    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01892       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01893    } else {
01894       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01895       res = -1;
01896       goto exit;
01897    }
01898    
01899    /* Find the format of the attached file */
01900 
01901    strsep(&attachedfilefmt, ".");
01902    if (!attachedfilefmt) {
01903       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01904       res = -1;
01905       goto exit;
01906    }
01907    
01908    save_body(body, vms, "2", attachedfilefmt, 0);
01909    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01910       *vms->introfn = '\0';
01911    }
01912 
01913    /* Get info from headers!! */
01914    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01915 
01916    if (!(text_file_ptr = fopen(text_file, "w"))) {
01917       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01918    }
01919 
01920    fprintf(text_file_ptr, "%s\n", "[message]");
01921 
01922    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01923    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01924    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01925    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01926    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01927    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01928    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01929    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01930    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01931    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01932    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01933    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01934    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01935    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01936    fclose(text_file_ptr);
01937 
01938 exit:
01939    free_user(vmu);
01940    return res;
01941 }
01942 
01943 static int folder_int(const char *folder)
01944 {
01945    /*assume a NULL folder means INBOX*/
01946    if (!folder) {
01947       return 0;
01948    }
01949    if (!strcasecmp(folder, imapfolder)) {
01950       return 0;
01951    } else if (!strcasecmp(folder, "Old")) {
01952       return 1;
01953    } else if (!strcasecmp(folder, "Work")) {
01954       return 2;
01955    } else if (!strcasecmp(folder, "Family")) {
01956       return 3;
01957    } else if (!strcasecmp(folder, "Friends")) {
01958       return 4;
01959    } else if (!strcasecmp(folder, "Cust1")) {
01960       return 5;
01961    } else if (!strcasecmp(folder, "Cust2")) {
01962       return 6;
01963    } else if (!strcasecmp(folder, "Cust3")) {
01964       return 7;
01965    } else if (!strcasecmp(folder, "Cust4")) {
01966       return 8;
01967    } else if (!strcasecmp(folder, "Cust5")) {
01968       return 9;
01969    } else if (!strcasecmp(folder, "Urgent")) {
01970       return 11;
01971    } else { /*assume they meant INBOX if folder is not found otherwise*/
01972       return 0;
01973    }
01974 }
01975 
01976 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01977 {
01978    SEARCHPGM *pgm;
01979    SEARCHHEADER *hdr;
01980 
01981    struct ast_vm_user *vmu, vmus;
01982    struct vm_state *vms_p;
01983    int ret = 0;
01984    int fold = folder_int(folder);
01985    int urgent = 0;
01986    
01987    /* If URGENT, then look at INBOX */
01988    if (fold == 11) {
01989       fold = NEW_FOLDER;
01990       urgent = 1;
01991    }
01992 
01993    if (ast_strlen_zero(mailbox))
01994       return 0;
01995 
01996    /* We have to get the user before we can open the stream! */
01997    vmu = find_user(&vmus, context, mailbox);
01998    if (!vmu) {
01999       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02000       return -1;
02001    } else {
02002       /* No IMAP account available */
02003       if (vmu->imapuser[0] == '\0') {
02004          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02005          return -1;
02006       }
02007    }
02008    
02009    /* No IMAP account available */
02010    if (vmu->imapuser[0] == '\0') {
02011       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02012       free_user(vmu);
02013       return -1;
02014    }
02015 
02016    /* check if someone is accessing this box right now... */
02017    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02018    if (!vms_p) {
02019       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02020    }
02021    if (vms_p) {
02022       ast_debug(3, "Returning before search - user is logged in\n");
02023       if (fold == 0) { /* INBOX */
02024          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02025       }
02026       if (fold == 1) { /* Old messages */
02027          return vms_p->oldmessages;
02028       }
02029    }
02030 
02031    /* add one if not there... */
02032    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02033    if (!vms_p) {
02034       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02035    }
02036 
02037    if (!vms_p) {
02038       vms_p = create_vm_state_from_user(vmu);
02039    }
02040    ret = init_mailstream(vms_p, fold);
02041    if (!vms_p->mailstream) {
02042       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02043       return -1;
02044    }
02045    if (ret == 0) {
02046       ast_mutex_lock(&vms_p->lock);
02047       pgm = mail_newsearchpgm ();
02048       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02049       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02050       pgm->header = hdr;
02051       if (fold != OLD_FOLDER) {
02052          pgm->unseen = 1;
02053          pgm->seen = 0;
02054       }
02055       /* In the special case where fold is 1 (old messages) we have to do things a bit
02056        * differently. Old messages are stored in the INBOX but are marked as "seen"
02057        */
02058       else {
02059          pgm->unseen = 0;
02060          pgm->seen = 1;
02061       }
02062       /* look for urgent messages */
02063       if (fold == NEW_FOLDER) {
02064          if (urgent) {
02065             pgm->flagged = 1;
02066             pgm->unflagged = 0;
02067          } else {
02068             pgm->flagged = 0;
02069             pgm->unflagged = 1;
02070          }
02071       }
02072       pgm->undeleted = 1;
02073       pgm->deleted = 0;
02074 
02075       vms_p->vmArrayIndex = 0;
02076       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02077       if (fold == 0 && urgent == 0)
02078          vms_p->newmessages = vms_p->vmArrayIndex;
02079       if (fold == 1)
02080          vms_p->oldmessages = vms_p->vmArrayIndex;
02081       if (fold == 0 && urgent == 1)
02082          vms_p->urgentmessages = vms_p->vmArrayIndex;
02083       /*Freeing the searchpgm also frees the searchhdr*/
02084       mail_free_searchpgm(&pgm);
02085       ast_mutex_unlock(&vms_p->lock);
02086       vms_p->updated = 0;
02087       return vms_p->vmArrayIndex;
02088    } else {
02089       ast_mutex_lock(&vms_p->lock);
02090       mail_ping(vms_p->mailstream);
02091       ast_mutex_unlock(&vms_p->lock);
02092    }
02093    return 0;
02094 }
02095 
02096 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02097 {
02098    /* Check if mailbox is full */
02099    check_quota(vms, vmu->imapfolder);
02100    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02101       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02102       ast_play_and_wait(chan, "vm-mailboxfull");
02103       return -1;
02104    }
02105    
02106    /* Check if we have exceeded maxmsg */
02107    ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
02108    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02109       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02110       ast_play_and_wait(chan, "vm-mailboxfull");
02111       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02112       return -1;
02113    }
02114 
02115    return 0;
02116 }
02117 
02118 /*!
02119  * \brief Gets the number of messages that exist in a mailbox folder.
02120  * \param context
02121  * \param mailbox
02122  * \param folder
02123  * 
02124  * This method is used when IMAP backend is used.
02125  * \return The number of messages in this mailbox folder (zero or more).
02126  */
02127 static int messagecount(const char *context, const char *mailbox, const char *folder)
02128 {
02129    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02130       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02131    } else {
02132       return __messagecount(context, mailbox, folder);
02133    }
02134 }
02135 
02136 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)
02137 {
02138    char *myserveremail = serveremail;
02139    char fn[PATH_MAX];
02140    char introfn[PATH_MAX];
02141    char mailbox[256];
02142    char *stringp;
02143    FILE *p = NULL;
02144    char tmp[80] = "/tmp/astmail-XXXXXX";
02145    long len;
02146    void *buf;
02147    int tempcopy = 0;
02148    STRING str;
02149    int ret; /* for better error checking */
02150    char *imap_flags = NIL;
02151    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02152 
02153     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02154     if (msgnum < 0 && !imapgreetings) {
02155         return 0;
02156     }
02157    
02158    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02159       return -1;
02160    }
02161 
02162    /* Set urgent flag for IMAP message */
02163    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02164       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02165       imap_flags = "\\FLAGGED";
02166    }
02167    
02168    /* Attach only the first format */
02169    fmt = ast_strdupa(fmt);
02170    stringp = fmt;
02171    strsep(&stringp, "|");
02172 
02173    if (!ast_strlen_zero(vmu->serveremail))
02174       myserveremail = vmu->serveremail;
02175 
02176    if (msgnum > -1)
02177       make_file(fn, sizeof(fn), dir, msgnum);
02178    else
02179       ast_copy_string (fn, dir, sizeof(fn));
02180 
02181    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02182    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02183       *introfn = '\0';
02184    }
02185    
02186    if (ast_strlen_zero(vmu->email)) {
02187       /* We need the vmu->email to be set when we call make_email_file, but
02188        * if we keep it set, a duplicate e-mail will be created. So at the end
02189        * of this function, we will revert back to an empty string if tempcopy
02190        * is 1.
02191        */
02192       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02193       tempcopy = 1;
02194    }
02195 
02196    if (!strcmp(fmt, "wav49"))
02197       fmt = "WAV";
02198    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02199 
02200    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02201       command hangs. */
02202    if (!(p = vm_mkftemp(tmp))) {
02203       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02204       if (tempcopy)
02205          *(vmu->email) = '\0';
02206       return -1;
02207    }
02208 
02209    if (msgnum < 0 && imapgreetings) {
02210       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02211          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02212          return -1;
02213       }
02214       imap_delete_old_greeting(fn, vms);
02215    }
02216 
02217    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02218       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02219       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02220       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02221    /* read mail file to memory */
02222    len = ftell(p);
02223    rewind(p);
02224    if (!(buf = ast_malloc(len + 1))) {
02225       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02226       fclose(p);
02227       if (tempcopy)
02228          *(vmu->email) = '\0';
02229       return -1;
02230    }
02231    if (fread(buf, len, 1, p) < len) {
02232       if (ferror(p)) {
02233          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02234          return -1;
02235       }
02236    }
02237    ((char *) buf)[len] = '\0';
02238    INIT(&str, mail_string, buf, len);
02239    ret = init_mailstream(vms, NEW_FOLDER);
02240    if (ret == 0) {
02241       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
02242       ast_mutex_lock(&vms->lock);
02243       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02244          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02245       ast_mutex_unlock(&vms->lock);
02246       fclose(p);
02247       unlink(tmp);
02248       ast_free(buf);
02249    } else {
02250       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02251       fclose(p);
02252       unlink(tmp);
02253       ast_free(buf);
02254       return -1;
02255    }
02256    ast_debug(3, "%s stored\n", fn);
02257    
02258    if (tempcopy)
02259       *(vmu->email) = '\0';
02260    inprocess_count(vmu->mailbox, vmu->context, -1);
02261    return 0;
02262 
02263 }
02264 
02265 /*!
02266  * \brief Gets the number of messages that exist in the inbox folder.
02267  * \param mailbox_context
02268  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02269  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02270  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02271  * 
02272  * This method is used when IMAP backend is used.
02273  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02274  *
02275  * \return zero on success, -1 on error.
02276  */
02277 
02278 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02279 {
02280    char tmp[PATH_MAX] = "";
02281    char *mailboxnc;
02282    char *context;
02283    char *mb;
02284    char *cur;
02285    if (newmsgs)
02286       *newmsgs = 0;
02287    if (oldmsgs)
02288       *oldmsgs = 0;
02289    if (urgentmsgs)
02290       *urgentmsgs = 0;
02291 
02292    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02293    /* If no mailbox, return immediately */
02294    if (ast_strlen_zero(mailbox_context))
02295       return 0;
02296    
02297    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02298    context = strchr(tmp, '@');
02299    if (strchr(mailbox_context, ',')) {
02300       int tmpnew, tmpold, tmpurgent;
02301       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02302       mb = tmp;
02303       while ((cur = strsep(&mb, ", "))) {
02304          if (!ast_strlen_zero(cur)) {
02305             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02306                return -1;
02307             else {
02308                if (newmsgs)
02309                   *newmsgs += tmpnew; 
02310                if (oldmsgs)
02311                   *oldmsgs += tmpold;
02312                if (urgentmsgs)
02313                   *urgentmsgs += tmpurgent;
02314             }
02315          }
02316       }
02317       return 0;
02318    }
02319    if (context) {
02320       *context = '\0';
02321       mailboxnc = tmp;
02322       context++;
02323    } else {
02324       context = "default";
02325       mailboxnc = (char *) mailbox_context;
02326    }
02327 
02328    if (newmsgs) {
02329       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02330       if (!vmu) {
02331          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02332          return -1;
02333       }
02334       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02335          return -1;
02336       }
02337    }
02338    if (oldmsgs) {
02339       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02340          return -1;
02341       }
02342    }
02343    if (urgentmsgs) {
02344       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02345          return -1;
02346       }
02347    }
02348    return 0;
02349 }
02350 
02351 /** 
02352  * \brief Determines if the given folder has messages.
02353  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02354  * \param folder the folder to look in
02355  *
02356  * This function is used when the mailbox is stored in an IMAP back end.
02357  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02358  * \return 1 if the folder has one or more messages. zero otherwise.
02359  */
02360 
02361 static int has_voicemail(const char *mailbox, const char *folder)
02362 {
02363    char tmp[256], *tmp2, *box, *context;
02364    ast_copy_string(tmp, mailbox, sizeof(tmp));
02365    tmp2 = tmp;
02366    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02367       while ((box = strsep(&tmp2, ",&"))) {
02368          if (!ast_strlen_zero(box)) {
02369             if (has_voicemail(box, folder)) {
02370                return 1;
02371             }
02372          }
02373       }
02374    }
02375    if ((context = strchr(tmp, '@'))) {
02376       *context++ = '\0';
02377    } else {
02378       context = "default";
02379    }
02380    return __messagecount(context, tmp, folder) ? 1 : 0;
02381 }
02382 
02383 /*!
02384  * \brief Copies a message from one mailbox to another.
02385  * \param chan
02386  * \param vmu
02387  * \param imbox
02388  * \param msgnum
02389  * \param duration
02390  * \param recip
02391  * \param fmt
02392  * \param dir
02393  *
02394  * This works with IMAP storage based mailboxes.
02395  *
02396  * \return zero on success, -1 on error.
02397  */
02398 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)
02399 {
02400    struct vm_state *sendvms = NULL, *destvms = NULL;
02401    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02402    if (msgnum >= recip->maxmsg) {
02403       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02404       return -1;
02405    }
02406    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02407       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02408       return -1;
02409    }
02410    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02411       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02412       return -1;
02413    }
02414    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02415    ast_mutex_lock(&sendvms->lock);
02416    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02417       ast_mutex_unlock(&sendvms->lock);
02418       return 0;
02419    }
02420    ast_mutex_unlock(&sendvms->lock);
02421    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02422    return -1;
02423 }
02424 
02425 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02426 {
02427    char tmp[256], *t = tmp;
02428    size_t left = sizeof(tmp);
02429    
02430    if (box == OLD_FOLDER) {
02431       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02432    } else {
02433       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02434    }
02435 
02436    if (box == NEW_FOLDER) {
02437       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02438    } else {
02439       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02440    }
02441 
02442    /* Build up server information */
02443    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02444 
02445    /* Add authentication user if present */
02446    if (!ast_strlen_zero(authuser))
02447       ast_build_string(&t, &left, "/authuser=%s", authuser);
02448 
02449    /* Add flags if present */
02450    if (!ast_strlen_zero(imapflags))
02451       ast_build_string(&t, &left, "/%s", imapflags);
02452 
02453    /* End with username */
02454 #if 1
02455    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02456 #else
02457    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02458 #endif
02459    if (box == NEW_FOLDER || box == OLD_FOLDER)
02460       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02461    else if (box == GREETINGS_FOLDER)
02462       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02463    else {   /* Other folders such as Friends, Family, etc... */
02464       if (!ast_strlen_zero(imapparentfolder)) {
02465          /* imapparentfolder would typically be set to INBOX */
02466          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02467       } else {
02468          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02469       }
02470    }
02471 }
02472 
02473 static int init_mailstream(struct vm_state *vms, int box)
02474 {
02475    MAILSTREAM *stream = NIL;
02476    long debug;
02477    char tmp[256];
02478    
02479    if (!vms) {
02480       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02481       return -1;
02482    }
02483    if (option_debug > 2)
02484       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02485    if (vms->mailstream == NIL || !vms->mailstream) {
02486       if (option_debug)
02487          ast_log(LOG_DEBUG, "mailstream not set.\n");
02488    } else {
02489       stream = vms->mailstream;
02490    }
02491    /* debug = T;  user wants protocol telemetry? */
02492    debug = NIL;  /* NO protocol telemetry? */
02493 
02494    if (delimiter == '\0') {      /* did not probe the server yet */
02495       char *cp;
02496 #ifdef USE_SYSTEM_IMAP
02497 #include <imap/linkage.c>
02498 #elif defined(USE_SYSTEM_CCLIENT)
02499 #include <c-client/linkage.c>
02500 #else
02501 #include "linkage.c"
02502 #endif
02503       /* Connect to INBOX first to get folders delimiter */
02504       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02505       ast_mutex_lock(&vms->lock);
02506       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02507       ast_mutex_unlock(&vms->lock);
02508       if (stream == NIL) {
02509          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02510          return -1;
02511       }
02512       get_mailbox_delimiter(stream);
02513       /* update delimiter in imapfolder */
02514       for (cp = vms->imapfolder; *cp; cp++)
02515          if (*cp == '/')
02516             *cp = delimiter;
02517    }
02518    /* Now connect to the target folder */
02519    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02520    if (option_debug > 2)
02521       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02522    ast_mutex_lock(&vms->lock);
02523    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02524    ast_mutex_unlock(&vms->lock);
02525    if (vms->mailstream == NIL) {
02526       return -1;
02527    } else {
02528       return 0;
02529    }
02530 }
02531 
02532 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02533 {
02534    SEARCHPGM *pgm;
02535    SEARCHHEADER *hdr;
02536    int ret, urgent = 0;
02537 
02538    /* If Urgent, then look at INBOX */
02539    if (box == 11) {
02540       box = NEW_FOLDER;
02541       urgent = 1;
02542    }
02543 
02544    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02545    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02546    vms->imapversion = vmu->imapversion;
02547    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02548 
02549    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02550       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02551       return -1;
02552    }
02553    
02554    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02555    
02556    /* Check Quota */
02557    if  (box == 0)  {
02558       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02559       check_quota(vms, (char *) mbox(vmu, box));
02560    }
02561 
02562    ast_mutex_lock(&vms->lock);
02563    pgm = mail_newsearchpgm();
02564 
02565    /* Check IMAP folder for Asterisk messages only... */
02566    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02567    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02568    pgm->header = hdr;
02569    pgm->deleted = 0;
02570    pgm->undeleted = 1;
02571 
02572    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02573    if (box == NEW_FOLDER && urgent == 1) {
02574       pgm->unseen = 1;
02575       pgm->seen = 0;
02576       pgm->flagged = 1;
02577       pgm->unflagged = 0;
02578    } else if (box == NEW_FOLDER && urgent == 0) {
02579       pgm->unseen = 1;
02580       pgm->seen = 0;
02581       pgm->flagged = 0;
02582       pgm->unflagged = 1;
02583    } else if (box == OLD_FOLDER) {
02584       pgm->seen = 1;
02585       pgm->unseen = 0;
02586    }
02587 
02588    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02589 
02590    vms->vmArrayIndex = 0;
02591    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02592    vms->lastmsg = vms->vmArrayIndex - 1;
02593    mail_free_searchpgm(&pgm);
02594    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02595     * ensure to allocate enough space to account for all of them. Warn if old messages
02596     * have not been checked first as that is required.
02597     */
02598    if (box == 0 && !vms->dh_arraysize) {
02599       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02600    }
02601    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02602       ast_mutex_unlock(&vms->lock);
02603       return -1;
02604    }
02605 
02606    ast_mutex_unlock(&vms->lock);
02607    return 0;
02608 }
02609 
02610 static void write_file(char *filename, char *buffer, unsigned long len)
02611 {
02612    FILE *output;
02613 
02614    output = fopen (filename, "w");
02615    if (fwrite(buffer, len, 1, output) != 1) {
02616       if (ferror(output)) {
02617          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02618       }
02619    }
02620    fclose (output);
02621 }
02622 
02623 static void update_messages_by_imapuser(const char *user, unsigned long number)
02624 {
02625    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02626 
02627    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02628       return;
02629    }
02630 
02631    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02632    vms->msgArray[vms->vmArrayIndex++] = number;
02633 }
02634 
02635 void mm_searched(MAILSTREAM *stream, unsigned long number)
02636 {
02637    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02638 
02639    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02640       return;
02641 
02642    update_messages_by_imapuser(user, number);
02643 }
02644 
02645 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02646 {
02647    struct ast_variable *var;
02648    struct ast_vm_user *vmu;
02649 
02650    vmu = ast_calloc(1, sizeof *vmu);
02651    if (!vmu)
02652       return NULL;
02653    ast_set_flag(vmu, VM_ALLOCED);
02654    populate_defaults(vmu);
02655 
02656    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02657    if (var) {
02658       apply_options_full(vmu, var);
02659       ast_variables_destroy(var);
02660       return vmu;
02661    } else {
02662       ast_free(vmu);
02663       return NULL;
02664    }
02665 }
02666 
02667 /* Interfaces to C-client */
02668 
02669 void mm_exists(MAILSTREAM * stream, unsigned long number)
02670 {
02671    /* mail_ping will callback here if new mail! */
02672    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02673    if (number == 0) return;
02674    set_update(stream);
02675 }
02676 
02677 
02678 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02679 {
02680    /* mail_ping will callback here if expunged mail! */
02681    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02682    if (number == 0) return;
02683    set_update(stream);
02684 }
02685 
02686 
02687 void mm_flags(MAILSTREAM * stream, unsigned long number)
02688 {
02689    /* mail_ping will callback here if read mail! */
02690    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02691    if (number == 0) return;
02692    set_update(stream);
02693 }
02694 
02695 
02696 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02697 {
02698    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02699    mm_log (string, errflg);
02700 }
02701 
02702 
02703 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02704 {
02705    if (delimiter == '\0') {
02706       delimiter = delim;
02707    }
02708 
02709    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02710    if (attributes & LATT_NOINFERIORS)
02711       ast_debug(5, "no inferiors\n");
02712    if (attributes & LATT_NOSELECT)
02713       ast_debug(5, "no select\n");
02714    if (attributes & LATT_MARKED)
02715       ast_debug(5, "marked\n");
02716    if (attributes & LATT_UNMARKED)
02717       ast_debug(5, "unmarked\n");
02718 }
02719 
02720 
02721 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02722 {
02723    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02724    if (attributes & LATT_NOINFERIORS)
02725       ast_debug(5, "no inferiors\n");
02726    if (attributes & LATT_NOSELECT)
02727       ast_debug(5, "no select\n");
02728    if (attributes & LATT_MARKED)
02729       ast_debug(5, "marked\n");
02730    if (attributes & LATT_UNMARKED)
02731       ast_debug(5, "unmarked\n");
02732 }
02733 
02734 
02735 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02736 {
02737    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02738    if (status->flags & SA_MESSAGES)
02739       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02740    if (status->flags & SA_RECENT)
02741       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02742    if (status->flags & SA_UNSEEN)
02743       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02744    if (status->flags & SA_UIDVALIDITY)
02745       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02746    if (status->flags & SA_UIDNEXT)
02747       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02748    ast_log(AST_LOG_NOTICE, "\n");
02749 }
02750 
02751 
02752 void mm_log(char *string, long errflg)
02753 {
02754    switch ((short) errflg) {
02755       case NIL:
02756          ast_debug(1, "IMAP Info: %s\n", string);
02757          break;
02758       case PARSE:
02759       case WARN:
02760          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02761          break;
02762       case ERROR:
02763          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02764          break;
02765    }
02766 }
02767 
02768 
02769 void mm_dlog(char *string)
02770 {
02771    ast_log(AST_LOG_NOTICE, "%s\n", string);
02772 }
02773 
02774 
02775 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02776 {
02777    struct ast_vm_user *vmu;
02778 
02779    ast_debug(4, "Entering callback mm_login\n");
02780 
02781    ast_copy_string(user, mb->user, MAILTMPLEN);
02782 
02783    /* We should only do this when necessary */
02784    if (!ast_strlen_zero(authpassword)) {
02785       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02786    } else {
02787       AST_LIST_TRAVERSE(&users, vmu, list) {
02788          if (!strcasecmp(mb->user, vmu->imapuser)) {
02789             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02790             break;
02791          }
02792       }
02793       if (!vmu) {
02794          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02795             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02796             free_user(vmu);
02797          }
02798       }
02799    }
02800 }
02801 
02802 
02803 void mm_critical(MAILSTREAM * stream)
02804 {
02805 }
02806 
02807 
02808 void mm_nocritical(MAILSTREAM * stream)
02809 {
02810 }
02811 
02812 
02813 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02814 {
02815    kill (getpid (), SIGSTOP);
02816    return NIL;
02817 }
02818 
02819 
02820 void mm_fatal(char *string)
02821 {
02822    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02823 }
02824 
02825 /* C-client callback to handle quota */
02826 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02827 {
02828    struct vm_state *vms;
02829    char *mailbox = stream->mailbox, *user;
02830    char buf[1024] = "";
02831    unsigned long usage = 0, limit = 0;
02832    
02833    while (pquota) {
02834       usage = pquota->usage;
02835       limit = pquota->limit;
02836       pquota = pquota->next;
02837    }
02838    
02839    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)))) {
02840       ast_log(AST_LOG_ERROR, "No state found.\n");
02841       return;
02842    }
02843 
02844    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02845 
02846    vms->quota_usage = usage;
02847    vms->quota_limit = limit;
02848 }
02849 
02850 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02851 {
02852    char *start, *eol_pnt;
02853    int taglen;
02854 
02855    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02856       return NULL;
02857 
02858    taglen = strlen(tag) + 1;
02859    if (taglen < 1)
02860       return NULL;
02861 
02862    if (!(start = strstr(header, tag)))
02863       return NULL;
02864 
02865    /* Since we can be called multiple times we should clear our buffer */
02866    memset(buf, 0, len);
02867 
02868    ast_copy_string(buf, start+taglen, len);
02869    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02870       *eol_pnt = '\0';
02871    return buf;
02872 }
02873 
02874 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02875 {
02876    char *start, *quote, *eol_pnt;
02877 
02878    if (ast_strlen_zero(mailbox))
02879       return NULL;
02880 
02881    if (!(start = strstr(mailbox, "/user=")))
02882       return NULL;
02883 
02884    ast_copy_string(buf, start+6, len);
02885 
02886    if (!(quote = strchr(buf, '\"'))) {
02887       if (!(eol_pnt = strchr(buf, '/')))
02888          eol_pnt = strchr(buf,'}');
02889       *eol_pnt = '\0';
02890       return buf;
02891    } else {
02892       eol_pnt = strchr(buf+1,'\"');
02893       *eol_pnt = '\0';
02894       return buf+1;
02895    }
02896 }
02897 
02898 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02899 {
02900    struct vm_state *vms_p;
02901 
02902    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02903    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02904       return vms_p;
02905    }
02906    if (option_debug > 4)
02907       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02908    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02909       return NULL;
02910    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02911    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02912    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02913    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02914    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02915    vms_p->imapversion = vmu->imapversion;
02916    if (option_debug > 4)
02917       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02918    vms_p->updated = 1;
02919    /* set mailbox to INBOX! */
02920    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02921    init_vm_state(vms_p);
02922    vmstate_insert(vms_p);
02923    return vms_p;
02924 }
02925 
02926 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02927 {
02928    struct vmstate *vlist = NULL;
02929 
02930    if (interactive) {
02931       struct vm_state *vms;
02932       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02933       vms = pthread_getspecific(ts_vmstate.key);
02934       return vms;
02935    }
02936 
02937    AST_LIST_LOCK(&vmstates);
02938    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02939       if (!vlist->vms) {
02940          ast_debug(3, "error: vms is NULL for %s\n", user);
02941          continue;
02942       }
02943       if (vlist->vms->imapversion != imapversion) {
02944          continue;
02945       }
02946       if (!vlist->vms->imapuser) {
02947          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02948          continue;
02949       }
02950 
02951       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02952          AST_LIST_UNLOCK(&vmstates);
02953          return vlist->vms;
02954       }
02955    }
02956    AST_LIST_UNLOCK(&vmstates);
02957 
02958    ast_debug(3, "%s not found in vmstates\n", user);
02959 
02960    return NULL;
02961 }
02962 
02963 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02964 {
02965 
02966    struct vmstate *vlist = NULL;
02967    const char *local_context = S_OR(context, "default");
02968 
02969    if (interactive) {
02970       struct vm_state *vms;
02971       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02972       vms = pthread_getspecific(ts_vmstate.key);
02973       return vms;
02974    }
02975 
02976    AST_LIST_LOCK(&vmstates);
02977    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02978       if (!vlist->vms) {
02979          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02980          continue;
02981       }
02982       if (vlist->vms->imapversion != imapversion) {
02983          continue;
02984       }
02985       if (!vlist->vms->username || !vlist->vms->context) {
02986          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02987          continue;
02988       }
02989 
02990       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);
02991       
02992       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02993          ast_debug(3, "Found it!\n");
02994          AST_LIST_UNLOCK(&vmstates);
02995          return vlist->vms;
02996       }
02997    }
02998    AST_LIST_UNLOCK(&vmstates);
02999 
03000    ast_debug(3, "%s not found in vmstates\n", mailbox);
03001 
03002    return NULL;
03003 }
03004 
03005 static void vmstate_insert(struct vm_state *vms) 
03006 {
03007    struct vmstate *v;
03008    struct vm_state *altvms;
03009 
03010    /* If interactive, it probably already exists, and we should
03011       use the one we already have since it is more up to date.
03012       We can compare the username to find the duplicate */
03013    if (vms->interactive == 1) {
03014       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03015       if (altvms) {  
03016          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03017          vms->newmessages = altvms->newmessages;
03018          vms->oldmessages = altvms->oldmessages;
03019          vms->vmArrayIndex = altvms->vmArrayIndex;
03020          vms->lastmsg = altvms->lastmsg;
03021          vms->curmsg = altvms->curmsg;
03022          /* get a pointer to the persistent store */
03023          vms->persist_vms = altvms;
03024          /* Reuse the mailstream? */
03025 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03026          vms->mailstream = altvms->mailstream;
03027 #else
03028          vms->mailstream = NIL;
03029 #endif
03030       }
03031       return;
03032    }
03033 
03034    if (!(v = ast_calloc(1, sizeof(*v))))
03035       return;
03036    
03037    v->vms = vms;
03038 
03039    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03040 
03041    AST_LIST_LOCK(&vmstates);
03042    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03043    AST_LIST_UNLOCK(&vmstates);
03044 }
03045 
03046 static void vmstate_delete(struct vm_state *vms) 
03047 {
03048    struct vmstate *vc = NULL;
03049    struct vm_state *altvms = NULL;
03050 
03051    /* If interactive, we should copy pertinent info
03052       back to the persistent state (to make update immediate) */
03053    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03054       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03055       altvms->newmessages = vms->newmessages;
03056       altvms->oldmessages = vms->oldmessages;
03057       altvms->updated = 1;
03058       vms->mailstream = mail_close(vms->mailstream);
03059 
03060       /* Interactive states are not stored within the persistent list */
03061       return;
03062    }
03063    
03064    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03065    
03066    AST_LIST_LOCK(&vmstates);
03067    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03068       if (vc->vms == vms) {
03069          AST_LIST_REMOVE_CURRENT(list);
03070          break;
03071       }
03072    }
03073    AST_LIST_TRAVERSE_SAFE_END
03074    AST_LIST_UNLOCK(&vmstates);
03075    
03076    if (vc) {
03077       ast_mutex_destroy(&vc->vms->lock);
03078       ast_free(vc);
03079    }
03080    else
03081       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03082 }
03083 
03084 static void set_update(MAILSTREAM * stream) 
03085 {
03086    struct vm_state *vms;
03087    char *mailbox = stream->mailbox, *user;
03088    char buf[1024] = "";
03089 
03090    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03091       if (user && option_debug > 2)
03092          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03093       return;
03094    }
03095 
03096    ast_debug(3, "User %s mailbox set for update.\n", user);
03097 
03098    vms->updated = 1; /* Set updated flag since mailbox changed */
03099 }
03100 
03101 static void init_vm_state(struct vm_state *vms) 
03102 {
03103    int x;
03104    vms->vmArrayIndex = 0;
03105    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03106       vms->msgArray[x] = 0;
03107    }
03108    ast_mutex_init(&vms->lock);
03109 }
03110 
03111 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03112 {
03113    char *body_content;
03114    char *body_decoded;
03115    char *fn = is_intro ? vms->introfn : vms->fn;
03116    unsigned long len;
03117    unsigned long newlen;
03118    char filename[256];
03119    
03120    if (!body || body == NIL)
03121       return -1;
03122 
03123    ast_mutex_lock(&vms->lock);
03124    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03125    ast_mutex_unlock(&vms->lock);
03126    if (body_content != NIL) {
03127       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03128       /* ast_debug(1,body_content); */
03129       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03130       /* If the body of the file is empty, return an error */
03131       if (!newlen) {
03132          return -1;
03133       }
03134       write_file(filename, (char *) body_decoded, newlen);
03135    } else {
03136       ast_debug(5, "Body of message is NULL.\n");
03137       return -1;
03138    }
03139    return 0;
03140 }
03141 
03142 /*! 
03143  * \brief Get delimiter via mm_list callback 
03144  * \param stream
03145  *
03146  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03147  */
03148 /* MUTEX should already be held */
03149 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03150    char tmp[50];
03151    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03152    mail_list(stream, tmp, "*");
03153 }
03154 
03155 /*! 
03156  * \brief Check Quota for user 
03157  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03158  * \param mailbox the mailbox to check the quota for.
03159  *
03160  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03161  */
03162 static void check_quota(struct vm_state *vms, char *mailbox) {
03163    ast_mutex_lock(&vms->lock);
03164    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03165    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03166    if (vms && vms->mailstream != NULL) {
03167       imap_getquotaroot(vms->mailstream, mailbox);
03168    } else {
03169       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03170    }
03171    ast_mutex_unlock(&vms->lock);
03172 }
03173 
03174 #endif /* IMAP_STORAGE */
03175 
03176 /*! \brief Lock file path
03177     only return failure if ast_lock_path returns 'timeout',
03178    not if the path does not exist or any other reason
03179 */
03180 static int vm_lock_path(const char *path)
03181 {
03182    switch (ast_lock_path(path)) {
03183    case AST_LOCK_TIMEOUT:
03184       return -1;
03185    default:
03186       return 0;
03187    }
03188 }
03189 
03190 
03191 #ifdef ODBC_STORAGE
03192 struct generic_prepare_struct {
03193    char *sql;
03194    int argc;
03195    char **argv;
03196 };
03197 
03198 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03199 {
03200    struct generic_prepare_struct *gps = data;
03201    int res, i;
03202    SQLHSTMT stmt;
03203 
03204    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03205    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03206       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03207       return NULL;
03208    }
03209    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03210    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03211       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03212       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03213       return NULL;
03214    }
03215    for (i = 0; i < gps->argc; i++)
03216       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03217 
03218    return stmt;
03219 }
03220 
03221 /*!
03222  * \brief Retrieves a file from an ODBC data store.
03223  * \param dir the path to the file to be retreived.
03224  * \param msgnum the message number, such as within a mailbox folder.
03225  * 
03226  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03227  * 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.
03228  *
03229  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03230  * The output is the message information file with the name msgnum and the extension .txt
03231  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03232  * 
03233  * \return 0 on success, -1 on error.
03234  */
03235 static int retrieve_file(char *dir, int msgnum)
03236 {
03237    int x = 0;
03238    int res;
03239    int fd = -1;
03240    size_t fdlen = 0;
03241    void *fdm = MAP_FAILED;
03242    SQLSMALLINT colcount = 0;
03243    SQLHSTMT stmt;
03244    char sql[PATH_MAX];
03245    char fmt[80]="";
03246    char *c;
03247    char coltitle[256];
03248    SQLSMALLINT collen;
03249    SQLSMALLINT datatype;
03250    SQLSMALLINT decimaldigits;
03251    SQLSMALLINT nullable;
03252    SQLULEN colsize;
03253    SQLLEN colsize2;
03254    FILE *f = NULL;
03255    char rowdata[80];
03256    char fn[PATH_MAX];
03257    char full_fn[PATH_MAX];
03258    char msgnums[80];
03259    char *argv[] = { dir, msgnums };
03260    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03261 
03262    struct odbc_obj *obj;
03263    obj = ast_odbc_request_obj(odbc_database, 0);
03264    if (obj) {
03265       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03266       c = strchr(fmt, '|');
03267       if (c)
03268          *c = '\0';
03269       if (!strcasecmp(fmt, "wav49"))
03270          strcpy(fmt, "WAV");
03271       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03272       if (msgnum > -1)
03273          make_file(fn, sizeof(fn), dir, msgnum);
03274       else
03275          ast_copy_string(fn, dir, sizeof(fn));
03276 
03277       /* Create the information file */
03278       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03279       
03280       if (!(f = fopen(full_fn, "w+"))) {
03281          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03282          goto yuck;
03283       }
03284       
03285       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03286       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03287       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03288       if (!stmt) {
03289          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03290          ast_odbc_release_obj(obj);
03291          goto yuck;
03292       }
03293       res = SQLFetch(stmt);
03294       if (res == SQL_NO_DATA) {
03295          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03296          ast_odbc_release_obj(obj);
03297          goto yuck;
03298       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03299          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03300          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03301          ast_odbc_release_obj(obj);
03302          goto yuck;
03303       }
03304       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03305       if (fd < 0) {
03306          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03307          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03308          ast_odbc_release_obj(obj);
03309          goto yuck;
03310       }
03311       res = SQLNumResultCols(stmt, &colcount);
03312       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03313          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03314          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03315          ast_odbc_release_obj(obj);
03316          goto yuck;
03317       }
03318       if (f) 
03319          fprintf(f, "[message]\n");
03320       for (x = 0; x < colcount; x++) {
03321          rowdata[0] = '\0';
03322          collen = sizeof(coltitle);
03323          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03324                   &datatype, &colsize, &decimaldigits, &nullable);
03325          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03326             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03327             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03328             ast_odbc_release_obj(obj);
03329             goto yuck;
03330          }
03331          if (!strcasecmp(coltitle, "recording")) {
03332             off_t offset;
03333             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03334             fdlen = colsize2;
03335             if (fd > -1) {
03336                char tmp[1]="";
03337                lseek(fd, fdlen - 1, SEEK_SET);
03338                if (write(fd, tmp, 1) != 1) {
03339                   close(fd);
03340                   fd = -1;
03341                   continue;
03342                }
03343                /* Read out in small chunks */
03344                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03345                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03346                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03347                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03348                      ast_odbc_release_obj(obj);
03349                      goto yuck;
03350                   } else {
03351                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03352                      munmap(fdm, CHUNKSIZE);
03353                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03354                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03355                         unlink(full_fn);
03356                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03357                         ast_odbc_release_obj(obj);
03358                         goto yuck;
03359                      }
03360                   }
03361                }
03362                if (truncate(full_fn, fdlen) < 0) {
03363                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03364                }
03365             }
03366          } else {
03367             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03368             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03369                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03370                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03371                ast_odbc_release_obj(obj);
03372                goto yuck;
03373             }
03374             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03375                fprintf(f, "%s=%s\n", coltitle, rowdata);
03376          }
03377       }
03378       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03379       ast_odbc_release_obj(obj);
03380    } else
03381       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03382 yuck: 
03383    if (f)
03384       fclose(f);
03385    if (fd > -1)
03386       close(fd);
03387    return x - 1;
03388 }
03389 
03390 /*!
03391  * \brief Determines the highest message number in use for a given user and mailbox folder.
03392  * \param vmu 
03393  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03394  *
03395  * This method is used when mailboxes are stored in an ODBC back end.
03396  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03397  *
03398  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03399  */
03400 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03401 {
03402    int x = 0;
03403    int res;
03404    SQLHSTMT stmt;
03405    char sql[PATH_MAX];
03406    char rowdata[20];
03407    char *argv[] = { dir };
03408    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03409 
03410    struct odbc_obj *obj;
03411    obj = ast_odbc_request_obj(odbc_database, 0);
03412    if (obj) {
03413       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03414       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03415       if (!stmt) {
03416          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03417          ast_odbc_release_obj(obj);
03418          goto yuck;
03419       }
03420       res = SQLFetch(stmt);
03421       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03422          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03423          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03424          ast_odbc_release_obj(obj);
03425          goto yuck;
03426       }
03427       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03428       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03429          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03430          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03431          ast_odbc_release_obj(obj);
03432          goto yuck;
03433       }
03434       if (sscanf(rowdata, "%30d", &x) != 1)
03435          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03436       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03437       ast_odbc_release_obj(obj);
03438    } else
03439       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03440 yuck: 
03441    return x - 1;
03442 }
03443 
03444 /*!
03445  * \brief Determines if the specified message exists.
03446  * \param dir the folder the mailbox folder to look for messages. 
03447  * \param msgnum the message index to query for.
03448  *
03449  * This method is used when mailboxes are stored in an ODBC back end.
03450  *
03451  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03452  */
03453 static int message_exists(char *dir, int msgnum)
03454 {
03455    int x = 0;
03456    int res;
03457    SQLHSTMT stmt;
03458    char sql[PATH_MAX];
03459    char rowdata[20];
03460    char msgnums[20];
03461    char *argv[] = { dir, msgnums };
03462    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03463 
03464    struct odbc_obj *obj;
03465    obj = ast_odbc_request_obj(odbc_database, 0);
03466    if (obj) {
03467       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03468       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03469       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03470       if (!stmt) {
03471          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03472          ast_odbc_release_obj(obj);
03473          goto yuck;
03474       }
03475       res = SQLFetch(stmt);
03476       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03477          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03478          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03479          ast_odbc_release_obj(obj);
03480          goto yuck;
03481       }
03482       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03483       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03484          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03485          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03486          ast_odbc_release_obj(obj);
03487          goto yuck;
03488       }
03489       if (sscanf(rowdata, "%30d", &x) != 1)
03490          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03491       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03492       ast_odbc_release_obj(obj);
03493    } else
03494       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03495 yuck: 
03496    return x;
03497 }
03498 
03499 /*!
03500  * \brief returns the one-based count for messages.
03501  * \param vmu
03502  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03503  *
03504  * This method is used when mailboxes are stored in an ODBC back end.
03505  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03506  * one-based messages.
03507  * This method just calls last_message_index and returns +1 of its value.
03508  *
03509  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03510  */
03511 static int count_messages(struct ast_vm_user *vmu, char *dir)
03512 {
03513    return last_message_index(vmu, dir) + 1;
03514 }
03515 
03516 /*!
03517  * \brief Deletes a message from the mailbox folder.
03518  * \param sdir The mailbox folder to work in.
03519  * \param smsg The message index to be deleted.
03520  *
03521  * This method is used when mailboxes are stored in an ODBC back end.
03522  * The specified message is directly deleted from the database 'voicemessages' table.
03523  * 
03524  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03525  */
03526 static void delete_file(const char *sdir, int smsg)
03527 {
03528    SQLHSTMT stmt;
03529    char sql[PATH_MAX];
03530    char msgnums[20];
03531    char *argv[] = { NULL, msgnums };
03532    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03533    struct odbc_obj *obj;
03534 
03535    argv[0] = ast_strdupa(sdir);
03536 
03537    obj = ast_odbc_request_obj(odbc_database, 0);
03538    if (obj) {
03539       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03540       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03541       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03542       if (!stmt)
03543          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03544       else
03545          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03546       ast_odbc_release_obj(obj);
03547    } else
03548       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03549    return;  
03550 }
03551 
03552 /*!
03553  * \brief Copies a voicemail from one mailbox to another.
03554  * \param sdir the folder for which to look for the message to be copied.
03555  * \param smsg the index of the message to be copied.
03556  * \param ddir the destination folder to copy the message into.
03557  * \param dmsg the index to be used for the copied message.
03558  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03559  * \param dmailboxcontext The context for the destination user.
03560  *
03561  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03562  */
03563 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03564 {
03565    SQLHSTMT stmt;
03566    char sql[512];
03567    char msgnums[20];
03568    char msgnumd[20];
03569    struct odbc_obj *obj;
03570    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03571    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03572 
03573    delete_file(ddir, dmsg);
03574    obj = ast_odbc_request_obj(odbc_database, 0);
03575    if (obj) {
03576       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03577       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03578       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);
03579       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03580       if (!stmt)
03581          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03582       else
03583          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03584       ast_odbc_release_obj(obj);
03585    } else
03586       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03587    return;  
03588 }
03589 
03590 struct insert_data {
03591    char *sql;
03592    const char *dir;
03593    const char *msgnums;
03594    void *data;
03595    SQLLEN datalen;
03596    SQLLEN indlen;
03597    const char *context;
03598    const char *macrocontext;
03599    const char *callerid;
03600    const char *origtime;
03601    const char *duration;
03602    const char *mailboxuser;
03603    const char *mailboxcontext;
03604    const char *category;
03605    const char *flag;
03606 };
03607 
03608 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03609 {
03610    struct insert_data *data = vdata;
03611    int res;
03612    SQLHSTMT stmt;
03613 
03614    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03615    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03616       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03617       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03618       return NULL;
03619    }
03620 
03621    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03622    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03623    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03624    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03625    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03626    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03627    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03628    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03629    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03630    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03631    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03632    if (!ast_strlen_zero(data->category)) {
03633       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03634    }
03635    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03636    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03637       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03638       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03639       return NULL;
03640    }
03641 
03642    return stmt;
03643 }
03644 
03645 /*!
03646  * \brief Stores a voicemail into the database.
03647  * \param dir the folder the mailbox folder to store the message.
03648  * \param mailboxuser the user owning the mailbox folder.
03649  * \param mailboxcontext
03650  * \param msgnum the message index for the message to be stored.
03651  *
03652  * This method is used when mailboxes are stored in an ODBC back end.
03653  * The message sound file and information file is looked up on the file system. 
03654  * A SQL query is invoked to store the message into the (MySQL) database.
03655  *
03656  * \return the zero on success -1 on error.
03657  */
03658 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03659 {
03660    int res = 0;
03661    int fd = -1;
03662    void *fdm = MAP_FAILED;
03663    size_t fdlen = -1;
03664    SQLHSTMT stmt;
03665    char sql[PATH_MAX];
03666    char msgnums[20];
03667    char fn[PATH_MAX];
03668    char full_fn[PATH_MAX];
03669    char fmt[80]="";
03670    char *c;
03671    struct ast_config *cfg = NULL;
03672    struct odbc_obj *obj;
03673    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03674       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03675    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03676 
03677    delete_file(dir, msgnum);
03678    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03679       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03680       return -1;
03681    }
03682 
03683    do {
03684       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03685       c = strchr(fmt, '|');
03686       if (c)
03687          *c = '\0';
03688       if (!strcasecmp(fmt, "wav49"))
03689          strcpy(fmt, "WAV");
03690       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03691       if (msgnum > -1)
03692          make_file(fn, sizeof(fn), dir, msgnum);
03693       else
03694          ast_copy_string(fn, dir, sizeof(fn));
03695       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03696       cfg = ast_config_load(full_fn, config_flags);
03697       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03698       fd = open(full_fn, O_RDWR);
03699       if (fd < 0) {
03700          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03701          res = -1;
03702          break;
03703       }
03704       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03705          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03706             idata.context = "";
03707          }
03708          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03709             idata.macrocontext = "";
03710          }
03711          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03712             idata.callerid = "";
03713          }
03714          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03715             idata.origtime = "";
03716          }
03717          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03718             idata.duration = "";
03719          }
03720          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03721             idata.category = "";
03722          }
03723          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03724             idata.flag = "";
03725          }
03726       }
03727       fdlen = lseek(fd, 0, SEEK_END);
03728       lseek(fd, 0, SEEK_SET);
03729       printf("Length is %zd\n", fdlen);
03730       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03731       if (fdm == MAP_FAILED) {
03732          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03733          res = -1;
03734          break;
03735       } 
03736       idata.data = fdm;
03737       idata.datalen = idata.indlen = fdlen;
03738 
03739       if (!ast_strlen_zero(idata.category)) 
03740          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03741       else
03742          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03743 
03744       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03745          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03746       } else {
03747          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03748          res = -1;
03749       }
03750    } while (0);
03751    if (obj) {
03752       ast_odbc_release_obj(obj);
03753    }
03754    if (cfg)
03755       ast_config_destroy(cfg);
03756    if (fdm != MAP_FAILED)
03757       munmap(fdm, fdlen);
03758    if (fd > -1)
03759       close(fd);
03760    return res;
03761 }
03762 
03763 /*!
03764  * \brief Renames a message in a mailbox folder.
03765  * \param sdir The folder of the message to be renamed.
03766  * \param smsg The index of the message to be renamed.
03767  * \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.
03768  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03769  * \param ddir The destination folder for the message to be renamed into
03770  * \param dmsg The destination message for the message to be renamed.
03771  *
03772  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03773  * 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.
03774  * 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.
03775  */
03776 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03777 {
03778    SQLHSTMT stmt;
03779    char sql[PATH_MAX];
03780    char msgnums[20];
03781    char msgnumd[20];
03782    struct odbc_obj *obj;
03783    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03784    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03785 
03786    delete_file(ddir, dmsg);
03787    obj = ast_odbc_request_obj(odbc_database, 0);
03788    if (obj) {
03789       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03790       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03791       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03792       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03793       if (!stmt)
03794          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03795       else
03796          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03797       ast_odbc_release_obj(obj);
03798    } else
03799       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03800    return;  
03801 }
03802 
03803 /*!
03804  * \brief Removes a voicemail message file.
03805  * \param dir the path to the message file.
03806  * \param msgnum the unique number for the message within the mailbox.
03807  *
03808  * Removes the message content file and the information file.
03809  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03810  * Typical use is to clean up after a RETRIEVE operation. 
03811  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03812  * \return zero on success, -1 on error.
03813  */
03814 static int remove_file(char *dir, int msgnum)
03815 {
03816    char fn[PATH_MAX];
03817    char full_fn[PATH_MAX];
03818    char msgnums[80];
03819    
03820    if (msgnum > -1) {
03821       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03822       make_file(fn, sizeof(fn), dir, msgnum);
03823    } else
03824       ast_copy_string(fn, dir, sizeof(fn));
03825    ast_filedelete(fn, NULL);  
03826    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03827    unlink(full_fn);
03828    return 0;
03829 }
03830 #else
03831 #ifndef IMAP_STORAGE
03832 /*!
03833  * \brief Find all .txt files - even if they are not in sequence from 0000.
03834  * \param vmu
03835  * \param dir
03836  *
03837  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03838  *
03839  * \return the count of messages, zero or more.
03840  */
03841 static int count_messages(struct ast_vm_user *vmu, char *dir)
03842 {
03843 
03844    int vmcount = 0;
03845    DIR *vmdir = NULL;
03846    struct dirent *vment = NULL;
03847 
03848    if (vm_lock_path(dir))
03849       return ERROR_LOCK_PATH;
03850 
03851    if ((vmdir = opendir(dir))) {
03852       while ((vment = readdir(vmdir))) {
03853          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03854             vmcount++;
03855          }
03856       }
03857       closedir(vmdir);
03858    }
03859    ast_unlock_path(dir);
03860    
03861    return vmcount;
03862 }
03863 
03864 /*!
03865  * \brief Renames a message in a mailbox folder.
03866  * \param sfn The path to the mailbox information and data file to be renamed.
03867  * \param dfn The path for where the message data and information files will be renamed to.
03868  *
03869  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03870  */
03871 static void rename_file(char *sfn, char *dfn)
03872 {
03873    char stxt[PATH_MAX];
03874    char dtxt[PATH_MAX];
03875    ast_filerename(sfn, dfn, NULL);
03876    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03877    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03878    if (ast_check_realtime("voicemail_data")) {
03879       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03880    }
03881    rename(stxt, dtxt);
03882 }
03883 
03884 /*! 
03885  * \brief Determines the highest message number in use for a given user and mailbox folder.
03886  * \param vmu 
03887  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03888  *
03889  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03890  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03891  *
03892  * \note Should always be called with a lock already set on dir.
03893  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03894  */
03895 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03896 {
03897    int x;
03898    unsigned char map[MAXMSGLIMIT] = "";
03899    DIR *msgdir;
03900    struct dirent *msgdirent;
03901    int msgdirint;
03902    char extension[4];
03903    int stopcount = 0;
03904 
03905    /* Reading the entire directory into a file map scales better than
03906     * doing a stat repeatedly on a predicted sequence.  I suspect this
03907     * is partially due to stat(2) internally doing a readdir(2) itself to
03908     * find each file. */
03909    if (!(msgdir = opendir(dir))) {
03910       return -1;
03911    }
03912 
03913    while ((msgdirent = readdir(msgdir))) {
03914       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
03915          map[msgdirint] = 1;
03916          stopcount++;
03917          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
03918       }
03919    }
03920    closedir(msgdir);
03921 
03922    for (x = 0; x < vmu->maxmsg; x++) {
03923       if (map[x] == 1) {
03924          stopcount--;
03925       } else if (map[x] == 0 && !stopcount) {
03926          break;
03927       }
03928    }
03929 
03930    return x - 1;
03931 }
03932 
03933 #endif /* #ifndef IMAP_STORAGE */
03934 #endif /* #else of #ifdef ODBC_STORAGE */
03935 #ifndef IMAP_STORAGE
03936 /*!
03937  * \brief Utility function to copy a file.
03938  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03939  * \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.
03940  *
03941  * 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.
03942  * The copy operation copies up to 4096 bytes at once.
03943  *
03944  * \return zero on success, -1 on error.
03945  */
03946 static int copy(char *infile, char *outfile)
03947 {
03948    int ifd;
03949    int ofd;
03950    int res;
03951    int len;
03952    char buf[4096];
03953 
03954 #ifdef HARDLINK_WHEN_POSSIBLE
03955    /* Hard link if possible; saves disk space & is faster */
03956    if (link(infile, outfile)) {
03957 #endif
03958       if ((ifd = open(infile, O_RDONLY)) < 0) {
03959          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03960          return -1;
03961       }
03962       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03963          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03964          close(ifd);
03965          return -1;
03966       }
03967       do {
03968          len = read(ifd, buf, sizeof(buf));
03969          if (len < 0) {
03970             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03971             close(ifd);
03972             close(ofd);
03973             unlink(outfile);
03974          }
03975          if (len) {
03976             res = write(ofd, buf, len);
03977             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03978                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03979                close(ifd);
03980                close(ofd);
03981                unlink(outfile);
03982             }
03983          }
03984       } while (len);
03985       close(ifd);
03986       close(ofd);
03987       return 0;
03988 #ifdef HARDLINK_WHEN_POSSIBLE
03989    } else {
03990       /* Hard link succeeded */
03991       return 0;
03992    }
03993 #endif
03994 }
03995 
03996 /*!
03997  * \brief Copies a voicemail information (envelope) file.
03998  * \param frompath
03999  * \param topath 
04000  *
04001  * Every voicemail has the data (.wav) file, and the information file.
04002  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04003  * This is used by the COPY macro when not using IMAP storage.
04004  */
04005 static void copy_plain_file(char *frompath, char *topath)
04006 {
04007    char frompath2[PATH_MAX], topath2[PATH_MAX];
04008    struct ast_variable *tmp,*var = NULL;
04009    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04010    ast_filecopy(frompath, topath, NULL);
04011    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04012    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04013    if (ast_check_realtime("voicemail_data")) {
04014       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04015       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04016       for (tmp = var; tmp; tmp = tmp->next) {
04017          if (!strcasecmp(tmp->name, "origmailbox")) {
04018             origmailbox = tmp->value;
04019          } else if (!strcasecmp(tmp->name, "context")) {
04020             context = tmp->value;
04021          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04022             macrocontext = tmp->value;
04023          } else if (!strcasecmp(tmp->name, "exten")) {
04024             exten = tmp->value;
04025          } else if (!strcasecmp(tmp->name, "priority")) {
04026             priority = tmp->value;
04027          } else if (!strcasecmp(tmp->name, "callerchan")) {
04028             callerchan = tmp->value;
04029          } else if (!strcasecmp(tmp->name, "callerid")) {
04030             callerid = tmp->value;
04031          } else if (!strcasecmp(tmp->name, "origdate")) {
04032             origdate = tmp->value;
04033          } else if (!strcasecmp(tmp->name, "origtime")) {
04034             origtime = tmp->value;
04035          } else if (!strcasecmp(tmp->name, "category")) {
04036             category = tmp->value;
04037          } else if (!strcasecmp(tmp->name, "duration")) {
04038             duration = tmp->value;
04039          }
04040       }
04041       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);
04042    }
04043    copy(frompath2, topath2);
04044    ast_variables_destroy(var);
04045 }
04046 #endif
04047 
04048 /*! 
04049  * \brief Removes the voicemail sound and information file.
04050  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04051  *
04052  * This is used by the DELETE macro when voicemails are stored on the file system.
04053  *
04054  * \return zero on success, -1 on error.
04055  */
04056 static int vm_delete(char *file)
04057 {
04058    char *txt;
04059    int txtsize = 0;
04060 
04061    txtsize = (strlen(file) + 5)*sizeof(char);
04062    txt = alloca(txtsize);
04063    /* Sprintf here would safe because we alloca'd exactly the right length,
04064     * but trying to eliminate all sprintf's anyhow
04065     */
04066    if (ast_check_realtime("voicemail_data")) {
04067       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04068    }
04069    snprintf(txt, txtsize, "%s.txt", file);
04070    unlink(txt);
04071    return ast_filedelete(file, NULL);
04072 }
04073 
04074 /*!
04075  * \brief utility used by inchar(), for base_encode()
04076  */
04077 static int inbuf(struct baseio *bio, FILE *fi)
04078 {
04079    int l;
04080 
04081    if (bio->ateof)
04082       return 0;
04083 
04084    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04085       if (ferror(fi))
04086          return -1;
04087 
04088       bio->ateof = 1;
04089       return 0;
04090    }
04091 
04092    bio->iolen = l;
04093    bio->iocp = 0;
04094 
04095    return 1;
04096 }
04097 
04098 /*!
04099  * \brief utility used by base_encode()
04100  */
04101 static int inchar(struct baseio *bio, FILE *fi)
04102 {
04103    if (bio->iocp>=bio->iolen) {
04104       if (!inbuf(bio, fi))
04105          return EOF;
04106    }
04107 
04108    return bio->iobuf[bio->iocp++];
04109 }
04110 
04111 /*!
04112  * \brief utility used by base_encode()
04113  */
04114 static int ochar(struct baseio *bio, int c, FILE *so)
04115 {
04116    if (bio->linelength >= BASELINELEN) {
04117       if (fputs(ENDL, so) == EOF) {
04118          return -1;
04119       }
04120 
04121       bio->linelength = 0;
04122    }
04123 
04124    if (putc(((unsigned char) c), so) == EOF) {
04125       return -1;
04126    }
04127 
04128    bio->linelength++;
04129 
04130    return 1;
04131 }
04132 
04133 /*!
04134  * \brief Performs a base 64 encode algorithm on the contents of a File
04135  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04136  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04137  *
04138  * 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 ?
04139  *
04140  * \return zero on success, -1 on error.
04141  */
04142 static int base_encode(char *filename, FILE *so)
04143 {
04144    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04145       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04146       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04147       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04148    int i, hiteof = 0;
04149    FILE *fi;
04150    struct baseio bio;
04151 
04152    memset(&bio, 0, sizeof(bio));
04153    bio.iocp = BASEMAXINLINE;
04154 
04155    if (!(fi = fopen(filename, "rb"))) {
04156       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04157       return -1;
04158    }
04159 
04160    while (!hiteof){
04161       unsigned char igroup[3], ogroup[4];
04162       int c, n;
04163 
04164       memset(igroup, 0, sizeof(igroup));
04165 
04166       for (n = 0; n < 3; n++) {
04167          if ((c = inchar(&bio, fi)) == EOF) {
04168             hiteof = 1;
04169             break;
04170          }
04171 
04172          igroup[n] = (unsigned char) c;
04173       }
04174 
04175       if (n > 0) {
04176          ogroup[0]= dtable[igroup[0] >> 2];
04177          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04178          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04179          ogroup[3]= dtable[igroup[2] & 0x3F];
04180 
04181          if (n < 3) {
04182             ogroup[3] = '=';
04183 
04184             if (n < 2)
04185                ogroup[2] = '=';
04186          }
04187 
04188          for (i = 0; i < 4; i++)
04189             ochar(&bio, ogroup[i], so);
04190       }
04191    }
04192 
04193    fclose(fi);
04194    
04195    if (fputs(ENDL, so) == EOF) {
04196       return 0;
04197    }
04198 
04199    return 1;
04200 }
04201 
04202 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, const char *category, const char *flag)
04203 {
04204    char callerid[256];
04205    char num[12];
04206    char fromdir[256], fromfile[256];
04207    struct ast_config *msg_cfg;
04208    const char *origcallerid, *origtime;
04209    char origcidname[80], origcidnum[80], origdate[80];
04210    int inttime;
04211    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04212 
04213    /* Prepare variables for substitution in email body and subject */
04214    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04215    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04216    snprintf(num, sizeof(num), "%d", msgnum);
04217    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04218    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04219    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04220    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04221       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04222    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04223    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04224    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04225    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04226    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04227 
04228    /* Retrieve info from VM attribute file */
04229    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04230    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04231    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04232       strcat(fromfile, ".txt");
04233    }
04234    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04235       if (option_debug > 0) {
04236          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04237       }
04238       return;
04239    }
04240 
04241    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04242       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04243       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04244       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04245       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04246    }
04247 
04248    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04249       struct timeval tv = { inttime, };
04250       struct ast_tm tm;
04251       ast_localtime(&tv, &tm, NULL);
04252       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04253       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04254    }
04255    ast_config_destroy(msg_cfg);
04256 }
04257 
04258 /*!
04259  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04260  * \param from The string to work with.
04261  * \param buf The buffer into which to write the modified quoted string.
04262  * \param maxlen Always zero, but see \see ast_str
04263  * 
04264  * \return The destination string with quotes wrapped on it (the to field).
04265  */
04266 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04267 {
04268    const char *ptr;
04269 
04270    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04271    ast_str_set(buf, maxlen, "\"");
04272    for (ptr = from; *ptr; ptr++) {
04273       if (*ptr == '"' || *ptr == '\\') {
04274          ast_str_append(buf, maxlen, "\\%c", *ptr);
04275       } else {
04276          ast_str_append(buf, maxlen, "%c", *ptr);
04277       }
04278    }
04279    ast_str_append(buf, maxlen, "\"");
04280 
04281    return ast_str_buffer(*buf);
04282 }
04283 
04284 /*! \brief
04285  * fill in *tm for current time according to the proper timezone, if any.
04286  * \return tm so it can be used as a function argument.
04287  */
04288 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04289 {
04290    const struct vm_zone *z = NULL;
04291    struct timeval t = ast_tvnow();
04292 
04293    /* Does this user have a timezone specified? */
04294    if (!ast_strlen_zero(vmu->zonetag)) {
04295       /* Find the zone in the list */
04296       AST_LIST_LOCK(&zones);
04297       AST_LIST_TRAVERSE(&zones, z, list) {
04298          if (!strcmp(z->name, vmu->zonetag))
04299             break;
04300       }
04301       AST_LIST_UNLOCK(&zones);
04302    }
04303    ast_localtime(&t, tm, z ? z->timezone : NULL);
04304    return tm;
04305 }
04306 
04307 /*!\brief Check if the string would need encoding within the MIME standard, to
04308  * avoid confusing certain mail software that expects messages to be 7-bit
04309  * clean.
04310  */
04311 static int check_mime(const char *str)
04312 {
04313    for (; *str; str++) {
04314       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04315          return 1;
04316       }
04317    }
04318    return 0;
04319 }
04320 
04321 /*!\brief Encode a string according to the MIME rules for encoding strings
04322  * that are not 7-bit clean or contain control characters.
04323  *
04324  * Additionally, if the encoded string would exceed the MIME limit of 76
04325  * characters per line, then the encoding will be broken up into multiple
04326  * sections, separated by a space character, in order to facilitate
04327  * breaking up the associated header across multiple lines.
04328  *
04329  * \param end An expandable buffer for holding the result
04330  * \param maxlen Always zero, but see \see ast_str
04331  * \param start A string to be encoded
04332  * \param preamble The length of the first line already used for this string,
04333  * to ensure that each line maintains a maximum length of 76 chars.
04334  * \param postamble the length of any additional characters appended to the
04335  * line, used to ensure proper field wrapping.
04336  * \retval The encoded string.
04337  */
04338 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04339 {
04340    struct ast_str *tmp = ast_str_alloca(80);
04341    int first_section = 1;
04342 
04343    ast_str_reset(*end);
04344    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04345    for (; *start; start++) {
04346       int need_encoding = 0;
04347       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04348          need_encoding = 1;
04349       }
04350       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04351          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04352          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04353          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04354          /* Start new line */
04355          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04356          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04357          first_section = 0;
04358       }
04359       if (need_encoding && *start == ' ') {
04360          ast_str_append(&tmp, -1, "_");
04361       } else if (need_encoding) {
04362          ast_str_append(&tmp, -1, "=%hhX", *start);
04363       } else {
04364          ast_str_append(&tmp, -1, "%c", *start);
04365       }
04366    }
04367    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04368    return ast_str_buffer(*end);
04369 }
04370 
04371 /*!
04372  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04373  * \param p The output file to generate the email contents into.
04374  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04375  * \param vmu The voicemail user who is sending the voicemail.
04376  * \param msgnum The message index in the mailbox folder.
04377  * \param context 
04378  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04379  * \param fromfolder
04380  * \param cidnum The caller ID number.
04381  * \param cidname The caller ID name.
04382  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04383  * \param attach2 
04384  * \param format The message sound file format. i.e. .wav
04385  * \param duration The time of the message content, in seconds.
04386  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04387  * \param chan
04388  * \param category
04389  * \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.
04390  * \param flag
04391  *
04392  * 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.
04393  */
04394 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)
04395 {
04396    char date[256];
04397    char host[MAXHOSTNAMELEN] = "";
04398    char who[256];
04399    char bound[256];
04400    char dur[256];
04401    struct ast_tm tm;
04402    char enc_cidnum[256] = "", enc_cidname[256] = "";
04403    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04404    char *greeting_attachment; 
04405    char filename[256];
04406 
04407    if (!str1 || !str2) {
04408       ast_free(str1);
04409       ast_free(str2);
04410       return;
04411    }
04412 
04413    if (cidnum) {
04414       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04415    }
04416    if (cidname) {
04417       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04418    }
04419    gethostname(host, sizeof(host) - 1);
04420 
04421    if (strchr(srcemail, '@')) {
04422       ast_copy_string(who, srcemail, sizeof(who));
04423    } else {
04424       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04425    }
04426 
04427    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04428    if (greeting_attachment) {
04429       *greeting_attachment++ = '\0';
04430    }
04431 
04432    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04433    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04434    fprintf(p, "Date: %s" ENDL, date);
04435 
04436    /* Set date format for voicemail mail */
04437    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04438 
04439    if (!ast_strlen_zero(fromstring)) {
04440       struct ast_channel *ast;
04441       if ((ast = ast_dummy_channel_alloc())) {
04442          char *ptr;
04443          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04444          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04445 
04446          if (check_mime(ast_str_buffer(str1))) {
04447             int first_line = 1;
04448             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04449             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04450                *ptr = '\0';
04451                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04452                first_line = 0;
04453                /* Substring is smaller, so this will never grow */
04454                ast_str_set(&str2, 0, "%s", ptr + 1);
04455             }
04456             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04457          } else {
04458             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04459          }
04460          ast = ast_channel_release(ast);
04461       } else {
04462          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04463       }
04464    } else {
04465       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04466    }
04467 
04468    if (check_mime(vmu->fullname)) {
04469       int first_line = 1;
04470       char *ptr;
04471       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04472       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04473          *ptr = '\0';
04474          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04475          first_line = 0;
04476          /* Substring is smaller, so this will never grow */
04477          ast_str_set(&str2, 0, "%s", ptr + 1);
04478       }
04479       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04480    } else {
04481       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04482    }
04483 
04484    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04485       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04486       struct ast_channel *ast;
04487       if ((ast = ast_dummy_channel_alloc())) {
04488          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04489          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04490          if (check_mime(ast_str_buffer(str1))) {
04491             int first_line = 1;
04492             char *ptr;
04493             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04494             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04495                *ptr = '\0';
04496                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04497                first_line = 0;
04498                /* Substring is smaller, so this will never grow */
04499                ast_str_set(&str2, 0, "%s", ptr + 1);
04500             }
04501             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04502          } else {
04503             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04504          }
04505          ast = ast_channel_release(ast);
04506       } else {
04507          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04508       }
04509    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04510       if (ast_strlen_zero(flag)) {
04511          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04512       } else {
04513          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04514       }
04515    } else {
04516       if (ast_strlen_zero(flag)) {
04517          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04518       } else {
04519          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04520       }
04521    }
04522 
04523    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04524       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04525    if (imap) {
04526       /* additional information needed for IMAP searching */
04527       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04528       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04529       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04530       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04531 #ifdef IMAP_STORAGE
04532       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04533 #else
04534       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04535 #endif
04536       /* flag added for Urgent */
04537       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04538       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04539       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04540       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04541       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04542       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04543       if (!ast_strlen_zero(category)) {
04544          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04545       } else {
04546          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04547       }
04548       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04549       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04550       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04551    }
04552    if (!ast_strlen_zero(cidnum)) {
04553       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04554    }
04555    if (!ast_strlen_zero(cidname)) {
04556       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04557    }
04558    fprintf(p, "MIME-Version: 1.0" ENDL);
04559    if (attach_user_voicemail) {
04560       /* Something unique. */
04561       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04562          (int) getpid(), (unsigned int) ast_random());
04563 
04564       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04565       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04566       fprintf(p, "--%s" ENDL, bound);
04567    }
04568    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04569    if (emailbody || vmu->emailbody) {
04570       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04571       struct ast_channel *ast;
04572       if ((ast = ast_dummy_channel_alloc())) {
04573          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04574          ast_str_substitute_variables(&str1, 0, ast, e_body);
04575 #ifdef IMAP_STORAGE
04576             {
04577                /* Convert body to native line terminators for IMAP backend */
04578                char *line = ast_str_buffer(str1), *next;
04579                do {
04580                   /* Terminate line before outputting it to the file */
04581                   if ((next = strchr(line, '\n'))) {
04582                      *next++ = '\0';
04583                   }
04584                   fprintf(p, "%s" ENDL, line);
04585                   line = next;
04586                } while (!ast_strlen_zero(line));
04587             }
04588 #else
04589          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04590 #endif
04591          ast = ast_channel_release(ast);
04592       } else {
04593          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04594       }
04595    } else if (msgnum > -1) {
04596       if (strcmp(vmu->mailbox, mailbox)) {
04597          /* Forwarded type */
04598          struct ast_config *msg_cfg;
04599          const char *v;
04600          int inttime;
04601          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04602          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04603          /* Retrieve info from VM attribute file */
04604          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04605          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04606          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04607             strcat(fromfile, ".txt");
04608          }
04609          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04610             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04611                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04612             }
04613 
04614             /* You might be tempted to do origdate, except that a) it's in the wrong
04615              * format, and b) it's missing for IMAP recordings. */
04616             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04617                struct timeval tv = { inttime, };
04618                struct ast_tm tm;
04619                ast_localtime(&tv, &tm, NULL);
04620                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04621             }
04622             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04623                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04624                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04625                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04626                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04627                date, origcallerid, origdate);
04628             ast_config_destroy(msg_cfg);
04629          } else {
04630             goto plain_message;
04631          }
04632       } else {
04633 plain_message:
04634          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04635             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04636             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04637             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04638             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04639       }
04640    } else {
04641       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04642             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04643    }
04644 
04645    if (imap || attach_user_voicemail) {
04646       if (!ast_strlen_zero(attach2)) {
04647          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04648          ast_debug(5, "creating second attachment filename %s\n", filename);
04649          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04650          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04651          ast_debug(5, "creating attachment filename %s\n", filename);
04652          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04653       } else {
04654          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04655          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04656          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04657       }
04658    }
04659    ast_free(str1);
04660    ast_free(str2);
04661 }
04662 
04663 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)
04664 {
04665    char tmpdir[256], newtmp[256];
04666    char fname[256];
04667    char tmpcmd[256];
04668    int tmpfd = -1;
04669 
04670    /* Eww. We want formats to tell us their own MIME type */
04671    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04672 
04673    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04674       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04675       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04676       tmpfd = mkstemp(newtmp);
04677       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04678       ast_debug(3, "newtmp: %s\n", newtmp);
04679       if (tmpfd > -1) {
04680          int soxstatus;
04681          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04682          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04683             attach = newtmp;
04684             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04685          } else {
04686             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04687                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04688             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04689          }
04690       }
04691    }
04692    fprintf(p, "--%s" ENDL, bound);
04693    if (msgnum > -1)
04694       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04695    else
04696       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04697    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04698    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04699    if (msgnum > -1)
04700       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04701    else
04702       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04703    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04704    base_encode(fname, p);
04705    if (last)
04706       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04707    if (tmpfd > -1) {
04708       unlink(fname);
04709       close(tmpfd);
04710       unlink(newtmp);
04711    }
04712    return 0;
04713 }
04714 
04715 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)
04716 {
04717    FILE *p = NULL;
04718    char tmp[80] = "/tmp/astmail-XXXXXX";
04719    char tmp2[256];
04720    char *stringp;
04721 
04722    if (vmu && ast_strlen_zero(vmu->email)) {
04723       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04724       return(0);
04725    }
04726 
04727    /* Mail only the first format */
04728    format = ast_strdupa(format);
04729    stringp = format;
04730    strsep(&stringp, "|");
04731 
04732    if (!strcmp(format, "wav49"))
04733       format = "WAV";
04734    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));
04735    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04736       command hangs */
04737    if ((p = vm_mkftemp(tmp)) == NULL) {
04738       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04739       return -1;
04740    } else {
04741       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04742       fclose(p);
04743       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04744       ast_safe_system(tmp2);
04745       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04746    }
04747    return 0;
04748 }
04749 
04750 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)
04751 {
04752    char enc_cidnum[256], enc_cidname[256];
04753    char date[256];
04754    char host[MAXHOSTNAMELEN] = "";
04755    char who[256];
04756    char dur[PATH_MAX];
04757    char tmp[80] = "/tmp/astmail-XXXXXX";
04758    char tmp2[PATH_MAX];
04759    struct ast_tm tm;
04760    FILE *p;
04761    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04762 
04763    if (!str1 || !str2) {
04764       ast_free(str1);
04765       ast_free(str2);
04766       return -1;
04767    }
04768 
04769    if (cidnum) {
04770       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04771    }
04772    if (cidname) {
04773       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04774    }
04775 
04776    if ((p = vm_mkftemp(tmp)) == NULL) {
04777       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04778       ast_free(str1);
04779       ast_free(str2);
04780       return -1;
04781    }
04782    gethostname(host, sizeof(host)-1);
04783    if (strchr(srcemail, '@')) {
04784       ast_copy_string(who, srcemail, sizeof(who));
04785    } else {
04786       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04787    }
04788    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04789    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04790    fprintf(p, "Date: %s\n", date);
04791 
04792    /* Reformat for custom pager format */
04793    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04794 
04795    if (!ast_strlen_zero(pagerfromstring)) {
04796       struct ast_channel *ast;
04797       if ((ast = ast_dummy_channel_alloc())) {
04798          char *ptr;
04799          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04800          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04801 
04802          if (check_mime(ast_str_buffer(str1))) {
04803             int first_line = 1;
04804             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04805             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04806                *ptr = '\0';
04807                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04808                first_line = 0;
04809                /* Substring is smaller, so this will never grow */
04810                ast_str_set(&str2, 0, "%s", ptr + 1);
04811             }
04812             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04813          } else {
04814             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04815          }
04816          ast = ast_channel_release(ast);
04817       } else {
04818          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04819       }
04820    } else {
04821       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04822    }
04823 
04824    if (check_mime(vmu->fullname)) {
04825       int first_line = 1;
04826       char *ptr;
04827       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04828       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04829          *ptr = '\0';
04830          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04831          first_line = 0;
04832          /* Substring is smaller, so this will never grow */
04833          ast_str_set(&str2, 0, "%s", ptr + 1);
04834       }
04835       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04836    } else {
04837       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04838    }
04839 
04840    if (!ast_strlen_zero(pagersubject)) {
04841       struct ast_channel *ast;
04842       if ((ast = ast_dummy_channel_alloc())) {
04843          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04844          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04845          if (check_mime(ast_str_buffer(str1))) {
04846             int first_line = 1;
04847             char *ptr;
04848             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04849             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04850                *ptr = '\0';
04851                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04852                first_line = 0;
04853                /* Substring is smaller, so this will never grow */
04854                ast_str_set(&str2, 0, "%s", ptr + 1);
04855             }
04856             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04857          } else {
04858             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04859          }
04860          ast = ast_channel_release(ast);
04861       } else {
04862          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04863       }
04864    } else {
04865       if (ast_strlen_zero(flag)) {
04866          fprintf(p, "Subject: New VM\n\n");
04867       } else {
04868          fprintf(p, "Subject: New %s VM\n\n", flag);
04869       }
04870    }
04871 
04872    if (pagerbody) {
04873       struct ast_channel *ast;
04874       if ((ast = ast_dummy_channel_alloc())) {
04875          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04876          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04877          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04878          ast = ast_channel_release(ast);
04879       } else {
04880          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04881       }
04882    } else {
04883       fprintf(p, "New %s long %s msg in box %s\n"
04884             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04885    }
04886 
04887    fclose(p);
04888    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04889    ast_safe_system(tmp2);
04890    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04891    ast_free(str1);
04892    ast_free(str2);
04893    return 0;
04894 }
04895 
04896 /*!
04897  * \brief Gets the current date and time, as formatted string.
04898  * \param s The buffer to hold the output formatted date.
04899  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04900  * 
04901  * The date format string used is "%a %b %e %r UTC %Y".
04902  * 
04903  * \return zero on success, -1 on error.
04904  */
04905 static int get_date(char *s, int len)
04906 {
04907    struct ast_tm tm;
04908    struct timeval t = ast_tvnow();
04909    
04910    ast_localtime(&t, &tm, "UTC");
04911 
04912    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04913 }
04914 
04915 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04916 {
04917    int res;
04918    char fn[PATH_MAX];
04919    char dest[PATH_MAX];
04920 
04921    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04922 
04923    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04924       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04925       return -1;
04926    }
04927 
04928    RETRIEVE(fn, -1, ext, context);
04929    if (ast_fileexists(fn, NULL, NULL) > 0) {
04930       res = ast_stream_and_wait(chan, fn, ecodes);
04931       if (res) {
04932          DISPOSE(fn, -1);
04933          return res;
04934       }
04935    } else {
04936       /* Dispose just in case */
04937       DISPOSE(fn, -1);
04938       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04939       if (res)
04940          return res;
04941       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04942       if (res)
04943          return res;
04944    }
04945    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04946    return res;
04947 }
04948 
04949 static void free_zone(struct vm_zone *z)
04950 {
04951    ast_free(z);
04952 }
04953 
04954 #ifdef ODBC_STORAGE
04955 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04956 {
04957    int x = -1;
04958    int res;
04959    SQLHSTMT stmt = NULL;
04960    char sql[PATH_MAX];
04961    char rowdata[20];
04962    char tmp[PATH_MAX] = "";
04963    struct odbc_obj *obj = NULL;
04964    char *context;
04965    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04966 
04967    if (newmsgs)
04968       *newmsgs = 0;
04969    if (oldmsgs)
04970       *oldmsgs = 0;
04971    if (urgentmsgs)
04972       *urgentmsgs = 0;
04973 
04974    /* If no mailbox, return immediately */
04975    if (ast_strlen_zero(mailbox))
04976       return 0;
04977 
04978    ast_copy_string(tmp, mailbox, sizeof(tmp));
04979 
04980    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04981       int u, n, o;
04982       char *next, *remaining = tmp;
04983       while ((next = strsep(&remaining, " ,"))) {
04984          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04985             return -1;
04986          }
04987          if (urgentmsgs) {
04988             *urgentmsgs += u;
04989          }
04990          if (newmsgs) {
04991             *newmsgs += n;
04992          }
04993          if (oldmsgs) {
04994             *oldmsgs += o;
04995          }
04996       }
04997       return 0;
04998    }
04999 
05000    context = strchr(tmp, '@');
05001    if (context) {
05002       *context = '\0';
05003       context++;
05004    } else
05005       context = "default";
05006 
05007    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05008       do {
05009          if (newmsgs) {
05010             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05011             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05012                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05013                break;
05014             }
05015             res = SQLFetch(stmt);
05016             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05017                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05018                break;
05019             }
05020             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05021             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05022                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05023                break;
05024             }
05025             *newmsgs = atoi(rowdata);
05026             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05027          }
05028 
05029          if (oldmsgs) {
05030             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05031             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05032                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05033                break;
05034             }
05035             res = SQLFetch(stmt);
05036             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05037                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05038                break;
05039             }
05040             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05041             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05042                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05043                break;
05044             }
05045             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05046             *oldmsgs = atoi(rowdata);
05047          }
05048 
05049          if (urgentmsgs) {
05050             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05051             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05052                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05053                break;
05054             }
05055             res = SQLFetch(stmt);
05056             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05057                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05058                break;
05059             }
05060             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05061             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05062                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05063                break;
05064             }
05065             *urgentmsgs = atoi(rowdata);
05066          }
05067 
05068          x = 0;
05069       } while (0);
05070    } else {
05071       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05072    }
05073 
05074    if (stmt) {
05075       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05076    }
05077    if (obj) {
05078       ast_odbc_release_obj(obj);
05079    }
05080 
05081    return x;
05082 }
05083 
05084 /*!
05085  * \brief Gets the number of messages that exist in a mailbox folder.
05086  * \param context
05087  * \param mailbox
05088  * \param folder
05089  * 
05090  * This method is used when ODBC backend is used.
05091  * \return The number of messages in this mailbox folder (zero or more).
05092  */
05093 static int messagecount(const char *context, const char *mailbox, const char *folder)
05094 {
05095    struct odbc_obj *obj = NULL;
05096    int nummsgs = 0;
05097    int res;
05098    SQLHSTMT stmt = NULL;
05099    char sql[PATH_MAX];
05100    char rowdata[20];
05101    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05102    if (!folder)
05103       folder = "INBOX";
05104    /* If no mailbox, return immediately */
05105    if (ast_strlen_zero(mailbox))
05106       return 0;
05107 
05108    obj = ast_odbc_request_obj(odbc_database, 0);
05109    if (obj) {
05110       if (!strcmp(folder, "INBOX")) {
05111          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);
05112       } else {
05113          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05114       }
05115       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05116       if (!stmt) {
05117          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05118          goto yuck;
05119       }
05120       res = SQLFetch(stmt);
05121       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05122          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05123          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05124          goto yuck;
05125       }
05126       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05127       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05128          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05129          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05130          goto yuck;
05131       }
05132       nummsgs = atoi(rowdata);
05133       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05134    } else
05135       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05136 
05137 yuck:
05138    if (obj)
05139       ast_odbc_release_obj(obj);
05140    return nummsgs;
05141 }
05142 
05143 /** 
05144  * \brief Determines if the given folder has messages.
05145  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05146  * 
05147  * This function is used when the mailbox is stored in an ODBC back end.
05148  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05149  * \return 1 if the folder has one or more messages. zero otherwise.
05150  */
05151 static int has_voicemail(const char *mailbox, const char *folder)
05152 {
05153    char tmp[256], *tmp2 = tmp, *box, *context;
05154    ast_copy_string(tmp, mailbox, sizeof(tmp));
05155    while ((context = box = strsep(&tmp2, ",&"))) {
05156       strsep(&context, "@");
05157       if (ast_strlen_zero(context))
05158          context = "default";
05159       if (messagecount(context, box, folder))
05160          return 1;
05161    }
05162    return 0;
05163 }
05164 #endif
05165 #ifndef IMAP_STORAGE
05166 /*! 
05167  * \brief Copies a message from one mailbox to another.
05168  * \param chan
05169  * \param vmu
05170  * \param imbox
05171  * \param msgnum
05172  * \param duration
05173  * \param recip
05174  * \param fmt
05175  * \param dir
05176  * \param flag
05177  *
05178  * This is only used by file storage based mailboxes.
05179  *
05180  * \return zero on success, -1 on error.
05181  */
05182 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)
05183 {
05184    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05185    const char *frombox = mbox(vmu, imbox);
05186    int recipmsgnum;
05187    int res = 0;
05188 
05189    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05190 
05191    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05192       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05193    } else {
05194       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05195    }
05196    
05197    if (!dir)
05198       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05199    else
05200       ast_copy_string(fromdir, dir, sizeof(fromdir));
05201 
05202    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05203    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05204 
05205    if (vm_lock_path(todir))
05206       return ERROR_LOCK_PATH;
05207 
05208    recipmsgnum = last_message_index(recip, todir) + 1;
05209    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05210       make_file(topath, sizeof(topath), todir, recipmsgnum);
05211 #ifndef ODBC_STORAGE
05212       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05213          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05214       } else {
05215 #endif
05216          /* If we are prepending a message for ODBC, then the message already
05217           * exists in the database, but we want to force copying from the
05218           * filesystem (since only the FS contains the prepend). */
05219          copy_plain_file(frompath, topath);
05220          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05221          vm_delete(topath);
05222 #ifndef ODBC_STORAGE
05223       }
05224 #endif
05225    } else {
05226       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05227       res = -1;
05228    }
05229    ast_unlock_path(todir);
05230    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05231       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05232       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05233       flag);
05234    
05235    return res;
05236 }
05237 #endif
05238 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05239 
05240 static int messagecount(const char *context, const char *mailbox, const char *folder)
05241 {
05242    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05243 }
05244 
05245 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05246 {
05247    DIR *dir;
05248    struct dirent *de;
05249    char fn[256];
05250    int ret = 0;
05251 
05252    /* If no mailbox, return immediately */
05253    if (ast_strlen_zero(mailbox))
05254       return 0;
05255 
05256    if (ast_strlen_zero(folder))
05257       folder = "INBOX";
05258    if (ast_strlen_zero(context))
05259       context = "default";
05260 
05261    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05262 
05263    if (!(dir = opendir(fn)))
05264       return 0;
05265 
05266    while ((de = readdir(dir))) {
05267       if (!strncasecmp(de->d_name, "msg", 3)) {
05268          if (shortcircuit) {
05269             ret = 1;
05270             break;
05271          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05272             ret++;
05273          }
05274       }
05275    }
05276 
05277    closedir(dir);
05278 
05279    return ret;
05280 }
05281 
05282 /** 
05283  * \brief Determines if the given folder has messages.
05284  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05285  * \param folder the folder to look in
05286  *
05287  * This function is used when the mailbox is stored in a filesystem back end.
05288  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05289  * \return 1 if the folder has one or more messages. zero otherwise.
05290  */
05291 static int has_voicemail(const char *mailbox, const char *folder)
05292 {
05293    char tmp[256], *tmp2 = tmp, *box, *context;
05294    ast_copy_string(tmp, mailbox, sizeof(tmp));
05295    if (ast_strlen_zero(folder)) {
05296       folder = "INBOX";
05297    }
05298    while ((box = strsep(&tmp2, ",&"))) {
05299       if ((context = strchr(box, '@')))
05300          *context++ = '\0';
05301       else
05302          context = "default";
05303       if (__has_voicemail(context, box, folder, 1))
05304          return 1;
05305       /* If we are checking INBOX, we should check Urgent as well */
05306       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05307          return 1;
05308       }
05309    }
05310    return 0;
05311 }
05312 
05313 
05314 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05315 {
05316    char tmp[256];
05317    char *context;
05318 
05319    /* If no mailbox, return immediately */
05320    if (ast_strlen_zero(mailbox))
05321       return 0;
05322 
05323    if (newmsgs)
05324       *newmsgs = 0;
05325    if (oldmsgs)
05326       *oldmsgs = 0;
05327    if (urgentmsgs)
05328       *urgentmsgs = 0;
05329 
05330    if (strchr(mailbox, ',')) {
05331       int tmpnew, tmpold, tmpurgent;
05332       char *mb, *cur;
05333 
05334       ast_copy_string(tmp, mailbox, sizeof(tmp));
05335       mb = tmp;
05336       while ((cur = strsep(&mb, ", "))) {
05337          if (!ast_strlen_zero(cur)) {
05338             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05339                return -1;
05340             else {
05341                if (newmsgs)
05342                   *newmsgs += tmpnew; 
05343                if (oldmsgs)
05344                   *oldmsgs += tmpold;
05345                if (urgentmsgs)
05346                   *urgentmsgs += tmpurgent;
05347             }
05348          }
05349       }
05350       return 0;
05351    }
05352 
05353    ast_copy_string(tmp, mailbox, sizeof(tmp));
05354    
05355    if ((context = strchr(tmp, '@')))
05356       *context++ = '\0';
05357    else
05358       context = "default";
05359 
05360    if (newmsgs)
05361       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05362    if (oldmsgs)
05363       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05364    if (urgentmsgs)
05365       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05366 
05367    return 0;
05368 }
05369 
05370 #endif
05371 
05372 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05373 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05374 {
05375    int urgentmsgs = 0;
05376    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05377    if (newmsgs) {
05378       *newmsgs += urgentmsgs;
05379    }
05380    return res;
05381 }
05382 
05383 static void run_externnotify(char *context, char *extension, const char *flag)
05384 {
05385    char arguments[255];
05386    char ext_context[256] = "";
05387    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05388    struct ast_smdi_mwi_message *mwi_msg;
05389 
05390    if (!ast_strlen_zero(context))
05391       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05392    else
05393       ast_copy_string(ext_context, extension, sizeof(ext_context));
05394 
05395    if (smdi_iface) {
05396       if (ast_app_has_voicemail(ext_context, NULL)) 
05397          ast_smdi_mwi_set(smdi_iface, extension);
05398       else
05399          ast_smdi_mwi_unset(smdi_iface, extension);
05400 
05401       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05402          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05403          if (!strncmp(mwi_msg->cause, "INV", 3))
05404             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05405          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05406             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05407          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05408          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05409       } else {
05410          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05411       }
05412    }
05413 
05414    if (!ast_strlen_zero(externnotify)) {
05415       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05416          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05417       } else {
05418          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05419          ast_debug(1, "Executing %s\n", arguments);
05420          ast_safe_system(arguments);
05421       }
05422    }
05423 }
05424 
05425 /*!
05426  * \brief Variables used for saving a voicemail.
05427  *
05428  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05429  */
05430 struct leave_vm_options {
05431    unsigned int flags;
05432    signed char record_gain;
05433    char *exitcontext;
05434 };
05435 
05436 /*!
05437  * \brief Prompts the user and records a voicemail to a mailbox.
05438  * \param chan
05439  * \param ext
05440  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05441  * 
05442  * 
05443  * 
05444  * \return zero on success, -1 on error.
05445  */
05446 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05447 {
05448 #ifdef IMAP_STORAGE
05449    int newmsgs, oldmsgs;
05450 #else
05451    char urgdir[PATH_MAX];
05452 #endif
05453    char txtfile[PATH_MAX];
05454    char tmptxtfile[PATH_MAX];
05455    struct vm_state *vms = NULL;
05456    char callerid[256];
05457    FILE *txt;
05458    char date[256];
05459    int txtdes;
05460    int res = 0;
05461    int msgnum;
05462    int duration = 0;
05463    int ausemacro = 0;
05464    int ousemacro = 0;
05465    int ouseexten = 0;
05466    char tmpdur[16];
05467    char priority[16];
05468    char origtime[16];
05469    char dir[PATH_MAX];
05470    char tmpdir[PATH_MAX];
05471    char fn[PATH_MAX];
05472    char prefile[PATH_MAX] = "";
05473    char tempfile[PATH_MAX] = "";
05474    char ext_context[256] = "";
05475    char fmt[80];
05476    char *context;
05477    char ecodes[17] = "#";
05478    struct ast_str *tmp = ast_str_create(16);
05479    char *tmpptr;
05480    struct ast_vm_user *vmu;
05481    struct ast_vm_user svm;
05482    const char *category = NULL;
05483    const char *code;
05484    const char *alldtmf = "0123456789ABCD*#";
05485    char flag[80];
05486 
05487    if (!tmp) {
05488       return -1;
05489    }
05490 
05491    ast_str_set(&tmp, 0, "%s", ext);
05492    ext = ast_str_buffer(tmp);
05493    if ((context = strchr(ext, '@'))) {
05494       *context++ = '\0';
05495       tmpptr = strchr(context, '&');
05496    } else {
05497       tmpptr = strchr(ext, '&');
05498    }
05499 
05500    if (tmpptr)
05501       *tmpptr++ = '\0';
05502 
05503    ast_channel_lock(chan);
05504    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05505       category = ast_strdupa(category);
05506    }
05507    ast_channel_unlock(chan);
05508 
05509    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05510       ast_copy_string(flag, "Urgent", sizeof(flag));
05511    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05512       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05513    } else {
05514       flag[0] = '\0';
05515    }
05516 
05517    ast_debug(3, "Before find_user\n");
05518    if (!(vmu = find_user(&svm, context, ext))) {
05519       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05520       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05521       ast_free(tmp);
05522       return res;
05523    }
05524    /* Setup pre-file if appropriate */
05525    if (strcmp(vmu->context, "default"))
05526       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05527    else
05528       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05529 
05530    /* Set the path to the prefile. Will be one of 
05531       VM_SPOOL_DIRcontext/ext/busy
05532       VM_SPOOL_DIRcontext/ext/unavail
05533       Depending on the flag set in options.
05534    */
05535    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05536       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05537    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05538       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05539    }
05540    /* Set the path to the tmpfile as
05541       VM_SPOOL_DIR/context/ext/temp
05542       and attempt to create the folder structure.
05543    */
05544    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05545    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05546       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05547       ast_free(tmp);
05548       return -1;
05549    }
05550    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05551    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05552       ast_copy_string(prefile, tempfile, sizeof(prefile));
05553 
05554    DISPOSE(tempfile, -1);
05555    /* It's easier just to try to make it than to check for its existence */
05556 #ifndef IMAP_STORAGE
05557    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05558 #else
05559    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05560    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05561       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05562    }
05563 #endif
05564 
05565    /* Check current or macro-calling context for special extensions */
05566    if (ast_test_flag(vmu, VM_OPERATOR)) {
05567       if (!ast_strlen_zero(vmu->exit)) {
05568          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05569             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05570             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05571             ouseexten = 1;
05572          }
05573       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05574          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05575          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05576          ouseexten = 1;
05577       } else if (!ast_strlen_zero(chan->macrocontext)
05578          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05579             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05580          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05581          ousemacro = 1;
05582       }
05583    }
05584 
05585    if (!ast_strlen_zero(vmu->exit)) {
05586       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05587          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05588          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05589       }
05590    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05591       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05592       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05593    } else if (!ast_strlen_zero(chan->macrocontext)
05594       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05595          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05596       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05597       ausemacro = 1;
05598    }
05599 
05600    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05601       for (code = alldtmf; *code; code++) {
05602          char e[2] = "";
05603          e[0] = *code;
05604          if (strchr(ecodes, e[0]) == NULL
05605             && ast_canmatch_extension(chan, chan->context, e, 1,
05606                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05607             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05608          }
05609       }
05610    }
05611 
05612    /* Play the beginning intro if desired */
05613    if (!ast_strlen_zero(prefile)) {
05614 #ifdef ODBC_STORAGE
05615       int success = 
05616 #endif
05617          RETRIEVE(prefile, -1, ext, context);
05618       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05619          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05620             res = ast_waitstream(chan, ecodes);
05621 #ifdef ODBC_STORAGE
05622          if (success == -1) {
05623             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05624             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05625             store_file(prefile, vmu->mailbox, vmu->context, -1);
05626          }
05627 #endif
05628       } else {
05629          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05630          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05631       }
05632       DISPOSE(prefile, -1);
05633       if (res < 0) {
05634          ast_debug(1, "Hang up during prefile playback\n");
05635          free_user(vmu);
05636          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05637          ast_free(tmp);
05638          return -1;
05639       }
05640    }
05641    if (res == '#') {
05642       /* On a '#' we skip the instructions */
05643       ast_set_flag(options, OPT_SILENT);
05644       res = 0;
05645    }
05646    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05647    if (vmu->maxmsg == 0) {
05648       if (option_debug > 2)
05649          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05650       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05651       goto leave_vm_out;
05652    }
05653    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05654       res = ast_stream_and_wait(chan, INTRO, ecodes);
05655       if (res == '#') {
05656          ast_set_flag(options, OPT_SILENT);
05657          res = 0;
05658       }
05659    }
05660    if (res > 0)
05661       ast_stopstream(chan);
05662    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05663     other than the operator -- an automated attendant or mailbox login for example */
05664    if (res == '*') {
05665       chan->exten[0] = 'a';
05666       chan->exten[1] = '\0';
05667       if (!ast_strlen_zero(vmu->exit)) {
05668          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05669       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05670          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05671       }
05672       chan->priority = 0;
05673       free_user(vmu);
05674       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05675       ast_free(tmp);
05676       return 0;
05677    }
05678 
05679    /* Check for a '0' here */
05680    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05681    transfer:
05682       if (ouseexten || ousemacro) {
05683          chan->exten[0] = 'o';
05684          chan->exten[1] = '\0';
05685          if (!ast_strlen_zero(vmu->exit)) {
05686             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05687          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05688             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05689          }
05690          ast_play_and_wait(chan, "transfer");
05691          chan->priority = 0;
05692          free_user(vmu);
05693          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05694       }
05695       ast_free(tmp);
05696       return OPERATOR_EXIT;
05697    }
05698 
05699    /* Allow all other digits to exit Voicemail and return to the dialplan */
05700    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05701       if (!ast_strlen_zero(options->exitcontext))
05702          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05703       free_user(vmu);
05704       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05705       ast_free(tmp);
05706       return res;
05707    }
05708 
05709    if (res < 0) {
05710       free_user(vmu);
05711       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05712       ast_free(tmp);
05713       return -1;
05714    }
05715    /* The meat of recording the message...  All the announcements and beeps have been played*/
05716    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05717    if (!ast_strlen_zero(fmt)) {
05718       msgnum = 0;
05719 
05720 #ifdef IMAP_STORAGE
05721       /* Is ext a mailbox? */
05722       /* must open stream for this user to get info! */
05723       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05724       if (res < 0) {
05725          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05726          ast_free(tmp);
05727          return -1;
05728       }
05729       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05730       /* It is possible under certain circumstances that inboxcount did not
05731        * create a vm_state when it was needed. This is a catchall which will
05732        * rarely be used.
05733        */
05734          if (!(vms = create_vm_state_from_user(vmu))) {
05735             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05736             ast_free(tmp);
05737             return -1;
05738          }
05739       }
05740       vms->newmessages++;
05741       
05742       /* here is a big difference! We add one to it later */
05743       msgnum = newmsgs + oldmsgs;
05744       ast_debug(3, "Messagecount set to %d\n", msgnum);
05745       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05746       /* set variable for compatibility */
05747       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05748 
05749       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05750          goto leave_vm_out;
05751       }
05752 #else
05753       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05754          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05755          if (!res)
05756             res = ast_waitstream(chan, "");
05757          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05758          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05759          inprocess_count(vmu->mailbox, vmu->context, -1);
05760          goto leave_vm_out;
05761       }
05762 
05763 #endif
05764       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05765       txtdes = mkstemp(tmptxtfile);
05766       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05767       if (txtdes < 0) {
05768          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05769          if (!res)
05770             res = ast_waitstream(chan, "");
05771          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05772          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05773          inprocess_count(vmu->mailbox, vmu->context, -1);
05774          goto leave_vm_out;
05775       }
05776 
05777       /* Now play the beep once we have the message number for our next message. */
05778       if (res >= 0) {
05779          /* Unless we're *really* silent, try to send the beep */
05780          res = ast_stream_and_wait(chan, "beep", "");
05781       }
05782             
05783       /* Store information in real-time storage */
05784       if (ast_check_realtime("voicemail_data")) {
05785          snprintf(priority, sizeof(priority), "%d", chan->priority);
05786          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05787          get_date(date, sizeof(date));
05788          ast_callerid_merge(callerid, sizeof(callerid),
05789             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05790             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05791             "Unknown");
05792          ast_store_realtime("voicemail_data",
05793             "origmailbox", ext,
05794             "context", chan->context,
05795             "macrocontext", chan->macrocontext,
05796             "exten", chan->exten,
05797             "priority", priority,
05798             "callerchan", chan->name,
05799             "callerid", callerid,
05800             "origdate", date,
05801             "origtime", origtime,
05802             "category", S_OR(category, ""),
05803             "filename", tmptxtfile,
05804             SENTINEL);
05805       }
05806 
05807       /* Store information */
05808       txt = fdopen(txtdes, "w+");
05809       if (txt) {
05810          get_date(date, sizeof(date));
05811          ast_callerid_merge(callerid, sizeof(callerid),
05812             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05813             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05814             "Unknown");
05815          fprintf(txt, 
05816             ";\n"
05817             "; Message Information file\n"
05818             ";\n"
05819             "[message]\n"
05820             "origmailbox=%s\n"
05821             "context=%s\n"
05822             "macrocontext=%s\n"
05823             "exten=%s\n"
05824             "rdnis=%s\n"
05825             "priority=%d\n"
05826             "callerchan=%s\n"
05827             "callerid=%s\n"
05828             "origdate=%s\n"
05829             "origtime=%ld\n"
05830             "category=%s\n",
05831             ext,
05832             chan->context,
05833             chan->macrocontext, 
05834             chan->exten,
05835             S_COR(chan->redirecting.from.number.valid,
05836                chan->redirecting.from.number.str, "unknown"),
05837             chan->priority,
05838             chan->name,
05839             callerid,
05840             date, (long) time(NULL),
05841             category ? category : "");
05842       } else {
05843          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05844          inprocess_count(vmu->mailbox, vmu->context, -1);
05845          if (ast_check_realtime("voicemail_data")) {
05846             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05847          }
05848          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05849          goto leave_vm_out;
05850       }
05851       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05852 
05853       if (txt) {
05854          fprintf(txt, "flag=%s\n", flag);
05855          if (duration < vmu->minsecs) {
05856             fclose(txt);
05857             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05858             ast_filedelete(tmptxtfile, NULL);
05859             unlink(tmptxtfile);
05860             if (ast_check_realtime("voicemail_data")) {
05861                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05862             }
05863             inprocess_count(vmu->mailbox, vmu->context, -1);
05864          } else {
05865             fprintf(txt, "duration=%d\n", duration);
05866             fclose(txt);
05867             if (vm_lock_path(dir)) {
05868                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05869                /* Delete files */
05870                ast_filedelete(tmptxtfile, NULL);
05871                unlink(tmptxtfile);
05872                inprocess_count(vmu->mailbox, vmu->context, -1);
05873             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05874                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05875                unlink(tmptxtfile);
05876                ast_unlock_path(dir);
05877                inprocess_count(vmu->mailbox, vmu->context, -1);
05878                if (ast_check_realtime("voicemail_data")) {
05879                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05880                }
05881             } else {
05882 #ifndef IMAP_STORAGE
05883                msgnum = last_message_index(vmu, dir) + 1;
05884 #endif
05885                make_file(fn, sizeof(fn), dir, msgnum);
05886 
05887                /* assign a variable with the name of the voicemail file */ 
05888 #ifndef IMAP_STORAGE
05889                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05890 #else
05891                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05892 #endif
05893 
05894                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05895                ast_filerename(tmptxtfile, fn, NULL);
05896                rename(tmptxtfile, txtfile);
05897                inprocess_count(vmu->mailbox, vmu->context, -1);
05898 
05899                /* Properly set permissions on voicemail text descriptor file.
05900                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05901                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05902                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05903 
05904                ast_unlock_path(dir);
05905                if (ast_check_realtime("voicemail_data")) {
05906                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05907                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05908                }
05909                /* We must store the file first, before copying the message, because
05910                 * ODBC storage does the entire copy with SQL.
05911                 */
05912                if (ast_fileexists(fn, NULL, NULL) > 0) {
05913                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05914                }
05915 
05916                /* Are there to be more recipients of this message? */
05917                while (tmpptr) {
05918                   struct ast_vm_user recipu, *recip;
05919                   char *exten, *cntx;
05920                
05921                   exten = strsep(&tmpptr, "&");
05922                   cntx = strchr(exten, '@');
05923                   if (cntx) {
05924                      *cntx = '\0';
05925                      cntx++;
05926                   }
05927                   if ((recip = find_user(&recipu, cntx, exten))) {
05928                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05929                      free_user(recip);
05930                   }
05931                }
05932 #ifndef IMAP_STORAGE
05933                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05934                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05935                   char sfn[PATH_MAX];
05936                   char dfn[PATH_MAX];
05937                   int x;
05938                   /* It's easier just to try to make it than to check for its existence */
05939                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05940                   x = last_message_index(vmu, urgdir) + 1;
05941                   make_file(sfn, sizeof(sfn), dir, msgnum);
05942                   make_file(dfn, sizeof(dfn), urgdir, x);
05943                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05944                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05945                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05946                   ast_copy_string(fn, dfn, sizeof(fn));
05947                   msgnum = x;
05948                }
05949 #endif
05950                /* Notification needs to happen after the copy, though. */
05951                if (ast_fileexists(fn, NULL, NULL)) {
05952 #ifdef IMAP_STORAGE
05953                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
05954                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05955                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05956                      flag);
05957 #else
05958                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
05959                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05960                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05961                      flag);
05962 #endif
05963                }
05964 
05965                /* Disposal needs to happen after the optional move and copy */
05966                if (ast_fileexists(fn, NULL, NULL)) {
05967                   DISPOSE(dir, msgnum);
05968                }
05969             }
05970          }
05971       } else {
05972          inprocess_count(vmu->mailbox, vmu->context, -1);
05973       }
05974       if (res == '0') {
05975          goto transfer;
05976       } else if (res > 0 && res != 't')
05977          res = 0;
05978 
05979       if (duration < vmu->minsecs)
05980          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05981          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05982       else
05983          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05984    } else
05985       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05986 leave_vm_out:
05987    free_user(vmu);
05988 
05989 #ifdef IMAP_STORAGE
05990    /* expunge message - use UID Expunge if supported on IMAP server*/
05991    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
05992    if (expungeonhangup == 1) {
05993       ast_mutex_lock(&vms->lock);
05994 #ifdef HAVE_IMAP_TK2006
05995       if (LEVELUIDPLUS (vms->mailstream)) {
05996          mail_expunge_full(vms->mailstream, NIL, EX_UID);
05997       } else 
05998 #endif
05999          mail_expunge(vms->mailstream);
06000       ast_mutex_unlock(&vms->lock);
06001    }
06002 #endif
06003 
06004    ast_free(tmp);
06005    return res;
06006 }
06007 
06008 #if !defined(IMAP_STORAGE) && !defined(ODBC_STORAGE)
06009 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06010 {
06011     /* we know the actual number of messages, so stop process when number is hit */
06012 
06013     int x, dest;
06014     char sfn[PATH_MAX];
06015     char dfn[PATH_MAX];
06016 
06017     if (vm_lock_path(dir))
06018         return ERROR_LOCK_PATH;
06019 
06020     for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06021         make_file(sfn, sizeof(sfn), dir, x);
06022         if (EXISTS(dir, x, sfn, NULL)) {
06023 
06024             if (x != dest) {
06025                 make_file(dfn, sizeof(dfn), dir, dest);
06026                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06027             }
06028 
06029             dest++;
06030         }
06031     }
06032     ast_unlock_path(dir);
06033 
06034     return dest;
06035 }
06036 #endif
06037 
06038 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06039 {
06040    int d;
06041    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06042    return d;
06043 }
06044 
06045 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06046 {
06047 #ifdef IMAP_STORAGE
06048    /* we must use mbox(x) folder names, and copy the message there */
06049    /* simple. huh? */
06050    char sequence[10];
06051    char mailbox[256];
06052    int res;
06053 
06054    /* get the real IMAP message number for this message */
06055    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06056    
06057    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06058    ast_mutex_lock(&vms->lock);
06059    /* if save to Old folder, put in INBOX as read */
06060    if (box == OLD_FOLDER) {
06061       mail_setflag(vms->mailstream, sequence, "\\Seen");
06062       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06063    } else if (box == NEW_FOLDER) {
06064       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06065       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06066    }
06067    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06068       ast_mutex_unlock(&vms->lock);
06069       return 0;
06070    }
06071    /* Create the folder if it don't exist */
06072    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06073    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06074    if (mail_create(vms->mailstream, mailbox) == NIL) 
06075       ast_debug(5, "Folder exists.\n");
06076    else
06077       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06078    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06079    ast_mutex_unlock(&vms->lock);
06080    return res;
06081 #else
06082    char *dir = vms->curdir;
06083    char *username = vms->username;
06084    char *context = vmu->context;
06085    char sfn[PATH_MAX];
06086    char dfn[PATH_MAX];
06087    char ddir[PATH_MAX];
06088    const char *dbox = mbox(vmu, box);
06089    int x, i;
06090    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06091 
06092    if (vm_lock_path(ddir))
06093       return ERROR_LOCK_PATH;
06094 
06095    x = last_message_index(vmu, ddir) + 1;
06096 
06097    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06098       x--;
06099       for (i = 1; i <= x; i++) {
06100          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06101          make_file(sfn, sizeof(sfn), ddir, i);
06102          make_file(dfn, sizeof(dfn), ddir, i - 1);
06103          if (EXISTS(ddir, i, sfn, NULL)) {
06104             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06105          } else
06106             break;
06107       }
06108    } else {
06109       if (x >= vmu->maxmsg) {
06110          ast_unlock_path(ddir);
06111          return -1;
06112       }
06113    }
06114    make_file(sfn, sizeof(sfn), dir, msg);
06115    make_file(dfn, sizeof(dfn), ddir, x);
06116    if (strcmp(sfn, dfn)) {
06117       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06118    }
06119    ast_unlock_path(ddir);
06120 #endif
06121    return 0;
06122 }
06123 
06124 static int adsi_logo(unsigned char *buf)
06125 {
06126    int bytes = 0;
06127    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06128    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06129    return bytes;
06130 }
06131 
06132 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06133 {
06134    unsigned char buf[256];
06135    int bytes = 0;
06136    int x;
06137    char num[5];
06138 
06139    *useadsi = 0;
06140    bytes += ast_adsi_data_mode(buf + bytes);
06141    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06142 
06143    bytes = 0;
06144    bytes += adsi_logo(buf);
06145    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06146 #ifdef DISPLAY
06147    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06148 #endif
06149    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06150    bytes += ast_adsi_data_mode(buf + bytes);
06151    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06152 
06153    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06154       bytes = 0;
06155       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06156       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06157       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06158       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06159       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06160       return 0;
06161    }
06162 
06163 #ifdef DISPLAY
06164    /* Add a dot */
06165    bytes = 0;
06166    bytes += ast_adsi_logo(buf);
06167    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06168    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06169    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06170    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06171 #endif
06172    bytes = 0;
06173    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06174    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06175    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06176    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06177    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06178    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06179    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06180 
06181 #ifdef DISPLAY
06182    /* Add another dot */
06183    bytes = 0;
06184    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06185    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06186 
06187    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06188    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06189 #endif
06190 
06191    bytes = 0;
06192    /* These buttons we load but don't use yet */
06193    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06194    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06195    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06196    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06197    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06198    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06199    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06200 
06201 #ifdef DISPLAY
06202    /* Add another dot */
06203    bytes = 0;
06204    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06205    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06206    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06207 #endif
06208 
06209    bytes = 0;
06210    for (x = 0; x < 5; x++) {
06211       snprintf(num, sizeof(num), "%d", x);
06212       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06213    }
06214    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06215    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06216 
06217 #ifdef DISPLAY
06218    /* Add another dot */
06219    bytes = 0;
06220    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06221    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06222    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06223 #endif
06224 
06225    if (ast_adsi_end_download(chan)) {
06226       bytes = 0;
06227       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06228       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06229       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06230       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06231       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06232       return 0;
06233    }
06234    bytes = 0;
06235    bytes += ast_adsi_download_disconnect(buf + bytes);
06236    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06237    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06238 
06239    ast_debug(1, "Done downloading scripts...\n");
06240 
06241 #ifdef DISPLAY
06242    /* Add last dot */
06243    bytes = 0;
06244    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06245    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06246 #endif
06247    ast_debug(1, "Restarting session...\n");
06248 
06249    bytes = 0;
06250    /* Load the session now */
06251    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06252       *useadsi = 1;
06253       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06254    } else
06255       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06256 
06257    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06258    return 0;
06259 }
06260 
06261 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06262 {
06263    int x;
06264    if (!ast_adsi_available(chan))
06265       return;
06266    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06267    if (x < 0)
06268       return;
06269    if (!x) {
06270       if (adsi_load_vmail(chan, useadsi)) {
06271          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06272          return;
06273       }
06274    } else
06275       *useadsi = 1;
06276 }
06277 
06278 static void adsi_login(struct ast_channel *chan)
06279 {
06280    unsigned char buf[256];
06281    int bytes = 0;
06282    unsigned char keys[8];
06283    int x;
06284    if (!ast_adsi_available(chan))
06285       return;
06286 
06287    for (x = 0; x < 8; x++)
06288       keys[x] = 0;
06289    /* Set one key for next */
06290    keys[3] = ADSI_KEY_APPS + 3;
06291 
06292    bytes += adsi_logo(buf + bytes);
06293    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06294    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06295    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06296    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06297    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06298    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06299    bytes += ast_adsi_set_keys(buf + bytes, keys);
06300    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06301    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06302 }
06303 
06304 static void adsi_password(struct ast_channel *chan)
06305 {
06306    unsigned char buf[256];
06307    int bytes = 0;
06308    unsigned char keys[8];
06309    int x;
06310    if (!ast_adsi_available(chan))
06311       return;
06312 
06313    for (x = 0; x < 8; x++)
06314       keys[x] = 0;
06315    /* Set one key for next */
06316    keys[3] = ADSI_KEY_APPS + 3;
06317 
06318    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06319    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06320    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06321    bytes += ast_adsi_set_keys(buf + bytes, keys);
06322    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06323    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06324 }
06325 
06326 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06327 {
06328    unsigned char buf[256];
06329    int bytes = 0;
06330    unsigned char keys[8];
06331    int x, y;
06332 
06333    if (!ast_adsi_available(chan))
06334       return;
06335 
06336    for (x = 0; x < 5; x++) {
06337       y = ADSI_KEY_APPS + 12 + start + x;
06338       if (y > ADSI_KEY_APPS + 12 + 4)
06339          y = 0;
06340       keys[x] = ADSI_KEY_SKT | y;
06341    }
06342    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06343    keys[6] = 0;
06344    keys[7] = 0;
06345 
06346    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06347    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06348    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06349    bytes += ast_adsi_set_keys(buf + bytes, keys);
06350    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06351 
06352    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06353 }
06354 
06355 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06356 {
06357    int bytes = 0;
06358    unsigned char buf[256]; 
06359    char buf1[256], buf2[256];
06360    char fn2[PATH_MAX];
06361 
06362    char cid[256] = "";
06363    char *val;
06364    char *name, *num;
06365    char datetime[21] = "";
06366    FILE *f;
06367 
06368    unsigned char keys[8];
06369 
06370    int x;
06371 
06372    if (!ast_adsi_available(chan))
06373       return;
06374 
06375    /* Retrieve important info */
06376    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06377    f = fopen(fn2, "r");
06378    if (f) {
06379       while (!feof(f)) {   
06380          if (!fgets((char *) buf, sizeof(buf), f)) {
06381             continue;
06382          }
06383          if (!feof(f)) {
06384             char *stringp = NULL;
06385             stringp = (char *) buf;
06386             strsep(&stringp, "=");
06387             val = strsep(&stringp, "=");
06388             if (!ast_strlen_zero(val)) {
06389                if (!strcmp((char *) buf, "callerid"))
06390                   ast_copy_string(cid, val, sizeof(cid));
06391                if (!strcmp((char *) buf, "origdate"))
06392                   ast_copy_string(datetime, val, sizeof(datetime));
06393             }
06394          }
06395       }
06396       fclose(f);
06397    }
06398    /* New meaning for keys */
06399    for (x = 0; x < 5; x++)
06400       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06401    keys[6] = 0x0;
06402    keys[7] = 0x0;
06403 
06404    if (!vms->curmsg) {
06405       /* No prev key, provide "Folder" instead */
06406       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06407    }
06408    if (vms->curmsg >= vms->lastmsg) {
06409       /* If last message ... */
06410       if (vms->curmsg) {
06411          /* but not only message, provide "Folder" instead */
06412          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06413          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06414 
06415       } else {
06416          /* Otherwise if only message, leave blank */
06417          keys[3] = 1;
06418       }
06419    }
06420 
06421    if (!ast_strlen_zero(cid)) {
06422       ast_callerid_parse(cid, &name, &num);
06423       if (!name)
06424          name = num;
06425    } else
06426       name = "Unknown Caller";
06427 
06428    /* If deleted, show "undeleted" */
06429 
06430    if (vms->deleted[vms->curmsg])
06431       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06432 
06433    /* Except "Exit" */
06434    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06435    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06436       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06437    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06438 
06439    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06440    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06441    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06442    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06443    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06444    bytes += ast_adsi_set_keys(buf + bytes, keys);
06445    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06446 
06447    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06448 }
06449 
06450 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06451 {
06452    int bytes = 0;
06453    unsigned char buf[256];
06454    unsigned char keys[8];
06455 
06456    int x;
06457 
06458    if (!ast_adsi_available(chan))
06459       return;
06460 
06461    /* New meaning for keys */
06462    for (x = 0; x < 5; x++)
06463       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06464 
06465    keys[6] = 0x0;
06466    keys[7] = 0x0;
06467 
06468    if (!vms->curmsg) {
06469       /* No prev key, provide "Folder" instead */
06470       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06471    }
06472    if (vms->curmsg >= vms->lastmsg) {
06473       /* If last message ... */
06474       if (vms->curmsg) {
06475          /* but not only message, provide "Folder" instead */
06476          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06477       } else {
06478          /* Otherwise if only message, leave blank */
06479          keys[3] = 1;
06480       }
06481    }
06482 
06483    /* If deleted, show "undeleted" */
06484    if (vms->deleted[vms->curmsg]) 
06485       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06486 
06487    /* Except "Exit" */
06488    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06489    bytes += ast_adsi_set_keys(buf + bytes, keys);
06490    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06491 
06492    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06493 }
06494 
06495 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06496 {
06497    unsigned char buf[256] = "";
06498    char buf1[256] = "", buf2[256] = "";
06499    int bytes = 0;
06500    unsigned char keys[8];
06501    int x;
06502 
06503    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06504    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06505    if (!ast_adsi_available(chan))
06506       return;
06507    if (vms->newmessages) {
06508       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06509       if (vms->oldmessages) {
06510          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06511          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06512       } else {
06513          snprintf(buf2, sizeof(buf2), "%s.", newm);
06514       }
06515    } else if (vms->oldmessages) {
06516       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06517       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06518    } else {
06519       strcpy(buf1, "You have no messages.");
06520       buf2[0] = ' ';
06521       buf2[1] = '\0';
06522    }
06523    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06524    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06525    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06526 
06527    for (x = 0; x < 6; x++)
06528       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06529    keys[6] = 0;
06530    keys[7] = 0;
06531 
06532    /* Don't let them listen if there are none */
06533    if (vms->lastmsg < 0)
06534       keys[0] = 1;
06535    bytes += ast_adsi_set_keys(buf + bytes, keys);
06536 
06537    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06538 
06539    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06540 }
06541 
06542 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06543 {
06544    unsigned char buf[256] = "";
06545    char buf1[256] = "", buf2[256] = "";
06546    int bytes = 0;
06547    unsigned char keys[8];
06548    int x;
06549 
06550    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06551 
06552    if (!ast_adsi_available(chan))
06553       return;
06554 
06555    /* Original command keys */
06556    for (x = 0; x < 6; x++)
06557       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06558 
06559    keys[6] = 0;
06560    keys[7] = 0;
06561 
06562    if ((vms->lastmsg + 1) < 1)
06563       keys[0] = 0;
06564 
06565    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06566       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06567 
06568    if (vms->lastmsg + 1)
06569       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06570    else
06571       strcpy(buf2, "no messages.");
06572    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06573    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06574    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06575    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06576    bytes += ast_adsi_set_keys(buf + bytes, keys);
06577 
06578    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06579 
06580    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06581    
06582 }
06583 
06584 /*
06585 static void adsi_clear(struct ast_channel *chan)
06586 {
06587    char buf[256];
06588    int bytes=0;
06589    if (!ast_adsi_available(chan))
06590       return;
06591    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06592    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06593 
06594    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06595 }
06596 */
06597 
06598 static void adsi_goodbye(struct ast_channel *chan)
06599 {
06600    unsigned char buf[256];
06601    int bytes = 0;
06602 
06603    if (!ast_adsi_available(chan))
06604       return;
06605    bytes += adsi_logo(buf + bytes);
06606    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06607    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06608    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06609    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06610 
06611    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06612 }
06613 
06614 /*!\brief get_folder: Folder menu
06615  * Plays "press 1 for INBOX messages" etc.
06616  * Should possibly be internationalized
06617  */
06618 static int get_folder(struct ast_channel *chan, int start)
06619 {
06620    int x;
06621    int d;
06622    char fn[PATH_MAX];
06623    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06624    if (d)
06625       return d;
06626    for (x = start; x < 5; x++) { /* For all folders */
06627       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06628          return d;
06629       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06630       if (d)
06631          return d;
06632       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06633       d = vm_play_folder_name(chan, fn);
06634       if (d)
06635          return d;
06636       d = ast_waitfordigit(chan, 500);
06637       if (d)
06638          return d;
06639    }
06640    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06641    if (d)
06642       return d;
06643    d = ast_waitfordigit(chan, 4000);
06644    return d;
06645 }
06646 
06647 /*!
06648  * \brief plays a prompt and waits for a keypress.
06649  * \param chan
06650  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06651  * \param start Does not appear to be used at this time.
06652  *
06653  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06654  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06655  * prompting for the number inputs that correspond to the available folders.
06656  * 
06657  * \return zero on success, or -1 on error.
06658  */
06659 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06660 {
06661    int res = 0;
06662    int loops = 0;
06663    res = ast_play_and_wait(chan, fn);  /* Folder name */
06664    while (((res < '0') || (res > '9')) &&
06665          (res != '#') && (res >= 0) &&
06666          loops < 4) {
06667       res = get_folder(chan, 0);
06668       loops++;
06669    }
06670    if (loops == 4) { /* give up */
06671       return '#';
06672    }
06673    return res;
06674 }
06675 
06676 /*!
06677  * \brief presents the option to prepend to an existing message when forwarding it.
06678  * \param chan
06679  * \param vmu
06680  * \param curdir
06681  * \param curmsg
06682  * \param vm_fmts
06683  * \param context
06684  * \param record_gain
06685  * \param duration
06686  * \param vms
06687  * \param flag 
06688  *
06689  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06690  *
06691  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06692  * \return zero on success, -1 on error.
06693  */
06694 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06695          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06696 {
06697 #ifdef IMAP_STORAGE
06698    int res;
06699 #endif
06700    int cmd = 0;
06701    int retries = 0, prepend_duration = 0, already_recorded = 0;
06702    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06703    char textfile[PATH_MAX];
06704    struct ast_config *msg_cfg;
06705    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06706 #ifndef IMAP_STORAGE
06707    signed char zero_gain = 0;
06708 #endif
06709    const char *duration_str;
06710 
06711    /* Must always populate duration correctly */
06712    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06713    strcpy(textfile, msgfile);
06714    strcpy(backup, msgfile);
06715    strcpy(backup_textfile, msgfile);
06716    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06717    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06718    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06719 
06720    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06721       *duration = atoi(duration_str);
06722    } else {
06723       *duration = 0;
06724    }
06725 
06726    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06727       if (cmd)
06728          retries = 0;
06729       switch (cmd) {
06730       case '1': 
06731 
06732 #ifdef IMAP_STORAGE
06733          /* Record new intro file */
06734          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06735          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06736          res = ast_play_and_wait(chan, INTRO);
06737          res = ast_play_and_wait(chan, "beep");
06738          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06739          cmd = 't';
06740 #else
06741 
06742          /* prepend a message to the current message, update the metadata and return */
06743 
06744          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06745          strcpy(textfile, msgfile);
06746          strncat(textfile, ".txt", sizeof(textfile) - 1);
06747          *duration = 0;
06748 
06749          /* if we can't read the message metadata, stop now */
06750          if (!msg_cfg) {
06751             cmd = 0;
06752             break;
06753          }
06754          
06755          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06756 #ifndef IMAP_STORAGE
06757          if (already_recorded) {
06758             ast_filecopy(backup, msgfile, NULL);
06759             copy(backup_textfile, textfile);
06760          }
06761          else {
06762             ast_filecopy(msgfile, backup, NULL);
06763             copy(textfile, backup_textfile);
06764          }
06765 #endif
06766          already_recorded = 1;
06767 
06768          if (record_gain)
06769             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06770 
06771          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06772          if (cmd == 'S') {
06773             ast_filerename(backup, msgfile, NULL);
06774          }
06775 
06776          if (record_gain)
06777             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06778 
06779          
06780          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06781             *duration = atoi(duration_str);
06782 
06783          if (prepend_duration) {
06784             struct ast_category *msg_cat;
06785             /* need enough space for a maximum-length message duration */
06786             char duration_buf[12];
06787 
06788             *duration += prepend_duration;
06789             msg_cat = ast_category_get(msg_cfg, "message");
06790             snprintf(duration_buf, 11, "%ld", *duration);
06791             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06792                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06793             }
06794          }
06795 
06796 #endif
06797          break;
06798       case '2': 
06799          /* NULL out introfile so we know there is no intro! */
06800 #ifdef IMAP_STORAGE
06801          *vms->introfn = '\0';
06802 #endif
06803          cmd = 't';
06804          break;
06805       case '*':
06806          cmd = '*';
06807          break;
06808       default: 
06809          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06810             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06811          if (!cmd)
06812             cmd = ast_play_and_wait(chan, "vm-starmain");
06813             /* "press star to return to the main menu" */
06814          if (!cmd)
06815             cmd = ast_waitfordigit(chan, 6000);
06816          if (!cmd)
06817             retries++;
06818          if (retries > 3)
06819             cmd = 't';
06820       }
06821    }
06822 
06823    if (msg_cfg)
06824       ast_config_destroy(msg_cfg);
06825    if (prepend_duration)
06826       *duration = prepend_duration;
06827 
06828    if (already_recorded && cmd == -1) {
06829       /* restore original message if prepention cancelled */
06830       ast_filerename(backup, msgfile, NULL);
06831       rename(backup_textfile, textfile);
06832    }
06833 
06834    if (cmd == 't' || cmd == 'S')
06835       cmd = 0;
06836    return cmd;
06837 }
06838 
06839 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06840 {
06841    struct ast_event *event;
06842    char *mailbox, *context;
06843 
06844    /* Strip off @default */
06845    context = mailbox = ast_strdupa(box);
06846    strsep(&context, "@");
06847    if (ast_strlen_zero(context))
06848       context = "default";
06849 
06850    if (!(event = ast_event_new(AST_EVENT_MWI,
06851          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06852          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06853          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06854          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06855          AST_EVENT_IE_END))) {
06856       return;
06857    }
06858 
06859    ast_event_queue_and_cache(event);
06860 }
06861 
06862 /*!
06863  * \brief Sends email notification that a user has a new voicemail waiting for them.
06864  * \param chan
06865  * \param vmu
06866  * \param vms
06867  * \param msgnum
06868  * \param duration
06869  * \param fmt
06870  * \param cidnum The Caller ID phone number value.
06871  * \param cidname The Caller ID name value.
06872  * \param flag
06873  *
06874  * \return zero on success, -1 on error.
06875  */
06876 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)
06877 {
06878    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06879    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06880    const char *category;
06881    char *myserveremail = serveremail;
06882 
06883    ast_channel_lock(chan);
06884    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06885       category = ast_strdupa(category);
06886    }
06887    ast_channel_unlock(chan);
06888 
06889 #ifndef IMAP_STORAGE
06890    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06891 #else
06892    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06893 #endif
06894    make_file(fn, sizeof(fn), todir, msgnum);
06895    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06896 
06897    if (!ast_strlen_zero(vmu->attachfmt)) {
06898       if (strstr(fmt, vmu->attachfmt))
06899          fmt = vmu->attachfmt;
06900       else
06901          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);
06902    }
06903 
06904    /* Attach only the first format */
06905    fmt = ast_strdupa(fmt);
06906    stringp = fmt;
06907    strsep(&stringp, "|");
06908 
06909    if (!ast_strlen_zero(vmu->serveremail))
06910       myserveremail = vmu->serveremail;
06911 
06912    if (!ast_strlen_zero(vmu->email)) {
06913       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06914 
06915       if (attach_user_voicemail)
06916          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06917 
06918       /* XXX possible imap issue, should category be NULL XXX */
06919       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06920 
06921       if (attach_user_voicemail)
06922          DISPOSE(todir, msgnum);
06923    }
06924 
06925    if (!ast_strlen_zero(vmu->pager)) {
06926       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
06927    }
06928 
06929    if (ast_test_flag(vmu, VM_DELETE))
06930       DELETE(todir, msgnum, fn, vmu);
06931 
06932    /* Leave voicemail for someone */
06933    if (ast_app_has_voicemail(ext_context, NULL)) 
06934       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06935 
06936    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06937 
06938    ast_manager_event(chan, 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);
06939    run_externnotify(vmu->context, vmu->mailbox, flag);
06940 
06941 #ifdef IMAP_STORAGE
06942    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06943    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06944       vm_imap_delete(NULL, vms->curmsg, vmu);
06945       vms->newmessages--;  /* Fix new message count */
06946    }
06947 #endif
06948 
06949    return 0;
06950 }
06951 
06952 /*!
06953  * \brief Sends a voicemail message to a mailbox recipient.
06954  * \param chan
06955  * \param context
06956  * \param vms
06957  * \param sender
06958  * \param fmt
06959  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06960  *             Will be 0 when called to forward an existing message (option 8)
06961  *             Will be 1 when called to leave a message (option 3->5)
06962  * \param record_gain 
06963  * \param urgent
06964  *
06965  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06966  * 
06967  * When in the leave message mode (is_new_message == 1):
06968  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06969  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06970  *
06971  * When in the forward message mode (is_new_message == 0):
06972  *   - retreives the current message to be forwarded
06973  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06974  *   - determines the target mailbox and folders
06975  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06976  *
06977  * \return zero on success, -1 on error.
06978  */
06979 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)
06980 {
06981 #ifdef IMAP_STORAGE
06982    int todircount = 0;
06983    struct vm_state *dstvms;
06984 #endif
06985    char username[70]="";
06986    char fn[PATH_MAX]; /* for playback of name greeting */
06987    char ecodes[16] = "#";
06988    int res = 0, cmd = 0;
06989    struct ast_vm_user *receiver = NULL, *vmtmp;
06990    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06991    char *stringp;
06992    const char *s;
06993    int saved_messages = 0;
06994    int valid_extensions = 0;
06995    char *dir;
06996    int curmsg;
06997    char urgent_str[7] = "";
06998    char tmptxtfile[PATH_MAX];
06999    int prompt_played = 0;
07000 #ifndef IMAP_STORAGE
07001    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07002 #endif
07003    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07004       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07005    }
07006 
07007    if (vms == NULL) return -1;
07008    dir = vms->curdir;
07009    curmsg = vms->curmsg;
07010 
07011    tmptxtfile[0] = '\0';
07012    while (!res && !valid_extensions) {
07013       int use_directory = 0;
07014       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07015          int done = 0;
07016          int retries = 0;
07017          cmd = 0;
07018          while ((cmd >= 0) && !done ){
07019             if (cmd)
07020                retries = 0;
07021             switch (cmd) {
07022             case '1': 
07023                use_directory = 0;
07024                done = 1;
07025                break;
07026             case '2': 
07027                use_directory = 1;
07028                done = 1;
07029                break;
07030             case '*': 
07031                cmd = 't';
07032                done = 1;
07033                break;
07034             default: 
07035                /* Press 1 to enter an extension press 2 to use the directory */
07036                cmd = ast_play_and_wait(chan, "vm-forward");
07037                if (!cmd)
07038                   cmd = ast_waitfordigit(chan, 3000);
07039                if (!cmd)
07040                   retries++;
07041                if (retries > 3) {
07042                   cmd = 't';
07043                   done = 1;
07044                }
07045                
07046             }
07047          }
07048          if (cmd < 0 || cmd == 't')
07049             break;
07050       }
07051       
07052       if (use_directory) {
07053          /* use app_directory */
07054          
07055          char old_context[sizeof(chan->context)];
07056          char old_exten[sizeof(chan->exten)];
07057          int old_priority;
07058          struct ast_app* directory_app;
07059 
07060          directory_app = pbx_findapp("Directory");
07061          if (directory_app) {
07062             char vmcontext[256];
07063             /* make backup copies */
07064             memcpy(old_context, chan->context, sizeof(chan->context));
07065             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07066             old_priority = chan->priority;
07067             
07068             /* call the the Directory, changes the channel */
07069             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07070             res = pbx_exec(chan, directory_app, vmcontext);
07071             
07072             ast_copy_string(username, chan->exten, sizeof(username));
07073             
07074             /* restore the old context, exten, and priority */
07075             memcpy(chan->context, old_context, sizeof(chan->context));
07076             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07077             chan->priority = old_priority;
07078          } else {
07079             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07080             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07081          }
07082       } else {
07083          /* Ask for an extension */
07084          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07085          prompt_played++;
07086          if (res || prompt_played > 4)
07087             break;
07088          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07089             break;
07090       }
07091       
07092       /* start all over if no username */
07093       if (ast_strlen_zero(username))
07094          continue;
07095       stringp = username;
07096       s = strsep(&stringp, "*");
07097       /* start optimistic */
07098       valid_extensions = 1;
07099       while (s) {
07100          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07101             int oldmsgs;
07102             int newmsgs;
07103             int capacity;
07104             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07105                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07106                /* Shouldn't happen, but allow trying another extension if it does */
07107                res = ast_play_and_wait(chan, "pbx-invalid");
07108                valid_extensions = 0;
07109                break;
07110             }
07111             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07112             if ((newmsgs + oldmsgs) >= capacity) {
07113                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07114                res = ast_play_and_wait(chan, "vm-mailboxfull");
07115                valid_extensions = 0;
07116                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07117                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07118                   free_user(vmtmp);
07119                }
07120                inprocess_count(receiver->mailbox, receiver->context, -1);
07121                break;
07122             }
07123             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07124          } else {
07125             /* XXX Optimization for the future.  When we encounter a single bad extension,
07126              * bailing out on all of the extensions may not be the way to go.  We should
07127              * probably just bail on that single extension, then allow the user to enter
07128              * several more. XXX
07129              */
07130             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07131                free_user(receiver);
07132             }
07133             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07134             /* "I am sorry, that's not a valid extension.  Please try again." */
07135             res = ast_play_and_wait(chan, "pbx-invalid");
07136             valid_extensions = 0;
07137             break;
07138          }
07139 
07140          /* play name if available, else play extension number */
07141          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07142          RETRIEVE(fn, -1, s, receiver->context);
07143          if (ast_fileexists(fn, NULL, NULL) > 0) {
07144             res = ast_stream_and_wait(chan, fn, ecodes);
07145             if (res) {
07146                DISPOSE(fn, -1);
07147                return res;
07148             }
07149          } else {
07150             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07151          }
07152          DISPOSE(fn, -1);
07153 
07154          s = strsep(&stringp, "*");
07155       }
07156       /* break from the loop of reading the extensions */
07157       if (valid_extensions)
07158          break;
07159    }
07160    /* check if we're clear to proceed */
07161    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07162       return res;
07163    if (is_new_message == 1) {
07164       struct leave_vm_options leave_options;
07165       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07166       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07167 
07168       /* Send VoiceMail */
07169       memset(&leave_options, 0, sizeof(leave_options));
07170       leave_options.record_gain = record_gain;
07171       cmd = leave_voicemail(chan, mailbox, &leave_options);
07172    } else {
07173       /* Forward VoiceMail */
07174       long duration = 0;
07175       struct vm_state vmstmp;
07176       int copy_msg_result = 0;
07177       memcpy(&vmstmp, vms, sizeof(vmstmp));
07178 
07179       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07180 
07181       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07182       if (!cmd) {
07183          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07184 #ifdef IMAP_STORAGE
07185             int attach_user_voicemail;
07186             char *myserveremail = serveremail;
07187             
07188             /* get destination mailbox */
07189             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07190             if (!dstvms) {
07191                dstvms = create_vm_state_from_user(vmtmp);
07192             }
07193             if (dstvms) {
07194                init_mailstream(dstvms, 0);
07195                if (!dstvms->mailstream) {
07196                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07197                } else {
07198                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07199                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07200                }
07201             } else {
07202                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07203             }
07204             if (!ast_strlen_zero(vmtmp->serveremail))
07205                myserveremail = vmtmp->serveremail;
07206             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07207             /* NULL category for IMAP storage */
07208             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07209                dstvms->curbox,
07210                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07211                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07212                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07213                NULL, urgent_str);
07214 #else
07215             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07216 #endif
07217             saved_messages++;
07218             AST_LIST_REMOVE_CURRENT(list);
07219             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07220             free_user(vmtmp);
07221             if (res)
07222                break;
07223          }
07224          AST_LIST_TRAVERSE_SAFE_END;
07225          if (saved_messages > 0 && !copy_msg_result) {
07226             /* give confirmation that the message was saved */
07227             /* commented out since we can't forward batches yet
07228             if (saved_messages == 1)
07229                res = ast_play_and_wait(chan, "vm-message");
07230             else
07231                res = ast_play_and_wait(chan, "vm-messages");
07232             if (!res)
07233                res = ast_play_and_wait(chan, "vm-saved"); */
07234 #ifdef IMAP_STORAGE
07235             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07236             if (ast_strlen_zero(vmstmp.introfn))
07237 #endif
07238             res = ast_play_and_wait(chan, "vm-msgsaved");
07239          }
07240 #ifndef IMAP_STORAGE
07241          else {
07242             /* with IMAP, mailbox full warning played by imap_check_limits */
07243             res = ast_play_and_wait(chan, "vm-mailboxfull");
07244          }
07245          /* Restore original message without prepended message if backup exists */
07246          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07247          strcpy(textfile, msgfile);
07248          strcpy(backup, msgfile);
07249          strcpy(backup_textfile, msgfile);
07250          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07251          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07252          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07253          if (ast_fileexists(backup, NULL, NULL) > 0) {
07254             ast_filerename(backup, msgfile, NULL);
07255             rename(backup_textfile, textfile);
07256          }
07257 #endif
07258       }
07259       DISPOSE(dir, curmsg);
07260 #ifndef IMAP_STORAGE
07261       if (cmd) { /* assuming hangup, cleanup backup file */
07262          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07263          strcpy(textfile, msgfile);
07264          strcpy(backup_textfile, msgfile);
07265          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07266          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07267          rename(backup_textfile, textfile);
07268       }
07269 #endif
07270    }
07271 
07272    /* If anything failed above, we still have this list to free */
07273    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07274       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07275       free_user(vmtmp);
07276    }
07277    return res ? res : cmd;
07278 }
07279 
07280 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07281 {
07282    int res;
07283    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07284       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07285    return res;
07286 }
07287 
07288 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07289 {
07290    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);
07291 }
07292 
07293 static int play_message_category(struct ast_channel *chan, const char *category)
07294 {
07295    int res = 0;
07296 
07297    if (!ast_strlen_zero(category))
07298       res = ast_play_and_wait(chan, category);
07299 
07300    if (res) {
07301       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07302       res = 0;
07303    }
07304 
07305    return res;
07306 }
07307 
07308 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07309 {
07310    int res = 0;
07311    struct vm_zone *the_zone = NULL;
07312    time_t t;
07313 
07314    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07315       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07316       return 0;
07317    }
07318 
07319    /* Does this user have a timezone specified? */
07320    if (!ast_strlen_zero(vmu->zonetag)) {
07321       /* Find the zone in the list */
07322       struct vm_zone *z;
07323       AST_LIST_LOCK(&zones);
07324       AST_LIST_TRAVERSE(&zones, z, list) {
07325          if (!strcmp(z->name, vmu->zonetag)) {
07326             the_zone = z;
07327             break;
07328          }
07329       }
07330       AST_LIST_UNLOCK(&zones);
07331    }
07332 
07333 /* No internal variable parsing for now, so we'll comment it out for the time being */
07334 #if 0
07335    /* Set the DIFF_* variables */
07336    ast_localtime(&t, &time_now, NULL);
07337    tv_now = ast_tvnow();
07338    ast_localtime(&tv_now, &time_then, NULL);
07339 
07340    /* Day difference */
07341    if (time_now.tm_year == time_then.tm_year)
07342       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07343    else
07344       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07345    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07346 
07347    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07348 #endif
07349    if (the_zone) {
07350       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07351    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07352       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07353    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07354       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07355    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07356       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);
07357    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07358       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07359    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07360       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07361    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07362       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07363    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07364       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);
07365    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07366       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07367    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07368       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07369    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07370       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
07371    } else {
07372       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07373    }
07374 #if 0
07375    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07376 #endif
07377    return res;
07378 }
07379 
07380 
07381 
07382 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07383 {
07384    int res = 0;
07385    int i;
07386    char *callerid, *name;
07387    char prefile[PATH_MAX] = "";
07388    
07389 
07390    /* If voicemail cid is not enabled, or we didn't get cid or context from
07391     * the attribute file, leave now.
07392     *
07393     * TODO Still need to change this so that if this function is called by the
07394     * message envelope (and someone is explicitly requesting to hear the CID),
07395     * it does not check to see if CID is enabled in the config file.
07396     */
07397    if ((cid == NULL)||(context == NULL))
07398       return res;
07399 
07400    /* Strip off caller ID number from name */
07401    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07402    ast_callerid_parse(cid, &name, &callerid);
07403    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07404       /* Check for internal contexts and only */
07405       /* say extension when the call didn't come from an internal context in the list */
07406       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07407          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07408          if ((strcmp(cidinternalcontexts[i], context) == 0))
07409             break;
07410       }
07411       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07412          if (!res) {
07413             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07414             if (!ast_strlen_zero(prefile)) {
07415             /* See if we can find a recorded name for this person instead of their extension number */
07416                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07417                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07418                   if (!callback)
07419                      res = wait_file2(chan, vms, "vm-from");
07420                   res = ast_stream_and_wait(chan, prefile, "");
07421                } else {
07422                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07423                   /* Say "from extension" as one saying to sound smoother */
07424                   if (!callback)
07425                      res = wait_file2(chan, vms, "vm-from-extension");
07426                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07427                }
07428             }
07429          }
07430       } else if (!res) {
07431          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07432          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07433          if (!callback)
07434             res = wait_file2(chan, vms, "vm-from-phonenumber");
07435          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07436       }
07437    } else {
07438       /* Number unknown */
07439       ast_debug(1, "VM-CID: From an unknown number\n");
07440       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07441       res = wait_file2(chan, vms, "vm-unknown-caller");
07442    }
07443    return res;
07444 }
07445 
07446 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07447 {
07448    int res = 0;
07449    int durationm;
07450    int durations;
07451    /* Verify that we have a duration for the message */
07452    if (duration == NULL)
07453       return res;
07454 
07455    /* Convert from seconds to minutes */
07456    durations = atoi(duration);
07457    durationm = (durations / 60);
07458 
07459    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07460 
07461    if ((!res) && (durationm >= minduration)) {
07462       res = wait_file2(chan, vms, "vm-duration");
07463 
07464       /* POLISH syntax */
07465       if (!strncasecmp(chan->language, "pl", 2)) {
07466          div_t num = div(durationm, 10);
07467 
07468          if (durationm == 1) {
07469             res = ast_play_and_wait(chan, "digits/1z");
07470             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07471          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07472             if (num.rem == 2) {
07473                if (!num.quot) {
07474                   res = ast_play_and_wait(chan, "digits/2-ie");
07475                } else {
07476                   res = say_and_wait(chan, durationm - 2 , chan->language);
07477                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07478                }
07479             } else {
07480                res = say_and_wait(chan, durationm, chan->language);
07481             }
07482             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07483          } else {
07484             res = say_and_wait(chan, durationm, chan->language);
07485             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07486          }
07487       /* DEFAULT syntax */
07488       } else {
07489          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07490          res = wait_file2(chan, vms, "vm-minutes");
07491       }
07492    }
07493    return res;
07494 }
07495 
07496 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07497 {
07498    int res = 0;
07499    char filename[256], *cid;
07500    const char *origtime, *context, *category, *duration, *flag;
07501    struct ast_config *msg_cfg;
07502    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07503 
07504    vms->starting = 0;
07505    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07506    adsi_message(chan, vms);
07507    if (!vms->curmsg)
07508       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07509    else if (vms->curmsg == vms->lastmsg)
07510       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07511 
07512    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07513    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07514    msg_cfg = ast_config_load(filename, config_flags);
07515    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07516       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07517       return 0;
07518    }
07519    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07520 
07521    /* Play the word urgent if we are listening to urgent messages */
07522    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07523       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07524    }
07525 
07526    if (!res) {
07527       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07528       /* POLISH syntax */
07529       if (!strncasecmp(chan->language, "pl", 2)) {
07530          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07531             int ten, one;
07532             char nextmsg[256];
07533             ten = (vms->curmsg + 1) / 10;
07534             one = (vms->curmsg + 1) % 10;
07535 
07536             if (vms->curmsg < 20) {
07537                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07538                res = wait_file2(chan, vms, nextmsg);
07539             } else {
07540                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07541                res = wait_file2(chan, vms, nextmsg);
07542                if (one > 0) {
07543                   if (!res) {
07544                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07545                      res = wait_file2(chan, vms, nextmsg);
07546                   }
07547                }
07548             }
07549          }
07550          if (!res)
07551             res = wait_file2(chan, vms, "vm-message");
07552       /* HEBREW syntax */
07553       } else if (!strncasecmp(chan->language, "he", 2)) {
07554          if (!vms->curmsg) {
07555             res = wait_file2(chan, vms, "vm-message");
07556             res = wait_file2(chan, vms, "vm-first");
07557          } else if (vms->curmsg == vms->lastmsg) {
07558             res = wait_file2(chan, vms, "vm-message");
07559             res = wait_file2(chan, vms, "vm-last");
07560          } else {
07561             res = wait_file2(chan, vms, "vm-message");
07562             res = wait_file2(chan, vms, "vm-number");
07563             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07564          }
07565       /* VIETNAMESE syntax */
07566       } else if (!strncasecmp(chan->language, "vi", 2)) {
07567          if (!vms->curmsg) {
07568             res = wait_file2(chan, vms, "vm-message");
07569             res = wait_file2(chan, vms, "vm-first");
07570          } else if (vms->curmsg == vms->lastmsg) {
07571             res = wait_file2(chan, vms, "vm-message");
07572             res = wait_file2(chan, vms, "vm-last");
07573          } else {
07574             res = wait_file2(chan, vms, "vm-message");
07575             res = wait_file2(chan, vms, "vm-number");
07576             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07577          }
07578       } else {
07579          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07580             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07581          } else { /* DEFAULT syntax */
07582             res = wait_file2(chan, vms, "vm-message");
07583          }
07584          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07585             if (!res) {
07586                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07587             }
07588          }
07589       }
07590    }
07591 
07592    if (!msg_cfg) {
07593       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07594       return 0;
07595    }
07596 
07597    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07598       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07599       DISPOSE(vms->curdir, vms->curmsg);
07600       ast_config_destroy(msg_cfg);
07601       return 0;
07602    }
07603 
07604    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07605    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07606    category = ast_variable_retrieve(msg_cfg, "message", "category");
07607 
07608    context = ast_variable_retrieve(msg_cfg, "message", "context");
07609    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07610       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07611    if (!res) {
07612       res = play_message_category(chan, category);
07613    }
07614    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07615       res = play_message_datetime(chan, vmu, origtime, filename);
07616    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07617       res = play_message_callerid(chan, vms, cid, context, 0);
07618    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07619       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07620    /* Allow pressing '1' to skip envelope / callerid */
07621    if (res == '1')
07622       res = 0;
07623    ast_config_destroy(msg_cfg);
07624 
07625    if (!res) {
07626       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07627       vms->heard[vms->curmsg] = 1;
07628 #ifdef IMAP_STORAGE
07629       /*IMAP storage stores any prepended message from a forward
07630        * as a separate file from the rest of the message
07631        */
07632       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07633          wait_file(chan, vms, vms->introfn);
07634       }
07635 #endif
07636       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07637          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07638          res = 0;
07639       }
07640    }
07641    DISPOSE(vms->curdir, vms->curmsg);
07642    return res;
07643 }
07644 
07645 #ifdef IMAP_STORAGE
07646 static int imap_remove_file(char *dir, int msgnum)
07647 {
07648    char fn[PATH_MAX];
07649    char full_fn[PATH_MAX];
07650    char intro[PATH_MAX] = {0,};
07651    
07652    if (msgnum > -1) {
07653       make_file(fn, sizeof(fn), dir, msgnum);
07654       snprintf(intro, sizeof(intro), "%sintro", fn);
07655    } else
07656       ast_copy_string(fn, dir, sizeof(fn));
07657    
07658    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07659       ast_filedelete(fn, NULL);
07660       if (!ast_strlen_zero(intro)) {
07661          ast_filedelete(intro, NULL);
07662       }
07663       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07664       unlink(full_fn);
07665    }
07666    return 0;
07667 }
07668 
07669 
07670 
07671 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07672 {
07673    char *file, *filename;
07674    char *attachment;
07675    char arg[10];
07676    int i;
07677    BODY* body;
07678 
07679    file = strrchr(ast_strdupa(dir), '/');
07680    if (file) {
07681       *file++ = '\0';
07682    } else {
07683       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07684       return -1;
07685    }
07686 
07687    ast_mutex_lock(&vms->lock);
07688    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07689       mail_fetchstructure(vms->mailstream, i + 1, &body);
07690       /* We have the body, now we extract the file name of the first attachment. */
07691       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07692          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07693       } else {
07694          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07695          ast_mutex_unlock(&vms->lock);
07696          return -1;
07697       }
07698       filename = strsep(&attachment, ".");
07699       if (!strcmp(filename, file)) {
07700          sprintf(arg, "%d", i + 1);
07701          mail_setflag(vms->mailstream, arg, "\\DELETED");
07702       }
07703    }
07704    mail_expunge(vms->mailstream);
07705    ast_mutex_unlock(&vms->lock);
07706    return 0;
07707 }
07708 
07709 #elif !defined(IMAP_STORAGE)
07710 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07711 {
07712    int count_msg, last_msg;
07713 
07714    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07715 
07716    /* Rename the member vmbox HERE so that we don't try to return before
07717     * we know what's going on.
07718     */
07719    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07720 
07721    /* Faster to make the directory than to check if it exists. */
07722    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07723 
07724    /* traverses directory using readdir (or select query for ODBC) */
07725    count_msg = count_messages(vmu, vms->curdir);
07726    if (count_msg < 0) {
07727       return count_msg;
07728    } else {
07729       vms->lastmsg = count_msg - 1;
07730    }
07731 
07732    if (vm_allocate_dh(vms, vmu, count_msg)) {
07733       return -1;
07734    }
07735 
07736    /*
07737    The following test is needed in case sequencing gets messed up.
07738    There appears to be more than one way to mess up sequence, so
07739    we will not try to find all of the root causes--just fix it when
07740    detected.
07741    */
07742 
07743    if (vm_lock_path(vms->curdir)) {
07744       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07745       return ERROR_LOCK_PATH;
07746    }
07747 
07748    /* for local storage, checks directory for messages up to maxmsg limit */
07749    last_msg = last_message_index(vmu, vms->curdir);
07750    ast_unlock_path(vms->curdir);
07751 
07752    if (last_msg < -1) {
07753       return last_msg;
07754 #ifndef ODBC_STORAGE
07755    } else if (vms->lastmsg != last_msg) {
07756       ast_log(LOG_NOTICE, "Resequencing mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07757         resequence_mailbox(vmu, vms->curdir, count_msg);
07758 #endif
07759    }
07760 
07761    return 0;
07762 }
07763 #endif
07764 
07765 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07766 {
07767    int x = 0;
07768 #ifndef IMAP_STORAGE
07769    int res = 0, nummsg;
07770    char fn2[PATH_MAX];
07771 #endif
07772 
07773    if (vms->lastmsg <= -1) {
07774       goto done;
07775    }
07776 
07777    vms->curmsg = -1;
07778 #ifndef IMAP_STORAGE
07779    /* Get the deleted messages fixed */
07780    if (vm_lock_path(vms->curdir)) {
07781       return ERROR_LOCK_PATH;
07782    }
07783 
07784    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07785    for (x = 0; x < vms->lastmsg + 1; x++) {
07786       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07787          /* Save this message.  It's not in INBOX or hasn't been heard */
07788          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07789          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07790             break;
07791          }
07792          vms->curmsg++;
07793          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07794          if (strcmp(vms->fn, fn2)) {
07795             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07796          }
07797       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07798          /* Move to old folder before deleting */
07799          res = save_to_folder(vmu, vms, x, 1);
07800          if (res == ERROR_LOCK_PATH) {
07801             /* If save failed do not delete the message */
07802             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07803             vms->deleted[x] = 0;
07804             vms->heard[x] = 0;
07805             --x;
07806          }
07807       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07808          /* Move to deleted folder */
07809          res = save_to_folder(vmu, vms, x, 10);
07810          if (res == ERROR_LOCK_PATH) {
07811             /* If save failed do not delete the message */
07812             vms->deleted[x] = 0;
07813             vms->heard[x] = 0;
07814             --x;
07815          }
07816       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07817          /* If realtime storage enabled - we should explicitly delete this message,
07818          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07819          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07820          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07821             DELETE(vms->curdir, x, vms->fn, vmu);
07822          }
07823       }
07824    }
07825 
07826    /* Delete ALL remaining messages */
07827    nummsg = x - 1;
07828    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07829       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07830       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07831          DELETE(vms->curdir, x, vms->fn, vmu);
07832       }
07833    }
07834    ast_unlock_path(vms->curdir);
07835 #else /* defined(IMAP_STORAGE) */
07836    if (vms->deleted) {
07837       /* Since we now expunge after each delete, deleting in reverse order
07838        * ensures that no reordering occurs between each step. */
07839       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07840          if (vms->deleted[x]) {
07841             ast_debug(3, "IMAP delete of %d\n", x);
07842             DELETE(vms->curdir, x, vms->fn, vmu);
07843          }
07844       }
07845    }
07846 #endif
07847 
07848 done:
07849    if (vms->deleted && vmu->maxmsg) {
07850       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07851    }
07852    if (vms->heard && vmu->maxmsg) {
07853       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07854    }
07855 
07856    return 0;
07857 }
07858 
07859 /* In Greek even though we CAN use a syntax like "friends messages"
07860  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07861  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07862  * syntax for the above three categories which is more elegant.
07863  */
07864 
07865 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07866 {
07867    int cmd;
07868    char *buf;
07869 
07870    buf = alloca(strlen(box) + 2);
07871    strcpy(buf, box);
07872    strcat(buf, "s");
07873 
07874    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07875       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07876       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07877    } else {
07878       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07879       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07880    }
07881 }
07882 
07883 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07884 {
07885    int cmd;
07886 
07887    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07888       if (!strcasecmp(box, "vm-INBOX"))
07889          cmd = ast_play_and_wait(chan, "vm-new-e");
07890       else
07891          cmd = ast_play_and_wait(chan, "vm-old-e");
07892       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07893    } else {
07894       cmd = ast_play_and_wait(chan, "vm-messages");
07895       return cmd ? cmd : ast_play_and_wait(chan, box);
07896    }
07897 }
07898 
07899 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07900 {
07901    int cmd;
07902 
07903    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07904       cmd = ast_play_and_wait(chan, "vm-messages");
07905       return cmd ? cmd : ast_play_and_wait(chan, box);
07906    } else {
07907       cmd = ast_play_and_wait(chan, box);
07908       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07909    }
07910 }
07911 
07912 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07913 {
07914    int cmd;
07915 
07916    if (  !strncasecmp(chan->language, "it", 2) ||
07917         !strncasecmp(chan->language, "es", 2) ||
07918         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07919       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07920       return cmd ? cmd : ast_play_and_wait(chan, box);
07921    } else if (!strncasecmp(chan->language, "gr", 2)) {
07922       return vm_play_folder_name_gr(chan, box);
07923    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07924       return ast_play_and_wait(chan, box);
07925    } else if (!strncasecmp(chan->language, "pl", 2)) {
07926       return vm_play_folder_name_pl(chan, box);
07927    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07928       return vm_play_folder_name_ua(chan, box);
07929    } else if (!strncasecmp(chan->language, "vi", 2)) {
07930       return ast_play_and_wait(chan, box);
07931    } else {  /* Default English */
07932       cmd = ast_play_and_wait(chan, box);
07933       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07934    }
07935 }
07936 
07937 /* GREEK SYNTAX
07938    In greek the plural for old/new is
07939    different so we need the following files
07940    We also need vm-denExeteMynhmata because
07941    this syntax is different.
07942 
07943    -> vm-Olds.wav : "Palia"
07944    -> vm-INBOXs.wav : "Nea"
07945    -> vm-denExeteMynhmata : "den exete mynhmata"
07946 */
07947 
07948 
07949 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07950 {
07951    int res = 0;
07952 
07953    if (vms->newmessages) {
07954       res = ast_play_and_wait(chan, "vm-youhave");
07955       if (!res) 
07956          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07957       if (!res) {
07958          if ((vms->newmessages == 1)) {
07959             res = ast_play_and_wait(chan, "vm-INBOX");
07960             if (!res)
07961                res = ast_play_and_wait(chan, "vm-message");
07962          } else {
07963             res = ast_play_and_wait(chan, "vm-INBOXs");
07964             if (!res)
07965                res = ast_play_and_wait(chan, "vm-messages");
07966          }
07967       }
07968    } else if (vms->oldmessages){
07969       res = ast_play_and_wait(chan, "vm-youhave");
07970       if (!res)
07971          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07972       if ((vms->oldmessages == 1)){
07973          res = ast_play_and_wait(chan, "vm-Old");
07974          if (!res)
07975             res = ast_play_and_wait(chan, "vm-message");
07976       } else {
07977          res = ast_play_and_wait(chan, "vm-Olds");
07978          if (!res)
07979             res = ast_play_and_wait(chan, "vm-messages");
07980       }
07981    } else if (!vms->oldmessages && !vms->newmessages) 
07982       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07983    return res;
07984 }
07985 
07986 /* Version of vm_intro() designed to work for many languages.
07987  *
07988  * It is hoped that this function can prevent the proliferation of 
07989  * language-specific vm_intro() functions and in time replace the language-
07990  * specific functions which already exist.  An examination of the language-
07991  * specific functions revealed that they all corrected the same deficiencies
07992  * in vm_intro_en() (which was the default function). Namely:
07993  *
07994  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07995  *     wording of the voicemail greeting hides this problem.  For example,
07996  *     vm-INBOX contains only the word "new".  This means that both of these
07997  *     sequences produce valid utterances:
07998  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07999  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08000  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08001  *     in many languages) the first utterance becomes "you have 1 the new message".
08002  *  2) The function contains hardcoded rules for pluralizing the word "message".
08003  *     These rules are correct for English, but not for many other languages.
08004  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08005  *     required in many languages.
08006  *  4) The gender of the word for "message" is not specified. This is a problem
08007  *     because in many languages the gender of the number in phrases such
08008  *     as "you have one new message" must match the gender of the word
08009  *     meaning "message".
08010  *
08011  * Fixing these problems for each new language has meant duplication of effort.
08012  * This new function solves the problems in the following general ways:
08013  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08014  *     and vm-Old respectively for those languages where it makes sense.
08015  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08016  *     on vm-message.
08017  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08018  *     prefix on vm-new and vm-old (none for English).
08019  *  4) Pass the gender of the language's word for "message" as an agument to
08020  *     this function which is can in turn pass on to the functions which 
08021  *     say numbers and put endings on nounds and adjectives.
08022  *
08023  * All languages require these messages:
08024  *  vm-youhave    "You have..."
08025  *  vm-and     "and"
08026  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08027  *
08028  * To use it for English, you will need these additional sound files:
08029  *  vm-new     "new"
08030  *  vm-message    "message", singular
08031  *  vm-messages      "messages", plural
08032  *
08033  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08034  *
08035  *  vm-newn    "novoye" (singular, neuter)
08036  *  vm-newx    "novikh" (counting plural form, genative plural)
08037  *  vm-message    "sobsheniye" (singular form)
08038  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08039  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08040  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08041  *  digits/2n     "dva" (neuter singular)
08042  */
08043 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08044 {
08045    int res;
08046    int lastnum = 0;
08047 
08048    res = ast_play_and_wait(chan, "vm-youhave");
08049 
08050    if (!res && vms->newmessages) {
08051       lastnum = vms->newmessages;
08052 
08053       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08054          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08055       }
08056 
08057       if (!res && vms->oldmessages) {
08058          res = ast_play_and_wait(chan, "vm-and");
08059       }
08060    }
08061 
08062    if (!res && vms->oldmessages) {
08063       lastnum = vms->oldmessages;
08064 
08065       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08066          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08067       }
08068    }
08069 
08070    if (!res) {
08071       if (lastnum == 0) {
08072          res = ast_play_and_wait(chan, "vm-no");
08073       }
08074       if (!res) {
08075          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08076       }
08077    }
08078 
08079    return res;
08080 }
08081 
08082 /* Default Hebrew syntax */
08083 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08084 {
08085    int res = 0;
08086 
08087    /* Introduce messages they have */
08088    if (!res) {
08089       if ((vms->newmessages) || (vms->oldmessages)) {
08090          res = ast_play_and_wait(chan, "vm-youhave");
08091       }
08092       /*
08093        * The word "shtei" refers to the number 2 in hebrew when performing a count
08094        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08095        * an element, this is one of them.
08096        */
08097       if (vms->newmessages) {
08098          if (!res) {
08099             if (vms->newmessages == 1) {
08100                res = ast_play_and_wait(chan, "vm-INBOX1");
08101             } else {
08102                if (vms->newmessages == 2) {
08103                   res = ast_play_and_wait(chan, "vm-shtei");
08104                } else {
08105                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08106                }
08107                res = ast_play_and_wait(chan, "vm-INBOX");
08108             }
08109          }
08110          if (vms->oldmessages && !res) {
08111             res = ast_play_and_wait(chan, "vm-and");
08112             if (vms->oldmessages == 1) {
08113                res = ast_play_and_wait(chan, "vm-Old1");
08114             } else {
08115                if (vms->oldmessages == 2) {
08116                   res = ast_play_and_wait(chan, "vm-shtei");
08117                } else {
08118                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08119                }
08120                res = ast_play_and_wait(chan, "vm-Old");
08121             }
08122          }
08123       }
08124       if (!res && vms->oldmessages && !vms->newmessages) {
08125          if (!res) {
08126             if (vms->oldmessages == 1) {
08127                res = ast_play_and_wait(chan, "vm-Old1");
08128             } else {
08129                if (vms->oldmessages == 2) {
08130                   res = ast_play_and_wait(chan, "vm-shtei");
08131                } else {
08132                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08133                }
08134                res = ast_play_and_wait(chan, "vm-Old");
08135             }
08136          }
08137       }
08138       if (!res) {
08139          if (!vms->oldmessages && !vms->newmessages) {
08140             if (!res) {
08141                res = ast_play_and_wait(chan, "vm-nomessages");
08142             }
08143          }
08144       }
08145    }
08146    return res;
08147 }
08148    
08149 /* Default English syntax */
08150 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08151 {
08152    int res;
08153 
08154    /* Introduce messages they have */
08155    res = ast_play_and_wait(chan, "vm-youhave");
08156    if (!res) {
08157       if (vms->urgentmessages) {
08158          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08159          if (!res)
08160             res = ast_play_and_wait(chan, "vm-Urgent");
08161          if ((vms->oldmessages || vms->newmessages) && !res) {
08162             res = ast_play_and_wait(chan, "vm-and");
08163          } else if (!res) {
08164             if ((vms->urgentmessages == 1))
08165                res = ast_play_and_wait(chan, "vm-message");
08166             else
08167                res = ast_play_and_wait(chan, "vm-messages");
08168          }
08169       }
08170       if (vms->newmessages) {
08171          res = say_and_wait(chan, vms->newmessages, chan->language);
08172          if (!res)
08173             res = ast_play_and_wait(chan, "vm-INBOX");
08174          if (vms->oldmessages && !res)
08175             res = ast_play_and_wait(chan, "vm-and");
08176          else if (!res) {
08177             if ((vms->newmessages == 1))
08178                res = ast_play_and_wait(chan, "vm-message");
08179             else
08180                res = ast_play_and_wait(chan, "vm-messages");
08181          }
08182             
08183       }
08184       if (!res && vms->oldmessages) {
08185          res = say_and_wait(chan, vms->oldmessages, chan->language);
08186          if (!res)
08187             res = ast_play_and_wait(chan, "vm-Old");
08188          if (!res) {
08189             if (vms->oldmessages == 1)
08190                res = ast_play_and_wait(chan, "vm-message");
08191             else
08192                res = ast_play_and_wait(chan, "vm-messages");
08193          }
08194       }
08195       if (!res) {
08196          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08197             res = ast_play_and_wait(chan, "vm-no");
08198             if (!res)
08199                res = ast_play_and_wait(chan, "vm-messages");
08200          }
08201       }
08202    }
08203    return res;
08204 }
08205 
08206 /* ITALIAN syntax */
08207 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08208 {
08209    /* Introduce messages they have */
08210    int res;
08211    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08212       res = ast_play_and_wait(chan, "vm-no") ||
08213          ast_play_and_wait(chan, "vm-message");
08214    else
08215       res = ast_play_and_wait(chan, "vm-youhave");
08216    if (!res && vms->newmessages) {
08217       res = (vms->newmessages == 1) ?
08218          ast_play_and_wait(chan, "digits/un") ||
08219          ast_play_and_wait(chan, "vm-nuovo") ||
08220          ast_play_and_wait(chan, "vm-message") :
08221          /* 2 or more new messages */
08222          say_and_wait(chan, vms->newmessages, chan->language) ||
08223          ast_play_and_wait(chan, "vm-nuovi") ||
08224          ast_play_and_wait(chan, "vm-messages");
08225       if (!res && vms->oldmessages)
08226          res = ast_play_and_wait(chan, "vm-and");
08227    }
08228    if (!res && vms->oldmessages) {
08229       res = (vms->oldmessages == 1) ?
08230          ast_play_and_wait(chan, "digits/un") ||
08231          ast_play_and_wait(chan, "vm-vecchio") ||
08232          ast_play_and_wait(chan, "vm-message") :
08233          /* 2 or more old messages */
08234          say_and_wait(chan, vms->oldmessages, chan->language) ||
08235          ast_play_and_wait(chan, "vm-vecchi") ||
08236          ast_play_and_wait(chan, "vm-messages");
08237    }
08238    return res;
08239 }
08240 
08241 /* POLISH syntax */
08242 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08243 {
08244    /* Introduce messages they have */
08245    int res;
08246    div_t num;
08247 
08248    if (!vms->oldmessages && !vms->newmessages) {
08249       res = ast_play_and_wait(chan, "vm-no");
08250       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08251       return res;
08252    } else {
08253       res = ast_play_and_wait(chan, "vm-youhave");
08254    }
08255 
08256    if (vms->newmessages) {
08257       num = div(vms->newmessages, 10);
08258       if (vms->newmessages == 1) {
08259          res = ast_play_and_wait(chan, "digits/1-a");
08260          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08261          res = res ? res : ast_play_and_wait(chan, "vm-message");
08262       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08263          if (num.rem == 2) {
08264             if (!num.quot) {
08265                res = ast_play_and_wait(chan, "digits/2-ie");
08266             } else {
08267                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08268                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08269             }
08270          } else {
08271             res = say_and_wait(chan, vms->newmessages, chan->language);
08272          }
08273          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08274          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08275       } else {
08276          res = say_and_wait(chan, vms->newmessages, chan->language);
08277          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08278          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08279       }
08280       if (!res && vms->oldmessages)
08281          res = ast_play_and_wait(chan, "vm-and");
08282    }
08283    if (!res && vms->oldmessages) {
08284       num = div(vms->oldmessages, 10);
08285       if (vms->oldmessages == 1) {
08286          res = ast_play_and_wait(chan, "digits/1-a");
08287          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08288          res = res ? res : ast_play_and_wait(chan, "vm-message");
08289       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08290          if (num.rem == 2) {
08291             if (!num.quot) {
08292                res = ast_play_and_wait(chan, "digits/2-ie");
08293             } else {
08294                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08295                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08296             }
08297          } else {
08298             res = say_and_wait(chan, vms->oldmessages, chan->language);
08299          }
08300          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08301          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08302       } else {
08303          res = say_and_wait(chan, vms->oldmessages, chan->language);
08304          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08305          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08306       }
08307    }
08308 
08309    return res;
08310 }
08311 
08312 /* SWEDISH syntax */
08313 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08314 {
08315    /* Introduce messages they have */
08316    int res;
08317 
08318    res = ast_play_and_wait(chan, "vm-youhave");
08319    if (res)
08320       return res;
08321 
08322    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08323       res = ast_play_and_wait(chan, "vm-no");
08324       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08325       return res;
08326    }
08327 
08328    if (vms->newmessages) {
08329       if ((vms->newmessages == 1)) {
08330          res = ast_play_and_wait(chan, "digits/ett");
08331          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08332          res = res ? res : ast_play_and_wait(chan, "vm-message");
08333       } else {
08334          res = say_and_wait(chan, vms->newmessages, chan->language);
08335          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08336          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08337       }
08338       if (!res && vms->oldmessages)
08339          res = ast_play_and_wait(chan, "vm-and");
08340    }
08341    if (!res && vms->oldmessages) {
08342       if (vms->oldmessages == 1) {
08343          res = ast_play_and_wait(chan, "digits/ett");
08344          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08345          res = res ? res : ast_play_and_wait(chan, "vm-message");
08346       } else {
08347          res = say_and_wait(chan, vms->oldmessages, chan->language);
08348          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08349          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08350       }
08351    }
08352 
08353    return res;
08354 }
08355 
08356 /* NORWEGIAN syntax */
08357 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08358 {
08359    /* Introduce messages they have */
08360    int res;
08361 
08362    res = ast_play_and_wait(chan, "vm-youhave");
08363    if (res)
08364       return res;
08365 
08366    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08367       res = ast_play_and_wait(chan, "vm-no");
08368       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08369       return res;
08370    }
08371 
08372    if (vms->newmessages) {
08373       if ((vms->newmessages == 1)) {
08374          res = ast_play_and_wait(chan, "digits/1");
08375          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08376          res = res ? res : ast_play_and_wait(chan, "vm-message");
08377       } else {
08378          res = say_and_wait(chan, vms->newmessages, chan->language);
08379          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08380          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08381       }
08382       if (!res && vms->oldmessages)
08383          res = ast_play_and_wait(chan, "vm-and");
08384    }
08385    if (!res && vms->oldmessages) {
08386       if (vms->oldmessages == 1) {
08387          res = ast_play_and_wait(chan, "digits/1");
08388          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08389          res = res ? res : ast_play_and_wait(chan, "vm-message");
08390       } else {
08391          res = say_and_wait(chan, vms->oldmessages, chan->language);
08392          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08393          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08394       }
08395    }
08396 
08397    return res;
08398 }
08399 
08400 /* GERMAN syntax */
08401 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08402 {
08403    /* Introduce messages they have */
08404    int res;
08405    res = ast_play_and_wait(chan, "vm-youhave");
08406    if (!res) {
08407       if (vms->newmessages) {
08408          if ((vms->newmessages == 1))
08409             res = ast_play_and_wait(chan, "digits/1F");
08410          else
08411             res = say_and_wait(chan, vms->newmessages, chan->language);
08412          if (!res)
08413             res = ast_play_and_wait(chan, "vm-INBOX");
08414          if (vms->oldmessages && !res)
08415             res = ast_play_and_wait(chan, "vm-and");
08416          else if (!res) {
08417             if ((vms->newmessages == 1))
08418                res = ast_play_and_wait(chan, "vm-message");
08419             else
08420                res = ast_play_and_wait(chan, "vm-messages");
08421          }
08422             
08423       }
08424       if (!res && vms->oldmessages) {
08425          if (vms->oldmessages == 1)
08426             res = ast_play_and_wait(chan, "digits/1F");
08427          else
08428             res = say_and_wait(chan, vms->oldmessages, chan->language);
08429          if (!res)
08430             res = ast_play_and_wait(chan, "vm-Old");
08431          if (!res) {
08432             if (vms->oldmessages == 1)
08433                res = ast_play_and_wait(chan, "vm-message");
08434             else
08435                res = ast_play_and_wait(chan, "vm-messages");
08436          }
08437       }
08438       if (!res) {
08439          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08440             res = ast_play_and_wait(chan, "vm-no");
08441             if (!res)
08442                res = ast_play_and_wait(chan, "vm-messages");
08443          }
08444       }
08445    }
08446    return res;
08447 }
08448 
08449 /* SPANISH syntax */
08450 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08451 {
08452    /* Introduce messages they have */
08453    int res;
08454    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08455       res = ast_play_and_wait(chan, "vm-youhaveno");
08456       if (!res)
08457          res = ast_play_and_wait(chan, "vm-messages");
08458    } else {
08459       res = ast_play_and_wait(chan, "vm-youhave");
08460    }
08461    if (!res) {
08462       if (vms->newmessages) {
08463          if (!res) {
08464             if ((vms->newmessages == 1)) {
08465                res = ast_play_and_wait(chan, "digits/1M");
08466                if (!res)
08467                   res = ast_play_and_wait(chan, "vm-message");
08468                if (!res)
08469                   res = ast_play_and_wait(chan, "vm-INBOXs");
08470             } else {
08471                res = say_and_wait(chan, vms->newmessages, chan->language);
08472                if (!res)
08473                   res = ast_play_and_wait(chan, "vm-messages");
08474                if (!res)
08475                   res = ast_play_and_wait(chan, "vm-INBOX");
08476             }
08477          }
08478          if (vms->oldmessages && !res)
08479             res = ast_play_and_wait(chan, "vm-and");
08480       }
08481       if (vms->oldmessages) {
08482          if (!res) {
08483             if (vms->oldmessages == 1) {
08484                res = ast_play_and_wait(chan, "digits/1M");
08485                if (!res)
08486                   res = ast_play_and_wait(chan, "vm-message");
08487                if (!res)
08488                   res = ast_play_and_wait(chan, "vm-Olds");
08489             } else {
08490                res = say_and_wait(chan, vms->oldmessages, chan->language);
08491                if (!res)
08492                   res = ast_play_and_wait(chan, "vm-messages");
08493                if (!res)
08494                   res = ast_play_and_wait(chan, "vm-Old");
08495             }
08496          }
08497       }
08498    }
08499 return res;
08500 }
08501 
08502 /* BRAZILIAN PORTUGUESE syntax */
08503 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08504    /* Introduce messages they have */
08505    int res;
08506    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08507       res = ast_play_and_wait(chan, "vm-nomessages");
08508       return res;
08509    } else {
08510       res = ast_play_and_wait(chan, "vm-youhave");
08511    }
08512    if (vms->newmessages) {
08513       if (!res)
08514          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08515       if ((vms->newmessages == 1)) {
08516          if (!res)
08517             res = ast_play_and_wait(chan, "vm-message");
08518          if (!res)
08519             res = ast_play_and_wait(chan, "vm-INBOXs");
08520       } else {
08521          if (!res)
08522             res = ast_play_and_wait(chan, "vm-messages");
08523          if (!res)
08524             res = ast_play_and_wait(chan, "vm-INBOX");
08525       }
08526       if (vms->oldmessages && !res)
08527          res = ast_play_and_wait(chan, "vm-and");
08528    }
08529    if (vms->oldmessages) {
08530       if (!res)
08531          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08532       if (vms->oldmessages == 1) {
08533          if (!res)
08534             res = ast_play_and_wait(chan, "vm-message");
08535          if (!res)
08536             res = ast_play_and_wait(chan, "vm-Olds");
08537       } else {
08538          if (!res)
08539             res = ast_play_and_wait(chan, "vm-messages");
08540          if (!res)
08541             res = ast_play_and_wait(chan, "vm-Old");
08542       }
08543    }
08544    return res;
08545 }
08546 
08547 /* FRENCH syntax */
08548 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08549 {
08550    /* Introduce messages they have */
08551    int res;
08552    res = ast_play_and_wait(chan, "vm-youhave");
08553    if (!res) {
08554       if (vms->newmessages) {
08555          res = say_and_wait(chan, vms->newmessages, chan->language);
08556          if (!res)
08557             res = ast_play_and_wait(chan, "vm-INBOX");
08558          if (vms->oldmessages && !res)
08559             res = ast_play_and_wait(chan, "vm-and");
08560          else if (!res) {
08561             if ((vms->newmessages == 1))
08562                res = ast_play_and_wait(chan, "vm-message");
08563             else
08564                res = ast_play_and_wait(chan, "vm-messages");
08565          }
08566             
08567       }
08568       if (!res && vms->oldmessages) {
08569          res = say_and_wait(chan, vms->oldmessages, chan->language);
08570          if (!res)
08571             res = ast_play_and_wait(chan, "vm-Old");
08572          if (!res) {
08573             if (vms->oldmessages == 1)
08574                res = ast_play_and_wait(chan, "vm-message");
08575             else
08576                res = ast_play_and_wait(chan, "vm-messages");
08577          }
08578       }
08579       if (!res) {
08580          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08581             res = ast_play_and_wait(chan, "vm-no");
08582             if (!res)
08583                res = ast_play_and_wait(chan, "vm-messages");
08584          }
08585       }
08586    }
08587    return res;
08588 }
08589 
08590 /* DUTCH syntax */
08591 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08592 {
08593    /* Introduce messages they have */
08594    int res;
08595    res = ast_play_and_wait(chan, "vm-youhave");
08596    if (!res) {
08597       if (vms->newmessages) {
08598          res = say_and_wait(chan, vms->newmessages, chan->language);
08599          if (!res) {
08600             if (vms->newmessages == 1)
08601                res = ast_play_and_wait(chan, "vm-INBOXs");
08602             else
08603                res = ast_play_and_wait(chan, "vm-INBOX");
08604          }
08605          if (vms->oldmessages && !res)
08606             res = ast_play_and_wait(chan, "vm-and");
08607          else if (!res) {
08608             if ((vms->newmessages == 1))
08609                res = ast_play_and_wait(chan, "vm-message");
08610             else
08611                res = ast_play_and_wait(chan, "vm-messages");
08612          }
08613             
08614       }
08615       if (!res && vms->oldmessages) {
08616          res = say_and_wait(chan, vms->oldmessages, chan->language);
08617          if (!res) {
08618             if (vms->oldmessages == 1)
08619                res = ast_play_and_wait(chan, "vm-Olds");
08620             else
08621                res = ast_play_and_wait(chan, "vm-Old");
08622          }
08623          if (!res) {
08624             if (vms->oldmessages == 1)
08625                res = ast_play_and_wait(chan, "vm-message");
08626             else
08627                res = ast_play_and_wait(chan, "vm-messages");
08628          }
08629       }
08630       if (!res) {
08631          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08632             res = ast_play_and_wait(chan, "vm-no");
08633             if (!res)
08634                res = ast_play_and_wait(chan, "vm-messages");
08635          }
08636       }
08637    }
08638    return res;
08639 }
08640 
08641 /* PORTUGUESE syntax */
08642 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08643 {
08644    /* Introduce messages they have */
08645    int res;
08646    res = ast_play_and_wait(chan, "vm-youhave");
08647    if (!res) {
08648       if (vms->newmessages) {
08649          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08650          if (!res) {
08651             if ((vms->newmessages == 1)) {
08652                res = ast_play_and_wait(chan, "vm-message");
08653                if (!res)
08654                   res = ast_play_and_wait(chan, "vm-INBOXs");
08655             } else {
08656                res = ast_play_and_wait(chan, "vm-messages");
08657                if (!res)
08658                   res = ast_play_and_wait(chan, "vm-INBOX");
08659             }
08660          }
08661          if (vms->oldmessages && !res)
08662             res = ast_play_and_wait(chan, "vm-and");
08663       }
08664       if (!res && vms->oldmessages) {
08665          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08666          if (!res) {
08667             if (vms->oldmessages == 1) {
08668                res = ast_play_and_wait(chan, "vm-message");
08669                if (!res)
08670                   res = ast_play_and_wait(chan, "vm-Olds");
08671             } else {
08672                res = ast_play_and_wait(chan, "vm-messages");
08673                if (!res)
08674                   res = ast_play_and_wait(chan, "vm-Old");
08675             }
08676          }
08677       }
08678       if (!res) {
08679          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08680             res = ast_play_and_wait(chan, "vm-no");
08681             if (!res)
08682                res = ast_play_and_wait(chan, "vm-messages");
08683          }
08684       }
08685    }
08686    return res;
08687 }
08688 
08689 
08690 /* CZECH syntax */
08691 /* in czech there must be declension of word new and message
08692  * czech        : english        : czech      : english
08693  * --------------------------------------------------------
08694  * vm-youhave   : you have 
08695  * vm-novou     : one new        : vm-zpravu  : message
08696  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08697  * vm-novych    : 5-infinite new : vm-zprav   : messages
08698  * vm-starou   : one old
08699  * vm-stare     : 2-4 old 
08700  * vm-starych   : 5-infinite old
08701  * jednu        : one   - falling 4. 
08702  * vm-no        : no  ( no messages )
08703  */
08704 
08705 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08706 {
08707    int res;
08708    res = ast_play_and_wait(chan, "vm-youhave");
08709    if (!res) {
08710       if (vms->newmessages) {
08711          if (vms->newmessages == 1) {
08712             res = ast_play_and_wait(chan, "digits/jednu");
08713          } else {
08714             res = say_and_wait(chan, vms->newmessages, chan->language);
08715          }
08716          if (!res) {
08717             if ((vms->newmessages == 1))
08718                res = ast_play_and_wait(chan, "vm-novou");
08719             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08720                res = ast_play_and_wait(chan, "vm-nove");
08721             if (vms->newmessages > 4)
08722                res = ast_play_and_wait(chan, "vm-novych");
08723          }
08724          if (vms->oldmessages && !res)
08725             res = ast_play_and_wait(chan, "vm-and");
08726          else if (!res) {
08727             if ((vms->newmessages == 1))
08728                res = ast_play_and_wait(chan, "vm-zpravu");
08729             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08730                res = ast_play_and_wait(chan, "vm-zpravy");
08731             if (vms->newmessages > 4)
08732                res = ast_play_and_wait(chan, "vm-zprav");
08733          }
08734       }
08735       if (!res && vms->oldmessages) {
08736          res = say_and_wait(chan, vms->oldmessages, chan->language);
08737          if (!res) {
08738             if ((vms->oldmessages == 1))
08739                res = ast_play_and_wait(chan, "vm-starou");
08740             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08741                res = ast_play_and_wait(chan, "vm-stare");
08742             if (vms->oldmessages > 4)
08743                res = ast_play_and_wait(chan, "vm-starych");
08744          }
08745          if (!res) {
08746             if ((vms->oldmessages == 1))
08747                res = ast_play_and_wait(chan, "vm-zpravu");
08748             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08749                res = ast_play_and_wait(chan, "vm-zpravy");
08750             if (vms->oldmessages > 4)
08751                res = ast_play_and_wait(chan, "vm-zprav");
08752          }
08753       }
08754       if (!res) {
08755          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08756             res = ast_play_and_wait(chan, "vm-no");
08757             if (!res)
08758                res = ast_play_and_wait(chan, "vm-zpravy");
08759          }
08760       }
08761    }
08762    return res;
08763 }
08764 
08765 /* CHINESE (Taiwan) syntax */
08766 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08767 {
08768    int res;
08769    /* Introduce messages they have */
08770    res = ast_play_and_wait(chan, "vm-you");
08771 
08772    if (!res && vms->newmessages) {
08773       res = ast_play_and_wait(chan, "vm-have");
08774       if (!res)
08775          res = say_and_wait(chan, vms->newmessages, chan->language);
08776       if (!res)
08777          res = ast_play_and_wait(chan, "vm-tong");
08778       if (!res)
08779          res = ast_play_and_wait(chan, "vm-INBOX");
08780       if (vms->oldmessages && !res)
08781          res = ast_play_and_wait(chan, "vm-and");
08782       else if (!res) 
08783          res = ast_play_and_wait(chan, "vm-messages");
08784    }
08785    if (!res && vms->oldmessages) {
08786       res = ast_play_and_wait(chan, "vm-have");
08787       if (!res)
08788          res = say_and_wait(chan, vms->oldmessages, chan->language);
08789       if (!res)
08790          res = ast_play_and_wait(chan, "vm-tong");
08791       if (!res)
08792          res = ast_play_and_wait(chan, "vm-Old");
08793       if (!res)
08794          res = ast_play_and_wait(chan, "vm-messages");
08795    }
08796    if (!res && !vms->oldmessages && !vms->newmessages) {
08797       res = ast_play_and_wait(chan, "vm-haveno");
08798       if (!res)
08799          res = ast_play_and_wait(chan, "vm-messages");
08800    }
08801    return res;
08802 }
08803 
08804 /* Vietnamese syntax */
08805 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08806 {
08807    int res;
08808 
08809    /* Introduce messages they have */
08810    res = ast_play_and_wait(chan, "vm-youhave");
08811    if (!res) {
08812       if (vms->newmessages) {
08813          res = say_and_wait(chan, vms->newmessages, chan->language);
08814          if (!res)
08815             res = ast_play_and_wait(chan, "vm-INBOX");
08816          if (vms->oldmessages && !res)
08817             res = ast_play_and_wait(chan, "vm-and");
08818       }
08819       if (!res && vms->oldmessages) {
08820          res = say_and_wait(chan, vms->oldmessages, chan->language);
08821          if (!res)
08822             res = ast_play_and_wait(chan, "vm-Old");        
08823       }
08824       if (!res) {
08825          if (!vms->oldmessages && !vms->newmessages) {
08826             res = ast_play_and_wait(chan, "vm-no");
08827             if (!res)
08828                res = ast_play_and_wait(chan, "vm-message");
08829          }
08830       }
08831    }
08832    return res;
08833 }
08834 
08835 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08836 {
08837    char prefile[256];
08838    
08839    /* Notify the user that the temp greeting is set and give them the option to remove it */
08840    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08841    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08842       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08843       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08844          ast_play_and_wait(chan, "vm-tempgreetactive");
08845       }
08846       DISPOSE(prefile, -1);
08847    }
08848 
08849    /* Play voicemail intro - syntax is different for different languages */
08850    if (0) {
08851       return 0;
08852    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08853       return vm_intro_cs(chan, vms);
08854    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08855       static int deprecation_warning = 0;
08856       if (deprecation_warning++ % 10 == 0) {
08857          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08858       }
08859       return vm_intro_cs(chan, vms);
08860    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08861       return vm_intro_de(chan, vms);
08862    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08863       return vm_intro_es(chan, vms);
08864    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08865       return vm_intro_fr(chan, vms);
08866    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08867       return vm_intro_gr(chan, vms);
08868    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08869       return vm_intro_he(chan, vms);
08870    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08871       return vm_intro_it(chan, vms);
08872    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08873       return vm_intro_nl(chan, vms);
08874    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08875       return vm_intro_no(chan, vms);
08876    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08877       return vm_intro_pl(chan, vms);
08878    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08879       return vm_intro_pt_BR(chan, vms);
08880    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08881       return vm_intro_pt(chan, vms);
08882    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08883       return vm_intro_multilang(chan, vms, "n");
08884    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08885       return vm_intro_se(chan, vms);
08886    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08887       return vm_intro_multilang(chan, vms, "n");
08888    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
08889       return vm_intro_vi(chan, vms);
08890    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08891       return vm_intro_zh(chan, vms);
08892    } else {                                             /* Default to ENGLISH */
08893       return vm_intro_en(chan, vms);
08894    }
08895 }
08896 
08897 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08898 {
08899    int res = 0;
08900    /* Play instructions and wait for new command */
08901    while (!res) {
08902       if (vms->starting) {
08903          if (vms->lastmsg > -1) {
08904             if (skipadvanced)
08905                res = ast_play_and_wait(chan, "vm-onefor-full");
08906             else
08907                res = ast_play_and_wait(chan, "vm-onefor");
08908             if (!res)
08909                res = vm_play_folder_name(chan, vms->vmbox);
08910          }
08911          if (!res) {
08912             if (skipadvanced)
08913                res = ast_play_and_wait(chan, "vm-opts-full");
08914             else
08915                res = ast_play_and_wait(chan, "vm-opts");
08916          }
08917       } else {
08918          /* Added for additional help */
08919          if (skipadvanced) {
08920             res = ast_play_and_wait(chan, "vm-onefor-full");
08921             if (!res)
08922                res = vm_play_folder_name(chan, vms->vmbox);
08923             res = ast_play_and_wait(chan, "vm-opts-full");
08924          }
08925          /* Logic:
08926           * If the current message is not the first OR
08927           * if we're listening to the first new message and there are
08928           * also urgent messages, then prompt for navigation to the
08929           * previous message
08930           */
08931          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08932             res = ast_play_and_wait(chan, "vm-prev");
08933          }
08934          if (!res && !skipadvanced)
08935             res = ast_play_and_wait(chan, "vm-advopts");
08936          if (!res)
08937             res = ast_play_and_wait(chan, "vm-repeat");
08938          /* Logic:
08939           * If we're not listening to the last message OR
08940           * we're listening to the last urgent message and there are
08941           * also new non-urgent messages, then prompt for navigation
08942           * to the next message
08943           */
08944          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08945             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08946             res = ast_play_and_wait(chan, "vm-next");
08947          }
08948          if (!res) {
08949             if (!vms->deleted[vms->curmsg])
08950                res = ast_play_and_wait(chan, "vm-delete");
08951             else
08952                res = ast_play_and_wait(chan, "vm-undelete");
08953             if (!res)
08954                res = ast_play_and_wait(chan, "vm-toforward");
08955             if (!res)
08956                res = ast_play_and_wait(chan, "vm-savemessage");
08957          }
08958       }
08959       if (!res) {
08960          res = ast_play_and_wait(chan, "vm-helpexit");
08961       }
08962       if (!res)
08963          res = ast_waitfordigit(chan, 6000);
08964       if (!res) {
08965          vms->repeats++;
08966          if (vms->repeats > 2) {
08967             res = 't';
08968          }
08969       }
08970    }
08971    return res;
08972 }
08973 
08974 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08975 {
08976    int res = 0;
08977    /* Play instructions and wait for new command */
08978    while (!res) {
08979       if (vms->lastmsg > -1) {
08980          res = ast_play_and_wait(chan, "vm-listen");
08981          if (!res)
08982             res = vm_play_folder_name(chan, vms->vmbox);
08983          if (!res)
08984             res = ast_play_and_wait(chan, "press");
08985          if (!res)
08986             res = ast_play_and_wait(chan, "digits/1");
08987       }
08988       if (!res)
08989          res = ast_play_and_wait(chan, "vm-opts");
08990       if (!res) {
08991          vms->starting = 0;
08992          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08993       }
08994    }
08995    return res;
08996 }
08997 
08998 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08999 {
09000    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09001       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09002    } else {             /* Default to ENGLISH */
09003       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09004    }
09005 }
09006 
09007 
09008 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09009 {
09010    int cmd = 0;
09011    int duration = 0;
09012    int tries = 0;
09013    char newpassword[80] = "";
09014    char newpassword2[80] = "";
09015    char prefile[PATH_MAX] = "";
09016    unsigned char buf[256];
09017    int bytes = 0;
09018 
09019    if (ast_adsi_available(chan)) {
09020       bytes += adsi_logo(buf + bytes);
09021       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09022       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09023       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09024       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09025       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09026    }
09027 
09028    /* First, have the user change their password 
09029       so they won't get here again */
09030    for (;;) {
09031       newpassword[1] = '\0';
09032       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09033       if (cmd == '#')
09034          newpassword[0] = '\0';
09035       if (cmd < 0 || cmd == 't' || cmd == '#')
09036          return cmd;
09037       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09038       if (cmd < 0 || cmd == 't' || cmd == '#')
09039          return cmd;
09040       cmd = check_password(vmu, newpassword); /* perform password validation */
09041       if (cmd != 0) {
09042          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09043          cmd = ast_play_and_wait(chan, vm_invalid_password);
09044       } else {
09045          newpassword2[1] = '\0';
09046          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09047          if (cmd == '#')
09048             newpassword2[0] = '\0';
09049          if (cmd < 0 || cmd == 't' || cmd == '#')
09050             return cmd;
09051          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09052          if (cmd < 0 || cmd == 't' || cmd == '#')
09053             return cmd;
09054          if (!strcmp(newpassword, newpassword2))
09055             break;
09056          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09057          cmd = ast_play_and_wait(chan, vm_mismatch);
09058       }
09059       if (++tries == 3)
09060          return -1;
09061       if (cmd != 0) {
09062          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09063       }
09064    }
09065    if (pwdchange & PWDCHANGE_INTERNAL)
09066       vm_change_password(vmu, newpassword);
09067    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09068       vm_change_password_shell(vmu, newpassword);
09069 
09070    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09071    cmd = ast_play_and_wait(chan, vm_passchanged);
09072 
09073    /* If forcename is set, have the user record their name */  
09074    if (ast_test_flag(vmu, VM_FORCENAME)) {
09075       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09076       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09077          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09078          if (cmd < 0 || cmd == 't' || cmd == '#')
09079             return cmd;
09080       }
09081    }
09082 
09083    /* If forcegreetings is set, have the user record their greetings */
09084    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09085       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09086       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09087          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09088          if (cmd < 0 || cmd == 't' || cmd == '#')
09089             return cmd;
09090       }
09091 
09092       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09093       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09094          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09095          if (cmd < 0 || cmd == 't' || cmd == '#')
09096             return cmd;
09097       }
09098    }
09099 
09100    return cmd;
09101 }
09102 
09103 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09104 {
09105    int cmd = 0;
09106    int retries = 0;
09107    int duration = 0;
09108    char newpassword[80] = "";
09109    char newpassword2[80] = "";
09110    char prefile[PATH_MAX] = "";
09111    unsigned char buf[256];
09112    int bytes = 0;
09113 
09114    if (ast_adsi_available(chan)) {
09115       bytes += adsi_logo(buf + bytes);
09116       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09117       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09118       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09119       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09120       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09121    }
09122    while ((cmd >= 0) && (cmd != 't')) {
09123       if (cmd)
09124          retries = 0;
09125       switch (cmd) {
09126       case '1': /* Record your unavailable message */
09127          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09128          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09129          break;
09130       case '2':  /* Record your busy message */
09131          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09132          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09133          break;
09134       case '3': /* Record greeting */
09135          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09136          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09137          break;
09138       case '4':  /* manage the temporary greeting */
09139          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09140          break;
09141       case '5': /* change password */
09142          if (vmu->password[0] == '-') {
09143             cmd = ast_play_and_wait(chan, "vm-no");
09144             break;
09145          }
09146          newpassword[1] = '\0';
09147          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09148          if (cmd == '#')
09149             newpassword[0] = '\0';
09150          else {
09151             if (cmd < 0)
09152                break;
09153             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09154                break;
09155             }
09156          }
09157          cmd = check_password(vmu, newpassword); /* perform password validation */
09158          if (cmd != 0) {
09159             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09160             cmd = ast_play_and_wait(chan, vm_invalid_password);
09161             if (!cmd) {
09162                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09163             }
09164             break;
09165          }
09166          newpassword2[1] = '\0';
09167          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09168          if (cmd == '#')
09169             newpassword2[0] = '\0';
09170          else {
09171             if (cmd < 0)
09172                break;
09173 
09174             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09175                break;
09176             }
09177          }
09178          if (strcmp(newpassword, newpassword2)) {
09179             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09180             cmd = ast_play_and_wait(chan, vm_mismatch);
09181             if (!cmd) {
09182                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09183             }
09184             break;
09185          }
09186          if (pwdchange & PWDCHANGE_INTERNAL)
09187             vm_change_password(vmu, newpassword);
09188          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09189             vm_change_password_shell(vmu, newpassword);
09190 
09191          ast_debug(1, "User %s set password to %s of length %d\n",
09192             vms->username, newpassword, (int) strlen(newpassword));
09193          cmd = ast_play_and_wait(chan, vm_passchanged);
09194          break;
09195       case '*': 
09196          cmd = 't';
09197          break;
09198       default: 
09199          cmd = 0;
09200          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09201          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09202          if (ast_fileexists(prefile, NULL, NULL)) {
09203             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09204          }
09205          DISPOSE(prefile, -1);
09206          if (!cmd) {
09207             cmd = ast_play_and_wait(chan, "vm-options");
09208          }
09209          if (!cmd) {
09210             cmd = ast_waitfordigit(chan, 6000);
09211          }
09212          if (!cmd) {
09213             retries++;
09214          }
09215          if (retries > 3) {
09216             cmd = 't';
09217          }
09218       }
09219    }
09220    if (cmd == 't')
09221       cmd = 0;
09222    return cmd;
09223 }
09224 
09225 /*!
09226  * \brief The handler for 'record a temporary greeting'. 
09227  * \param chan
09228  * \param vmu
09229  * \param vms
09230  * \param fmtc
09231  * \param record_gain
09232  *
09233  * This is option 4 from the mailbox options menu.
09234  * This function manages the following promptings:
09235  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09236  * 2: remove (delete) the temporary greeting.
09237  * *: return to the main menu.
09238  *
09239  * \return zero on success, -1 on error.
09240  */
09241 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09242 {
09243    int cmd = 0;
09244    int retries = 0;
09245    int duration = 0;
09246    char prefile[PATH_MAX] = "";
09247    unsigned char buf[256];
09248    int bytes = 0;
09249 
09250    if (ast_adsi_available(chan)) {
09251       bytes += adsi_logo(buf + bytes);
09252       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09253       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09254       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09255       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09256       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09257    }
09258 
09259    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09260    while ((cmd >= 0) && (cmd != 't')) {
09261       if (cmd)
09262          retries = 0;
09263       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09264       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09265          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09266          cmd = 't';  
09267       } else {
09268          switch (cmd) {
09269          case '1':
09270             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09271             break;
09272          case '2':
09273             DELETE(prefile, -1, prefile, vmu);
09274             ast_play_and_wait(chan, "vm-tempremoved");
09275             cmd = 't';  
09276             break;
09277          case '*': 
09278             cmd = 't';
09279             break;
09280          default:
09281             cmd = ast_play_and_wait(chan,
09282                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09283                   "vm-tempgreeting2" : "vm-tempgreeting");
09284             if (!cmd)
09285                cmd = ast_waitfordigit(chan, 6000);
09286             if (!cmd)
09287                retries++;
09288             if (retries > 3)
09289                cmd = 't';
09290          }
09291       }
09292       DISPOSE(prefile, -1);
09293    }
09294    if (cmd == 't')
09295       cmd = 0;
09296    return cmd;
09297 }
09298 
09299 /*!
09300  * \brief Greek syntax for 'You have N messages' greeting.
09301  * \param chan
09302  * \param vms
09303  * \param vmu
09304  *
09305  * \return zero on success, -1 on error.
09306  */   
09307 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09308 {
09309    int cmd = 0;
09310 
09311    if (vms->lastmsg > -1) {
09312       cmd = play_message(chan, vmu, vms);
09313    } else {
09314       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09315       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09316          if (!cmd) {
09317             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09318             cmd = ast_play_and_wait(chan, vms->fn);
09319          }
09320          if (!cmd)
09321             cmd = ast_play_and_wait(chan, "vm-messages");
09322       } else {
09323          if (!cmd)
09324             cmd = ast_play_and_wait(chan, "vm-messages");
09325          if (!cmd) {
09326             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09327             cmd = ast_play_and_wait(chan, vms->fn);
09328          }
09329       }
09330    } 
09331    return cmd;
09332 }
09333 
09334 /* Hebrew Syntax */
09335 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09336 {
09337    int cmd = 0;
09338 
09339    if (vms->lastmsg > -1) {
09340       cmd = play_message(chan, vmu, vms);
09341    } else {
09342       if (!strcasecmp(vms->fn, "INBOX")) {
09343          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09344       } else {
09345          cmd = ast_play_and_wait(chan, "vm-nomessages");
09346       }
09347    }
09348    return cmd;
09349 }
09350 
09351 /*! 
09352  * \brief Default English syntax for 'You have N messages' greeting.
09353  * \param chan
09354  * \param vms
09355  * \param vmu
09356  *
09357  * \return zero on success, -1 on error.
09358  */
09359 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09360 {
09361    int cmd = 0;
09362 
09363    if (vms->lastmsg > -1) {
09364       cmd = play_message(chan, vmu, vms);
09365    } else {
09366       cmd = ast_play_and_wait(chan, "vm-youhave");
09367       if (!cmd) 
09368          cmd = ast_play_and_wait(chan, "vm-no");
09369       if (!cmd) {
09370          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09371          cmd = ast_play_and_wait(chan, vms->fn);
09372       }
09373       if (!cmd)
09374          cmd = ast_play_and_wait(chan, "vm-messages");
09375    }
09376    return cmd;
09377 }
09378 
09379 /*! 
09380  *\brief Italian syntax for 'You have N messages' greeting.
09381  * \param chan
09382  * \param vms
09383  * \param vmu
09384  *
09385  * \return zero on success, -1 on error.
09386  */
09387 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09388 {
09389    int cmd;
09390 
09391    if (vms->lastmsg > -1) {
09392       cmd = play_message(chan, vmu, vms);
09393    } else {
09394       cmd = ast_play_and_wait(chan, "vm-no");
09395       if (!cmd)
09396          cmd = ast_play_and_wait(chan, "vm-message");
09397       if (!cmd) {
09398          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09399          cmd = ast_play_and_wait(chan, vms->fn);
09400       }
09401    }
09402    return cmd;
09403 }
09404 
09405 /*! 
09406  * \brief Spanish syntax for 'You have N messages' greeting.
09407  * \param chan
09408  * \param vms
09409  * \param vmu
09410  *
09411  * \return zero on success, -1 on error.
09412  */
09413 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09414 {
09415    int cmd;
09416 
09417    if (vms->lastmsg > -1) {
09418       cmd = play_message(chan, vmu, vms);
09419    } else {
09420       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09421       if (!cmd)
09422          cmd = ast_play_and_wait(chan, "vm-messages");
09423       if (!cmd) {
09424          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09425          cmd = ast_play_and_wait(chan, vms->fn);
09426       }
09427    }
09428    return cmd;
09429 }
09430 
09431 /*! 
09432  * \brief Portuguese syntax for 'You have N messages' greeting.
09433  * \param chan
09434  * \param vms
09435  * \param vmu
09436  *
09437  * \return zero on success, -1 on error.
09438  */
09439 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09440 {
09441    int cmd;
09442 
09443    if (vms->lastmsg > -1) {
09444       cmd = play_message(chan, vmu, vms);
09445    } else {
09446       cmd = ast_play_and_wait(chan, "vm-no");
09447       if (!cmd) {
09448          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09449          cmd = ast_play_and_wait(chan, vms->fn);
09450       }
09451       if (!cmd)
09452          cmd = ast_play_and_wait(chan, "vm-messages");
09453    }
09454    return cmd;
09455 }
09456 
09457 /*! 
09458  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09459  * \param chan
09460  * \param vms
09461  * \param vmu
09462  *
09463  * \return zero on success, -1 on error.
09464  */
09465 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09466 {
09467    int cmd;
09468 
09469    if (vms->lastmsg > -1) {
09470       cmd = play_message(chan, vmu, vms);
09471    } else {
09472       cmd = ast_play_and_wait(chan, "vm-you");
09473       if (!cmd) 
09474          cmd = ast_play_and_wait(chan, "vm-haveno");
09475       if (!cmd)
09476          cmd = ast_play_and_wait(chan, "vm-messages");
09477       if (!cmd) {
09478          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09479          cmd = ast_play_and_wait(chan, vms->fn);
09480       }
09481    }
09482    return cmd;
09483 }
09484 
09485 /*! 
09486  * \brief Vietnamese syntax for 'You have N messages' greeting.
09487  * \param chan
09488  * \param vms
09489  * \param vmu
09490  *
09491  * \return zero on success, -1 on error.
09492  */
09493 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09494 {
09495    int cmd = 0;
09496 
09497    if (vms->lastmsg > -1) {
09498       cmd = play_message(chan, vmu, vms);
09499    } else {
09500       cmd = ast_play_and_wait(chan, "vm-no");
09501       if (!cmd) {
09502          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09503          cmd = ast_play_and_wait(chan, vms->fn);
09504       }
09505    }
09506    return cmd;
09507 }
09508 
09509 /*!
09510  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09511  * \param chan The channel for the current user. We read the language property from this.
09512  * \param vms passed into the language-specific vm_browse_messages function.
09513  * \param vmu passed into the language-specific vm_browse_messages function.
09514  * 
09515  * The method to be invoked is determined by the value of language code property in the user's channel.
09516  * The default (when unable to match) is to use english.
09517  *
09518  * \return zero on success, -1 on error.
09519  */
09520 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09521 {
09522    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09523       return vm_browse_messages_es(chan, vms, vmu);
09524    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09525       return vm_browse_messages_gr(chan, vms, vmu);
09526    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09527       return vm_browse_messages_he(chan, vms, vmu);
09528    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09529       return vm_browse_messages_it(chan, vms, vmu);
09530    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09531       return vm_browse_messages_pt(chan, vms, vmu);
09532    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09533       return vm_browse_messages_vi(chan, vms, vmu);
09534    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09535       return vm_browse_messages_zh(chan, vms, vmu);
09536    } else {                                             /* Default to English syntax */
09537       return vm_browse_messages_en(chan, vms, vmu);
09538    }
09539 }
09540 
09541 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09542          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09543          int skipuser, int max_logins, int silent)
09544 {
09545    int useadsi = 0, valid = 0, logretries = 0;
09546    char password[AST_MAX_EXTENSION]="", *passptr;
09547    struct ast_vm_user vmus, *vmu = NULL;
09548 
09549    /* If ADSI is supported, setup login screen */
09550    adsi_begin(chan, &useadsi);
09551    if (!skipuser && useadsi)
09552       adsi_login(chan);
09553    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09554       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09555       return -1;
09556    }
09557    
09558    /* Authenticate them and get their mailbox/password */
09559    
09560    while (!valid && (logretries < max_logins)) {
09561       /* Prompt for, and read in the username */
09562       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09563          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09564          return -1;
09565       }
09566       if (ast_strlen_zero(mailbox)) {
09567          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09568             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09569          } else {
09570             ast_verb(3, "Username not entered\n"); 
09571             return -1;
09572          }
09573       } else if (mailbox[0] == '*') {
09574          /* user entered '*' */
09575          if (ast_exists_extension(chan, chan->context, "a", 1,
09576             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09577             return -1;
09578          }
09579          mailbox[0] = '\0';
09580       }
09581 
09582       if (useadsi)
09583          adsi_password(chan);
09584 
09585       if (!ast_strlen_zero(prefix)) {
09586          char fullusername[80] = "";
09587          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09588          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09589          ast_copy_string(mailbox, fullusername, mailbox_size);
09590       }
09591 
09592       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09593       vmu = find_user(&vmus, context, mailbox);
09594       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09595          /* saved password is blank, so don't bother asking */
09596          password[0] = '\0';
09597       } else {
09598          if (ast_streamfile(chan, vm_password, chan->language)) {
09599             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09600             return -1;
09601          }
09602          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09603             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09604             return -1;
09605          } else if (password[0] == '*') {
09606             /* user entered '*' */
09607             if (ast_exists_extension(chan, chan->context, "a", 1,
09608                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09609                mailbox[0] = '*';
09610                return -1;
09611             }
09612             mailbox[0] = '\0';
09613          }
09614       }
09615 
09616       if (vmu) {
09617          passptr = vmu->password;
09618          if (passptr[0] == '-') passptr++;
09619       }
09620       if (vmu && !strcmp(passptr, password))
09621          valid++;
09622       else {
09623          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09624          if (!ast_strlen_zero(prefix))
09625             mailbox[0] = '\0';
09626       }
09627       logretries++;
09628       if (!valid) {
09629          if (skipuser || logretries >= max_logins) {
09630             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09631                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09632                return -1;
09633             }
09634          } else {
09635             if (useadsi)
09636                adsi_login(chan);
09637             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09638                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09639                return -1;
09640             }
09641          }
09642          if (ast_waitstream(chan, "")) /* Channel is hung up */
09643             return -1;
09644       }
09645    }
09646    if (!valid && (logretries >= max_logins)) {
09647       ast_stopstream(chan);
09648       ast_play_and_wait(chan, "vm-goodbye");
09649       return -1;
09650    }
09651    if (vmu && !skipuser) {
09652       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09653    }
09654    return 0;
09655 }
09656 
09657 static int vm_execmain(struct ast_channel *chan, const char *data)
09658 {
09659    /* XXX This is, admittedly, some pretty horrendous code.  For some
09660       reason it just seemed a lot easier to do with GOTO's.  I feel
09661       like I'm back in my GWBASIC days. XXX */
09662    int res = -1;
09663    int cmd = 0;
09664    int valid = 0;
09665    char prefixstr[80] ="";
09666    char ext_context[256]="";
09667    int box;
09668    int useadsi = 0;
09669    int skipuser = 0;
09670    struct vm_state vms;
09671    struct ast_vm_user *vmu = NULL, vmus;
09672    char *context = NULL;
09673    int silentexit = 0;
09674    struct ast_flags flags = { 0 };
09675    signed char record_gain = 0;
09676    int play_auto = 0;
09677    int play_folder = 0;
09678    int in_urgent = 0;
09679 #ifdef IMAP_STORAGE
09680    int deleted = 0;
09681 #endif
09682 
09683    /* Add the vm_state to the active list and keep it active */
09684    memset(&vms, 0, sizeof(vms));
09685 
09686    vms.lastmsg = -1;
09687 
09688    memset(&vmus, 0, sizeof(vmus));
09689 
09690    if (chan->_state != AST_STATE_UP) {
09691       ast_debug(1, "Before ast_answer\n");
09692       ast_answer(chan);
09693    }
09694 
09695    if (!ast_strlen_zero(data)) {
09696       char *opts[OPT_ARG_ARRAY_SIZE];
09697       char *parse;
09698       AST_DECLARE_APP_ARGS(args,
09699          AST_APP_ARG(argv0);
09700          AST_APP_ARG(argv1);
09701       );
09702 
09703       parse = ast_strdupa(data);
09704 
09705       AST_STANDARD_APP_ARGS(args, parse);
09706 
09707       if (args.argc == 2) {
09708          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09709             return -1;
09710          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09711             int gain;
09712             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09713                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09714                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09715                   return -1;
09716                } else {
09717                   record_gain = (signed char) gain;
09718                }
09719             } else {
09720                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09721             }
09722          }
09723          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09724             play_auto = 1;
09725             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09726                /* See if it is a folder name first */
09727                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09728                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09729                      play_folder = -1;
09730                   }
09731                } else {
09732                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09733                }
09734             } else {
09735                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09736             }
09737             if (play_folder > 9 || play_folder < 0) {
09738                ast_log(AST_LOG_WARNING,
09739                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09740                   opts[OPT_ARG_PLAYFOLDER]);
09741                play_folder = 0;
09742             }
09743          }
09744       } else {
09745          /* old style options parsing */
09746          while (*(args.argv0)) {
09747             if (*(args.argv0) == 's')
09748                ast_set_flag(&flags, OPT_SILENT);
09749             else if (*(args.argv0) == 'p')
09750                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09751             else 
09752                break;
09753             (args.argv0)++;
09754          }
09755 
09756       }
09757 
09758       valid = ast_test_flag(&flags, OPT_SILENT);
09759 
09760       if ((context = strchr(args.argv0, '@')))
09761          *context++ = '\0';
09762 
09763       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09764          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09765       else
09766          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09767 
09768       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09769          skipuser++;
09770       else
09771          valid = 0;
09772    }
09773 
09774    if (!valid)
09775       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09776 
09777    ast_debug(1, "After vm_authenticate\n");
09778 
09779    if (vms.username[0] == '*') {
09780       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09781 
09782       /* user entered '*' */
09783       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09784          res = 0; /* prevent hangup */
09785          goto out;
09786       }
09787    }
09788 
09789    if (!res) {
09790       valid = 1;
09791       if (!skipuser)
09792          vmu = &vmus;
09793    } else {
09794       res = 0;
09795    }
09796 
09797    /* If ADSI is supported, setup login screen */
09798    adsi_begin(chan, &useadsi);
09799 
09800    if (!valid) {
09801       goto out;
09802    }
09803 
09804 #ifdef IMAP_STORAGE
09805    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09806    pthread_setspecific(ts_vmstate.key, &vms);
09807 
09808    vms.interactive = 1;
09809    vms.updated = 1;
09810    if (vmu)
09811       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09812    vmstate_insert(&vms);
09813    init_vm_state(&vms);
09814 #endif
09815    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09816    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09817       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09818       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09819       return -1;
09820    }
09821    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09822       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09823       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09824       return -1;
09825    }
09826    
09827    /* Set language from config to override channel language */
09828    if (!ast_strlen_zero(vmu->language))
09829       ast_string_field_set(chan, language, vmu->language);
09830 
09831    /* Retrieve urgent, old and new message counts */
09832    ast_debug(1, "Before open_mailbox\n");
09833    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09834    if (res < 0)
09835       goto out;
09836    vms.oldmessages = vms.lastmsg + 1;
09837    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
09838    /* check INBOX */
09839    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09840    if (res < 0)
09841       goto out;
09842    vms.newmessages = vms.lastmsg + 1;
09843    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
09844    /* Start in Urgent */
09845    in_urgent = 1;
09846    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09847    if (res < 0)
09848       goto out;
09849    vms.urgentmessages = vms.lastmsg + 1;
09850    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
09851 
09852    /* Select proper mailbox FIRST!! */
09853    if (play_auto) {
09854       if (vms.urgentmessages) {
09855          in_urgent = 1;
09856          res = open_mailbox(&vms, vmu, 11);
09857       } else {
09858          in_urgent = 0;
09859          res = open_mailbox(&vms, vmu, play_folder);
09860       }
09861       if (res < 0)
09862          goto out;
09863 
09864       /* If there are no new messages, inform the user and hangup */
09865       if (vms.lastmsg == -1) {
09866          in_urgent = 0;
09867          cmd = vm_browse_messages(chan, &vms, vmu);
09868          res = 0;
09869          goto out;
09870       }
09871    } else {
09872       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09873          /* If we only have old messages start here */
09874          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09875          in_urgent = 0;
09876          play_folder = 1;
09877          if (res < 0)
09878             goto out;
09879       } else if (!vms.urgentmessages && vms.newmessages) {
09880          /* If we have new messages but none are urgent */
09881          in_urgent = 0;
09882          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09883          if (res < 0)
09884             goto out;
09885       }
09886    }
09887 
09888    if (useadsi)
09889       adsi_status(chan, &vms);
09890    res = 0;
09891 
09892    /* Check to see if this is a new user */
09893    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09894       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09895       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09896          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09897       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09898       if ((cmd == 't') || (cmd == '#')) {
09899          /* Timeout */
09900          res = 0;
09901          goto out;
09902       } else if (cmd < 0) {
09903          /* Hangup */
09904          res = -1;
09905          goto out;
09906       }
09907    }
09908 #ifdef IMAP_STORAGE
09909       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
09910       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09911          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09912          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09913       }
09914       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09915       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09916          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09917          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09918       }
09919 #endif
09920    if (play_auto) {
09921       cmd = '1';
09922    } else {
09923       cmd = vm_intro(chan, vmu, &vms);
09924    }
09925 
09926    vms.repeats = 0;
09927    vms.starting = 1;
09928    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09929       /* Run main menu */
09930       switch (cmd) {
09931       case '1': /* First message */
09932          vms.curmsg = 0;
09933          /* Fall through */
09934       case '5': /* Play current message */
09935          cmd = vm_browse_messages(chan, &vms, vmu);
09936          break;
09937       case '2': /* Change folders */
09938          if (useadsi)
09939             adsi_folders(chan, 0, "Change to folder...");
09940          cmd = get_folder2(chan, "vm-changeto", 0);
09941          if (cmd == '#') {
09942             cmd = 0;
09943          } else if (cmd > 0) {
09944             cmd = cmd - '0';
09945             res = close_mailbox(&vms, vmu);
09946             if (res == ERROR_LOCK_PATH)
09947                goto out;
09948             /* If folder is not urgent, set in_urgent to zero! */
09949             if (cmd != 11) in_urgent = 0;
09950             res = open_mailbox(&vms, vmu, cmd);
09951             if (res < 0)
09952                goto out;
09953             play_folder = cmd;
09954             cmd = 0;
09955          }
09956          if (useadsi)
09957             adsi_status2(chan, &vms);
09958             
09959          if (!cmd)
09960             cmd = vm_play_folder_name(chan, vms.vmbox);
09961 
09962          vms.starting = 1;
09963          break;
09964       case '3': /* Advanced options */
09965          cmd = 0;
09966          vms.repeats = 0;
09967          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09968             switch (cmd) {
09969             case '1': /* Reply */
09970                if (vms.lastmsg > -1 && !vms.starting) {
09971                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09972                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09973                      res = cmd;
09974                      goto out;
09975                   }
09976                } else
09977                   cmd = ast_play_and_wait(chan, "vm-sorry");
09978                cmd = 't';
09979                break;
09980             case '2': /* Callback */
09981                if (!vms.starting)
09982                   ast_verb(3, "Callback Requested\n");
09983                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09984                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09985                   if (cmd == 9) {
09986                      silentexit = 1;
09987                      goto out;
09988                   } else if (cmd == ERROR_LOCK_PATH) {
09989                      res = cmd;
09990                      goto out;
09991                   }
09992                } else 
09993                   cmd = ast_play_and_wait(chan, "vm-sorry");
09994                cmd = 't';
09995                break;
09996             case '3': /* Envelope */
09997                if (vms.lastmsg > -1 && !vms.starting) {
09998                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09999                   if (cmd == ERROR_LOCK_PATH) {
10000                      res = cmd;
10001                      goto out;
10002                   }
10003                } else
10004                   cmd = ast_play_and_wait(chan, "vm-sorry");
10005                cmd = 't';
10006                break;
10007             case '4': /* Dialout */
10008                if (!ast_strlen_zero(vmu->dialout)) {
10009                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10010                   if (cmd == 9) {
10011                      silentexit = 1;
10012                      goto out;
10013                   }
10014                } else 
10015                   cmd = ast_play_and_wait(chan, "vm-sorry");
10016                cmd = 't';
10017                break;
10018 
10019             case '5': /* Leave VoiceMail */
10020                if (ast_test_flag(vmu, VM_SVMAIL)) {
10021                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10022                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10023                      res = cmd;
10024                      goto out;
10025                   }
10026                } else
10027                   cmd = ast_play_and_wait(chan, "vm-sorry");
10028                cmd = 't';
10029                break;
10030                
10031             case '*': /* Return to main menu */
10032                cmd = 't';
10033                break;
10034 
10035             default:
10036                cmd = 0;
10037                if (!vms.starting) {
10038                   cmd = ast_play_and_wait(chan, "vm-toreply");
10039                }
10040                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10041                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10042                }
10043                if (!cmd && !vms.starting) {
10044                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10045                }
10046                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10047                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10048                }
10049                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
10050                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10051                if (!cmd)
10052                   cmd = ast_play_and_wait(chan, "vm-starmain");
10053                if (!cmd)
10054                   cmd = ast_waitfordigit(chan, 6000);
10055                if (!cmd)
10056                   vms.repeats++;
10057                if (vms.repeats > 3)
10058                   cmd = 't';
10059             }
10060          }
10061          if (cmd == 't') {
10062             cmd = 0;
10063             vms.repeats = 0;
10064          }
10065          break;
10066       case '4': /* Go to the previous message */
10067          if (vms.curmsg > 0) {
10068             vms.curmsg--;
10069             cmd = play_message(chan, vmu, &vms);
10070          } else {
10071             /* Check if we were listening to new
10072                messages.  If so, go to Urgent messages
10073                instead of saying "no more messages"
10074             */
10075             if (in_urgent == 0 && vms.urgentmessages > 0) {
10076                /* Check for Urgent messages */
10077                in_urgent = 1;
10078                res = close_mailbox(&vms, vmu);
10079                if (res == ERROR_LOCK_PATH)
10080                   goto out;
10081                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10082                if (res < 0)
10083                   goto out;
10084                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10085                vms.curmsg = vms.lastmsg;
10086                if (vms.lastmsg < 0)
10087                   cmd = ast_play_and_wait(chan, "vm-nomore");
10088             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10089                vms.curmsg = vms.lastmsg;
10090                cmd = play_message(chan, vmu, &vms);
10091             } else {
10092                cmd = ast_play_and_wait(chan, "vm-nomore");
10093             }
10094          }
10095          break;
10096       case '6': /* Go to the next message */
10097          if (vms.curmsg < vms.lastmsg) {
10098             vms.curmsg++;
10099             cmd = play_message(chan, vmu, &vms);
10100          } else {
10101             if (in_urgent && vms.newmessages > 0) {
10102                /* Check if we were listening to urgent
10103                 * messages.  If so, go to regular new messages
10104                 * instead of saying "no more messages"
10105                 */
10106                in_urgent = 0;
10107                res = close_mailbox(&vms, vmu);
10108                if (res == ERROR_LOCK_PATH)
10109                   goto out;
10110                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10111                if (res < 0)
10112                   goto out;
10113                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10114                vms.curmsg = -1;
10115                if (vms.lastmsg < 0) {
10116                   cmd = ast_play_and_wait(chan, "vm-nomore");
10117                }
10118             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10119                vms.curmsg = 0;
10120                cmd = play_message(chan, vmu, &vms);
10121             } else {
10122                cmd = ast_play_and_wait(chan, "vm-nomore");
10123             }
10124          }
10125          break;
10126       case '7': /* Delete the current message */
10127          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10128             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10129             if (useadsi)
10130                adsi_delete(chan, &vms);
10131             if (vms.deleted[vms.curmsg]) {
10132                if (play_folder == 0) {
10133                   if (in_urgent) {
10134                      vms.urgentmessages--;
10135                   } else {
10136                      vms.newmessages--;
10137                   }
10138                }
10139                else if (play_folder == 1)
10140                   vms.oldmessages--;
10141                cmd = ast_play_and_wait(chan, "vm-deleted");
10142             } else {
10143                if (play_folder == 0) {
10144                   if (in_urgent) {
10145                      vms.urgentmessages++;
10146                   } else {
10147                      vms.newmessages++;
10148                   }
10149                }
10150                else if (play_folder == 1)
10151                   vms.oldmessages++;
10152                cmd = ast_play_and_wait(chan, "vm-undeleted");
10153             }
10154             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10155                if (vms.curmsg < vms.lastmsg) {
10156                   vms.curmsg++;
10157                   cmd = play_message(chan, vmu, &vms);
10158                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10159                   vms.curmsg = 0;
10160                   cmd = play_message(chan, vmu, &vms);
10161                } else {
10162                   /* Check if we were listening to urgent
10163                      messages.  If so, go to regular new messages
10164                      instead of saying "no more messages"
10165                   */
10166                   if (in_urgent == 1) {
10167                      /* Check for new messages */
10168                      in_urgent = 0;
10169                      res = close_mailbox(&vms, vmu);
10170                      if (res == ERROR_LOCK_PATH)
10171                         goto out;
10172                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10173                      if (res < 0)
10174                         goto out;
10175                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10176                      vms.curmsg = -1;
10177                      if (vms.lastmsg < 0)
10178                         cmd = ast_play_and_wait(chan, "vm-nomore");
10179                   } else {
10180                      cmd = ast_play_and_wait(chan, "vm-nomore");
10181                   }
10182                }
10183             }
10184          } else /* Delete not valid if we haven't selected a message */
10185             cmd = 0;
10186 #ifdef IMAP_STORAGE
10187          deleted = 1;
10188 #endif
10189          break;
10190    
10191       case '8': /* Forward the current messgae */
10192          if (vms.lastmsg > -1) {
10193             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10194             if (cmd == ERROR_LOCK_PATH) {
10195                res = cmd;
10196                goto out;
10197             }
10198          } else {
10199             /* Check if we were listening to urgent
10200                messages.  If so, go to regular new messages
10201                instead of saying "no more messages"
10202             */
10203             if (in_urgent == 1 && vms.newmessages > 0) {
10204                /* Check for new messages */
10205                in_urgent = 0;
10206                res = close_mailbox(&vms, vmu);
10207                if (res == ERROR_LOCK_PATH)
10208                   goto out;
10209                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10210                if (res < 0)
10211                   goto out;
10212                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10213                vms.curmsg = -1;
10214                if (vms.lastmsg < 0)
10215                   cmd = ast_play_and_wait(chan, "vm-nomore");
10216             } else {
10217                cmd = ast_play_and_wait(chan, "vm-nomore");
10218             }
10219          }
10220          break;
10221       case '9': /* Save message to folder */
10222          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10223             /* No message selected */
10224             cmd = 0;
10225             break;
10226          }
10227          if (useadsi)
10228             adsi_folders(chan, 1, "Save to folder...");
10229          cmd = get_folder2(chan, "vm-savefolder", 1);
10230          box = 0; /* Shut up compiler */
10231          if (cmd == '#') {
10232             cmd = 0;
10233             break;
10234          } else if (cmd > 0) {
10235             box = cmd = cmd - '0';
10236             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10237             if (cmd == ERROR_LOCK_PATH) {
10238                res = cmd;
10239                goto out;
10240 #ifndef IMAP_STORAGE
10241             } else if (!cmd) {
10242                vms.deleted[vms.curmsg] = 1;
10243 #endif
10244             } else {
10245                vms.deleted[vms.curmsg] = 0;
10246                vms.heard[vms.curmsg] = 0;
10247             }
10248          }
10249          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10250          if (useadsi)
10251             adsi_message(chan, &vms);
10252          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10253          if (!cmd) {
10254             cmd = ast_play_and_wait(chan, "vm-message");
10255             if (!cmd) 
10256                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10257             if (!cmd)
10258                cmd = ast_play_and_wait(chan, "vm-savedto");
10259             if (!cmd)
10260                cmd = vm_play_folder_name(chan, vms.fn);
10261          } else {
10262             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10263          }
10264          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10265             if (vms.curmsg < vms.lastmsg) {
10266                vms.curmsg++;
10267                cmd = play_message(chan, vmu, &vms);
10268             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10269                vms.curmsg = 0;
10270                cmd = play_message(chan, vmu, &vms);
10271             } else {
10272                /* Check if we were listening to urgent
10273                   messages.  If so, go to regular new messages
10274                   instead of saying "no more messages"
10275                */
10276                if (in_urgent == 1 && vms.newmessages > 0) {
10277                   /* Check for new messages */
10278                   in_urgent = 0;
10279                   res = close_mailbox(&vms, vmu);
10280                   if (res == ERROR_LOCK_PATH)
10281                      goto out;
10282                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10283                   if (res < 0)
10284                      goto out;
10285                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10286                   vms.curmsg = -1;
10287                   if (vms.lastmsg < 0)
10288                      cmd = ast_play_and_wait(chan, "vm-nomore");
10289                } else {
10290                   cmd = ast_play_and_wait(chan, "vm-nomore");
10291                }
10292             }
10293          }
10294          break;
10295       case '*': /* Help */
10296          if (!vms.starting) {
10297             cmd = ast_play_and_wait(chan, "vm-onefor");
10298             if (!strncasecmp(chan->language, "he", 2)) {
10299                cmd = ast_play_and_wait(chan, "vm-for");
10300             }
10301             if (!cmd)
10302                cmd = vm_play_folder_name(chan, vms.vmbox);
10303             if (!cmd)
10304                cmd = ast_play_and_wait(chan, "vm-opts");
10305             if (!cmd)
10306                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10307          } else
10308             cmd = 0;
10309          break;
10310       case '0': /* Mailbox options */
10311          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10312          if (useadsi)
10313             adsi_status(chan, &vms);
10314          break;
10315       default: /* Nothing */
10316          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10317          break;
10318       }
10319    }
10320    if ((cmd == 't') || (cmd == '#')) {
10321       /* Timeout */
10322       res = 0;
10323    } else {
10324       /* Hangup */
10325       res = -1;
10326    }
10327 
10328 out:
10329    if (res > -1) {
10330       ast_stopstream(chan);
10331       adsi_goodbye(chan);
10332       if (valid && res != OPERATOR_EXIT) {
10333          if (silentexit)
10334             res = ast_play_and_wait(chan, "vm-dialout");
10335          else 
10336             res = ast_play_and_wait(chan, "vm-goodbye");
10337       }
10338       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10339          res = 0;
10340       }
10341       if (useadsi)
10342          ast_adsi_unload_session(chan);
10343    }
10344    if (vmu)
10345       close_mailbox(&vms, vmu);
10346    if (valid) {
10347       int new = 0, old = 0, urgent = 0;
10348       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10349       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10350       /* Urgent flag not passwd to externnotify here */
10351       run_externnotify(vmu->context, vmu->mailbox, NULL);
10352       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10353       queue_mwi_event(ext_context, urgent, new, old);
10354    }
10355 #ifdef IMAP_STORAGE
10356    /* expunge message - use UID Expunge if supported on IMAP server*/
10357    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10358    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10359       ast_mutex_lock(&vms.lock);
10360 #ifdef HAVE_IMAP_TK2006
10361       if (LEVELUIDPLUS (vms.mailstream)) {
10362          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10363       } else 
10364 #endif
10365          mail_expunge(vms.mailstream);
10366       ast_mutex_unlock(&vms.lock);
10367    }
10368    /*  before we delete the state, we should copy pertinent info
10369     *  back to the persistent model */
10370    if (vmu) {
10371       vmstate_delete(&vms);
10372    }
10373 #endif
10374    if (vmu)
10375       free_user(vmu);
10376    if (vms.deleted)
10377       ast_free(vms.deleted);
10378    if (vms.heard)
10379       ast_free(vms.heard);
10380 
10381 #ifdef IMAP_STORAGE
10382    pthread_setspecific(ts_vmstate.key, NULL);
10383 #endif
10384    return res;
10385 }
10386 
10387 static int vm_exec(struct ast_channel *chan, const char *data)
10388 {
10389    int res = 0;
10390    char *tmp;
10391    struct leave_vm_options leave_options;
10392    struct ast_flags flags = { 0 };
10393    char *opts[OPT_ARG_ARRAY_SIZE];
10394    AST_DECLARE_APP_ARGS(args,
10395       AST_APP_ARG(argv0);
10396       AST_APP_ARG(argv1);
10397    );
10398    
10399    memset(&leave_options, 0, sizeof(leave_options));
10400 
10401    if (chan->_state != AST_STATE_UP)
10402       ast_answer(chan);
10403 
10404    if (!ast_strlen_zero(data)) {
10405       tmp = ast_strdupa(data);
10406       AST_STANDARD_APP_ARGS(args, tmp);
10407       if (args.argc == 2) {
10408          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10409             return -1;
10410          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10411          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10412             int gain;
10413 
10414             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10415                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10416                return -1;
10417             } else {
10418                leave_options.record_gain = (signed char) gain;
10419             }
10420          }
10421          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10422             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10423                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10424          }
10425       }
10426    } else {
10427       char temp[256];
10428       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10429       if (res < 0)
10430          return res;
10431       if (ast_strlen_zero(temp))
10432          return 0;
10433       args.argv0 = ast_strdupa(temp);
10434    }
10435 
10436    res = leave_voicemail(chan, args.argv0, &leave_options);
10437    if (res == 't') {
10438       ast_play_and_wait(chan, "vm-goodbye");
10439       res = 0;
10440    }
10441 
10442    if (res == OPERATOR_EXIT) {
10443       res = 0;
10444    }
10445 
10446    if (res == ERROR_LOCK_PATH) {
10447       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10448       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10449       res = 0;
10450    }
10451 
10452    return res;
10453 }
10454 
10455 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10456 {
10457    struct ast_vm_user *vmu;
10458 
10459    AST_LIST_TRAVERSE(&users, vmu, list) {
10460       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10461          if (strcasecmp(vmu->context, context)) {
10462             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10463                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10464                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10465                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10466          }
10467          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10468          return NULL;
10469       }
10470       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10471          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10472          return NULL;
10473       }
10474    }
10475    
10476    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10477       return NULL;
10478    
10479    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10480    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10481 
10482    AST_LIST_INSERT_TAIL(&users, vmu, list);
10483    
10484    return vmu;
10485 }
10486 
10487 static int append_mailbox(const char *context, const char *box, const char *data)
10488 {
10489    /* Assumes lock is already held */
10490    char *tmp;
10491    char *stringp;
10492    char *s;
10493    struct ast_vm_user *vmu;
10494    char *mailbox_full;
10495    int new = 0, old = 0, urgent = 0;
10496    char secretfn[PATH_MAX] = "";
10497 
10498    tmp = ast_strdupa(data);
10499 
10500    if (!(vmu = find_or_create(context, box)))
10501       return -1;
10502 
10503    populate_defaults(vmu);
10504 
10505    stringp = tmp;
10506    if ((s = strsep(&stringp, ","))) {
10507       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10508    }
10509    if (stringp && (s = strsep(&stringp, ","))) {
10510       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10511    }
10512    if (stringp && (s = strsep(&stringp, ","))) {
10513       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10514    }
10515    if (stringp && (s = strsep(&stringp, ","))) {
10516       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10517    }
10518    if (stringp && (s = strsep(&stringp, ","))) {
10519       apply_options(vmu, s);
10520    }
10521 
10522    switch (vmu->passwordlocation) {
10523    case OPT_PWLOC_SPOOLDIR:
10524       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10525       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10526    }
10527 
10528    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10529    strcpy(mailbox_full, box);
10530    strcat(mailbox_full, "@");
10531    strcat(mailbox_full, context);
10532 
10533    inboxcount2(mailbox_full, &urgent, &new, &old);
10534    queue_mwi_event(mailbox_full, urgent, new, old);
10535 
10536    return 0;
10537 }
10538 
10539 AST_TEST_DEFINE(test_voicemail_vmuser)
10540 {
10541    int res = 0;
10542    struct ast_vm_user *vmu;
10543    /* language parameter seems to only be used for display in manager action */
10544    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10545       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10546       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10547       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10548       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10549       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10550       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir";
10551 #ifdef IMAP_STORAGE
10552    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10553       "imapfolder=INBOX|imapvmshareid=6000";
10554 #endif
10555 
10556    switch (cmd) {
10557    case TEST_INIT:
10558       info->name = "vmuser";
10559       info->category = "/apps/app_voicemail/";
10560       info->summary = "Vmuser unit test";
10561       info->description =
10562          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10563       return AST_TEST_NOT_RUN;
10564    case TEST_EXECUTE:
10565       break;
10566    }
10567 
10568    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10569       return AST_TEST_NOT_RUN;
10570    }
10571    ast_set_flag(vmu, VM_ALLOCED);
10572 
10573    apply_options(vmu, options_string);
10574 
10575    if (!ast_test_flag(vmu, VM_ATTACH)) {
10576       ast_test_status_update(test, "Parse failure for attach option\n");
10577       res = 1;
10578    }
10579    if (strcasecmp(vmu->attachfmt, "wav49")) {
10580       ast_test_status_update(test, "Parse failure for attachftm option\n");
10581       res = 1;
10582    }
10583    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10584       ast_test_status_update(test, "Parse failure for serveremail option\n");
10585       res = 1;
10586    }
10587    if (strcasecmp(vmu->zonetag, "central")) {
10588       ast_test_status_update(test, "Parse failure for tz option\n");
10589       res = 1;
10590    }
10591    if (!ast_test_flag(vmu, VM_DELETE)) {
10592       ast_test_status_update(test, "Parse failure for delete option\n");
10593       res = 1;
10594    }
10595    if (!ast_test_flag(vmu, VM_SAYCID)) {
10596       ast_test_status_update(test, "Parse failure for saycid option\n");
10597       res = 1;
10598    }
10599    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10600       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10601       res = 1;
10602    }
10603    if (!ast_test_flag(vmu, VM_REVIEW)) {
10604       ast_test_status_update(test, "Parse failure for review option\n");
10605       res = 1;
10606    }
10607    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10608       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10609       res = 1;
10610    }
10611    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10612       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10613       res = 1;
10614    }
10615    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10616       ast_test_status_update(test, "Parse failure for operator option\n");
10617       res = 1;
10618    }
10619    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10620       ast_test_status_update(test, "Parse failure for envelope option\n");
10621       res = 1;
10622    }
10623    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10624       ast_test_status_update(test, "Parse failure for moveheard option\n");
10625       res = 1;
10626    }
10627    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10628       ast_test_status_update(test, "Parse failure for sayduration option\n");
10629       res = 1;
10630    }
10631    if (vmu->saydurationm != 5) {
10632       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10633       res = 1;
10634    }
10635    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10636       ast_test_status_update(test, "Parse failure for forcename option\n");
10637       res = 1;
10638    }
10639    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10640       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10641       res = 1;
10642    }
10643    if (strcasecmp(vmu->callback, "somecontext")) {
10644       ast_test_status_update(test, "Parse failure for callbacks option\n");
10645       res = 1;
10646    }
10647    if (strcasecmp(vmu->dialout, "somecontext2")) {
10648       ast_test_status_update(test, "Parse failure for dialout option\n");
10649       res = 1;
10650    }
10651    if (strcasecmp(vmu->exit, "somecontext3")) {
10652       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10653       res = 1;
10654    }
10655    if (vmu->minsecs != 10) {
10656       ast_test_status_update(test, "Parse failure for minsecs option\n");
10657       res = 1;
10658    }
10659    if (vmu->maxsecs != 100) {
10660       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10661       res = 1;
10662    }
10663    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10664       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10665       res = 1;
10666    }
10667    if (vmu->maxdeletedmsg != 50) {
10668       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10669       res = 1;
10670    }
10671    if (vmu->volgain != 1.3) {
10672       ast_test_status_update(test, "Parse failure for volgain option\n");
10673       res = 1;
10674    }
10675    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10676       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10677       res = 1;
10678    }
10679 #ifdef IMAP_STORAGE
10680    apply_options(vmu, option_string2);
10681 
10682    if (strcasecmp(vmu->imapuser, "imapuser")) {
10683       ast_test_status_update(test, "Parse failure for imapuser option\n");
10684       res = 1;
10685    }
10686    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10687       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10688       res = 1;
10689    }
10690    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10691       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10692       res = 1;
10693    }
10694    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10695       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10696       res = 1;
10697    }
10698 #endif
10699 
10700    free_user(vmu);
10701    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10702 }
10703 
10704 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10705 {
10706    struct ast_vm_user svm;
10707    char *context, *box;
10708    AST_DECLARE_APP_ARGS(args,
10709       AST_APP_ARG(mbox);
10710       AST_APP_ARG(options);
10711    );
10712    static int dep_warning = 0;
10713 
10714    if (ast_strlen_zero(data)) {
10715       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10716       return -1;
10717    }
10718 
10719    if (!dep_warning) {
10720       dep_warning = 1;
10721       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10722    }
10723 
10724    box = ast_strdupa(data);
10725 
10726    AST_STANDARD_APP_ARGS(args, box);
10727 
10728    if (args.options) {
10729    }
10730 
10731    if ((context = strchr(args.mbox, '@'))) {
10732       *context = '\0';
10733       context++;
10734    }
10735 
10736    if (find_user(&svm, context, args.mbox)) {
10737       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10738    } else
10739       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10740 
10741    return 0;
10742 }
10743 
10744 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10745 {
10746    struct ast_vm_user svm;
10747    AST_DECLARE_APP_ARGS(arg,
10748       AST_APP_ARG(mbox);
10749       AST_APP_ARG(context);
10750    );
10751 
10752    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10753 
10754    if (ast_strlen_zero(arg.mbox)) {
10755       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10756       return -1;
10757    }
10758 
10759    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10760    return 0;
10761 }
10762 
10763 static struct ast_custom_function mailbox_exists_acf = {
10764    .name = "MAILBOX_EXISTS",
10765    .read = acf_mailbox_exists,
10766 };
10767 
10768 static int vmauthenticate(struct ast_channel *chan, const char *data)
10769 {
10770    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
10771    struct ast_vm_user vmus;
10772    char *options = NULL;
10773    int silent = 0, skipuser = 0;
10774    int res = -1;
10775    
10776    if (data) {
10777       s = ast_strdupa(data);
10778       user = strsep(&s, ",");
10779       options = strsep(&s, ",");
10780       if (user) {
10781          s = user;
10782          user = strsep(&s, "@");
10783          context = strsep(&s, "");
10784          if (!ast_strlen_zero(user))
10785             skipuser++;
10786          ast_copy_string(mailbox, user, sizeof(mailbox));
10787       }
10788    }
10789 
10790    if (options) {
10791       silent = (strchr(options, 's')) != NULL;
10792    }
10793 
10794    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10795       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10796       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10797       ast_play_and_wait(chan, "auth-thankyou");
10798       res = 0;
10799    } else if (mailbox[0] == '*') {
10800       /* user entered '*' */
10801       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10802          res = 0; /* prevent hangup */
10803       }
10804    }
10805 
10806    return res;
10807 }
10808 
10809 static char *show_users_realtime(int fd, const char *context)
10810 {
10811    struct ast_config *cfg;
10812    const char *cat = NULL;
10813 
10814    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10815       "context", context, SENTINEL))) {
10816       return CLI_FAILURE;
10817    }
10818 
10819    ast_cli(fd,
10820       "\n"
10821       "=============================================================\n"
10822       "=== Configured Voicemail Users ==============================\n"
10823       "=============================================================\n"
10824       "===\n");
10825 
10826    while ((cat = ast_category_browse(cfg, cat))) {
10827       struct ast_variable *var = NULL;
10828       ast_cli(fd,
10829          "=== Mailbox ...\n"
10830          "===\n");
10831       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10832          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10833       ast_cli(fd,
10834          "===\n"
10835          "=== ---------------------------------------------------------\n"
10836          "===\n");
10837    }
10838 
10839    ast_cli(fd,
10840       "=============================================================\n"
10841       "\n");
10842 
10843    ast_config_destroy(cfg);
10844 
10845    return CLI_SUCCESS;
10846 }
10847 
10848 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10849 {
10850    int which = 0;
10851    int wordlen;
10852    struct ast_vm_user *vmu;
10853    const char *context = "";
10854 
10855    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10856    if (pos > 4)
10857       return NULL;
10858    if (pos == 3)
10859       return (state == 0) ? ast_strdup("for") : NULL;
10860    wordlen = strlen(word);
10861    AST_LIST_TRAVERSE(&users, vmu, list) {
10862       if (!strncasecmp(word, vmu->context, wordlen)) {
10863          if (context && strcmp(context, vmu->context) && ++which > state)
10864             return ast_strdup(vmu->context);
10865          /* ignore repeated contexts ? */
10866          context = vmu->context;
10867       }
10868    }
10869    return NULL;
10870 }
10871 
10872 /*! \brief Show a list of voicemail users in the CLI */
10873 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10874 {
10875    struct ast_vm_user *vmu;
10876 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10877    const char *context = NULL;
10878    int users_counter = 0;
10879 
10880    switch (cmd) {
10881    case CLI_INIT:
10882       e->command = "voicemail show users";
10883       e->usage =
10884          "Usage: voicemail show users [for <context>]\n"
10885          "       Lists all mailboxes currently set up\n";
10886       return NULL;
10887    case CLI_GENERATE:
10888       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10889    }  
10890 
10891    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10892       return CLI_SHOWUSAGE;
10893    if (a->argc == 5) {
10894       if (strcmp(a->argv[3],"for"))
10895          return CLI_SHOWUSAGE;
10896       context = a->argv[4];
10897    }
10898 
10899    if (ast_check_realtime("voicemail")) {
10900       if (!context) {
10901          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10902          return CLI_SHOWUSAGE;
10903       }
10904       return show_users_realtime(a->fd, context);
10905    }
10906 
10907    AST_LIST_LOCK(&users);
10908    if (AST_LIST_EMPTY(&users)) {
10909       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10910       AST_LIST_UNLOCK(&users);
10911       return CLI_FAILURE;
10912    }
10913    if (a->argc == 3)
10914       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10915    else {
10916       int count = 0;
10917       AST_LIST_TRAVERSE(&users, vmu, list) {
10918          if (!strcmp(context, vmu->context))
10919             count++;
10920       }
10921       if (count) {
10922          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10923       } else {
10924          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10925          AST_LIST_UNLOCK(&users);
10926          return CLI_FAILURE;
10927       }
10928    }
10929    AST_LIST_TRAVERSE(&users, vmu, list) {
10930       int newmsgs = 0, oldmsgs = 0;
10931       char count[12], tmp[256] = "";
10932 
10933       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10934          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10935          inboxcount(tmp, &newmsgs, &oldmsgs);
10936          snprintf(count, sizeof(count), "%d", newmsgs);
10937          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10938          users_counter++;
10939       }
10940    }
10941    AST_LIST_UNLOCK(&users);
10942    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10943    return CLI_SUCCESS;
10944 }
10945 
10946 /*! \brief Show a list of voicemail zones in the CLI */
10947 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10948 {
10949    struct vm_zone *zone;
10950 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10951    char *res = CLI_SUCCESS;
10952 
10953    switch (cmd) {
10954    case CLI_INIT:
10955       e->command = "voicemail show zones";
10956       e->usage =
10957          "Usage: voicemail show zones\n"
10958          "       Lists zone message formats\n";
10959       return NULL;
10960    case CLI_GENERATE:
10961       return NULL;
10962    }
10963 
10964    if (a->argc != 3)
10965       return CLI_SHOWUSAGE;
10966 
10967    AST_LIST_LOCK(&zones);
10968    if (!AST_LIST_EMPTY(&zones)) {
10969       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10970       AST_LIST_TRAVERSE(&zones, zone, list) {
10971          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10972       }
10973    } else {
10974       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10975       res = CLI_FAILURE;
10976    }
10977    AST_LIST_UNLOCK(&zones);
10978 
10979    return res;
10980 }
10981 
10982 /*! \brief Reload voicemail configuration from the CLI */
10983 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10984 {
10985    switch (cmd) {
10986    case CLI_INIT:
10987       e->command = "voicemail reload";
10988       e->usage =
10989          "Usage: voicemail reload\n"
10990          "       Reload voicemail configuration\n";
10991       return NULL;
10992    case CLI_GENERATE:
10993       return NULL;
10994    }
10995 
10996    if (a->argc != 2)
10997       return CLI_SHOWUSAGE;
10998 
10999    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11000    load_config(1);
11001    
11002    return CLI_SUCCESS;
11003 }
11004 
11005 static struct ast_cli_entry cli_voicemail[] = {
11006    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11007    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11008    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11009 };
11010 
11011 #ifdef IMAP_STORAGE
11012    #define DATA_EXPORT_VM_USERS(USER)              \
11013       USER(ast_vm_user, context, AST_DATA_STRING)        \
11014       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11015       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11016       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11017       USER(ast_vm_user, email, AST_DATA_STRING)       \
11018       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11019       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11020       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11021       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11022       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11023       USER(ast_vm_user, language, AST_DATA_STRING)       \
11024       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11025       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11026       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11027       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11028       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11029       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11030       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11031       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11032       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11033       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11034       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11035       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11036       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11037       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11038       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11039 #else
11040    #define DATA_EXPORT_VM_USERS(USER)              \
11041       USER(ast_vm_user, context, AST_DATA_STRING)        \
11042       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11043       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11044       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11045       USER(ast_vm_user, email, AST_DATA_STRING)       \
11046       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11047       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11048       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11049       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11050       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11051       USER(ast_vm_user, language, AST_DATA_STRING)       \
11052       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11053       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11054       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11055       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11056       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11057       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11058       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11059       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11060       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11061       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11062       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11063       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11064 #endif
11065 
11066 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11067 
11068 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11069    ZONE(vm_zone, name, AST_DATA_STRING)      \
11070    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11071    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11072 
11073 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11074 
11075 /*!
11076  * \internal
11077  * \brief Add voicemail user to the data_root.
11078  * \param[in] search The search tree.
11079  * \param[in] data_root The main result node.
11080  * \param[in] user The voicemail user.
11081  */
11082 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11083     struct ast_data *data_root, struct ast_vm_user *user)
11084 {
11085    struct ast_data *data_user, *data_zone;
11086    struct ast_data *data_state;
11087    struct vm_zone *zone = NULL;
11088    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11089    char ext_context[256] = "";
11090 
11091    data_user = ast_data_add_node(data_root, "user");
11092    if (!data_user) {
11093       return -1;
11094    }
11095 
11096    ast_data_add_structure(ast_vm_user, data_user, user);
11097 
11098    AST_LIST_LOCK(&zones);
11099    AST_LIST_TRAVERSE(&zones, zone, list) {
11100       if (!strcmp(zone->name, user->zonetag)) {
11101          break;
11102       }
11103    }
11104    AST_LIST_UNLOCK(&zones);
11105 
11106    /* state */
11107    data_state = ast_data_add_node(data_user, "state");
11108    if (!data_state) {
11109       return -1;
11110    }
11111    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11112    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11113    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11114    ast_data_add_int(data_state, "newmsg", newmsg);
11115    ast_data_add_int(data_state, "oldmsg", oldmsg);
11116 
11117    if (zone) {
11118       data_zone = ast_data_add_node(data_user, "zone");
11119       ast_data_add_structure(vm_zone, data_zone, zone);
11120    }
11121 
11122    if (!ast_data_search_match(search, data_user)) {
11123       ast_data_remove_node(data_root, data_user);
11124    }
11125 
11126    return 0;
11127 }
11128 
11129 static int vm_users_data_provider_get(const struct ast_data_search *search,
11130    struct ast_data *data_root)
11131 {
11132    struct ast_vm_user *user;
11133 
11134    AST_LIST_LOCK(&users);
11135    AST_LIST_TRAVERSE(&users, user, list) {
11136       vm_users_data_provider_get_helper(search, data_root, user);
11137    }
11138    AST_LIST_UNLOCK(&users);
11139 
11140    return 0;
11141 }
11142 
11143 static const struct ast_data_handler vm_users_data_provider = {
11144    .version = AST_DATA_HANDLER_VERSION,
11145    .get = vm_users_data_provider_get
11146 };
11147 
11148 static const struct ast_data_entry vm_data_providers[] = {
11149    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11150 };
11151 
11152 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11153 {
11154    int new = 0, old = 0, urgent = 0;
11155 
11156    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11157 
11158    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11159       mwi_sub->old_urgent = urgent;
11160       mwi_sub->old_new = new;
11161       mwi_sub->old_old = old;
11162       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11163       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11164    }
11165 }
11166 
11167 static void poll_subscribed_mailboxes(void)
11168 {
11169    struct mwi_sub *mwi_sub;
11170 
11171    AST_RWLIST_RDLOCK(&mwi_subs);
11172    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11173       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11174          poll_subscribed_mailbox(mwi_sub);
11175       }
11176    }
11177    AST_RWLIST_UNLOCK(&mwi_subs);
11178 }
11179 
11180 static void *mb_poll_thread(void *data)
11181 {
11182    while (poll_thread_run) {
11183       struct timespec ts = { 0, };
11184       struct timeval wait;
11185 
11186       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11187       ts.tv_sec = wait.tv_sec;
11188       ts.tv_nsec = wait.tv_usec * 1000;
11189 
11190       ast_mutex_lock(&poll_lock);
11191       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11192       ast_mutex_unlock(&poll_lock);
11193 
11194       if (!poll_thread_run)
11195          break;
11196 
11197       poll_subscribed_mailboxes();
11198    }
11199 
11200    return NULL;
11201 }
11202 
11203 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11204 {
11205    ast_free(mwi_sub);
11206 }
11207 
11208 static int handle_unsubscribe(void *datap)
11209 {
11210    struct mwi_sub *mwi_sub;
11211    uint32_t *uniqueid = datap;
11212    
11213    AST_RWLIST_WRLOCK(&mwi_subs);
11214    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11215       if (mwi_sub->uniqueid == *uniqueid) {
11216          AST_LIST_REMOVE_CURRENT(entry);
11217          break;
11218       }
11219    }
11220    AST_RWLIST_TRAVERSE_SAFE_END
11221    AST_RWLIST_UNLOCK(&mwi_subs);
11222 
11223    if (mwi_sub)
11224       mwi_sub_destroy(mwi_sub);
11225 
11226    ast_free(uniqueid);  
11227    return 0;
11228 }
11229 
11230 static int handle_subscribe(void *datap)
11231 {
11232    unsigned int len;
11233    struct mwi_sub *mwi_sub;
11234    struct mwi_sub_task *p = datap;
11235 
11236    len = sizeof(*mwi_sub);
11237    if (!ast_strlen_zero(p->mailbox))
11238       len += strlen(p->mailbox);
11239 
11240    if (!ast_strlen_zero(p->context))
11241       len += strlen(p->context) + 1; /* Allow for seperator */
11242 
11243    if (!(mwi_sub = ast_calloc(1, len)))
11244       return -1;
11245 
11246    mwi_sub->uniqueid = p->uniqueid;
11247    if (!ast_strlen_zero(p->mailbox))
11248       strcpy(mwi_sub->mailbox, p->mailbox);
11249 
11250    if (!ast_strlen_zero(p->context)) {
11251       strcat(mwi_sub->mailbox, "@");
11252       strcat(mwi_sub->mailbox, p->context);
11253    }
11254 
11255    AST_RWLIST_WRLOCK(&mwi_subs);
11256    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11257    AST_RWLIST_UNLOCK(&mwi_subs);
11258    ast_free((void *) p->mailbox);
11259    ast_free((void *) p->context);
11260    ast_free(p);
11261    poll_subscribed_mailbox(mwi_sub);
11262    return 0;
11263 }
11264 
11265 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11266 {
11267    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11268    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11269       return;
11270 
11271    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11272       return;
11273 
11274    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11275    *uniqueid = u;
11276    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11277       ast_free(uniqueid);
11278    }
11279 }
11280 
11281 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11282 {
11283    struct mwi_sub_task *mwist;
11284    
11285    if (ast_event_get_type(event) != AST_EVENT_SUB)
11286       return;
11287 
11288    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11289       return;
11290 
11291    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11292       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11293       return;
11294    }
11295    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11296    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11297    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11298    
11299    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11300       ast_free(mwist);
11301    }
11302 }
11303 
11304 static void start_poll_thread(void)
11305 {
11306    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11307       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11308       AST_EVENT_IE_END);
11309 
11310    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11311       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11312       AST_EVENT_IE_END);
11313 
11314    if (mwi_sub_sub)
11315       ast_event_report_subs(mwi_sub_sub);
11316 
11317    poll_thread_run = 1;
11318 
11319    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11320 }
11321 
11322 static void stop_poll_thread(void)
11323 {
11324    poll_thread_run = 0;
11325 
11326    if (mwi_sub_sub) {
11327       ast_event_unsubscribe(mwi_sub_sub);
11328       mwi_sub_sub = NULL;
11329    }
11330 
11331    if (mwi_unsub_sub) {
11332       ast_event_unsubscribe(mwi_unsub_sub);
11333       mwi_unsub_sub = NULL;
11334    }
11335 
11336    ast_mutex_lock(&poll_lock);
11337    ast_cond_signal(&poll_cond);
11338    ast_mutex_unlock(&poll_lock);
11339 
11340    pthread_join(poll_thread, NULL);
11341 
11342    poll_thread = AST_PTHREADT_NULL;
11343 }
11344 
11345 /*! \brief Manager list voicemail users command */
11346 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11347 {
11348    struct ast_vm_user *vmu = NULL;
11349    const char *id = astman_get_header(m, "ActionID");
11350    char actionid[128] = "";
11351 
11352    if (!ast_strlen_zero(id))
11353       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11354 
11355    AST_LIST_LOCK(&users);
11356 
11357    if (AST_LIST_EMPTY(&users)) {
11358       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11359       AST_LIST_UNLOCK(&users);
11360       return RESULT_SUCCESS;
11361    }
11362    
11363    astman_send_ack(s, m, "Voicemail user list will follow");
11364    
11365    AST_LIST_TRAVERSE(&users, vmu, list) {
11366       char dirname[256];
11367 
11368 #ifdef IMAP_STORAGE
11369       int new, old;
11370       inboxcount(vmu->mailbox, &new, &old);
11371 #endif
11372       
11373       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11374       astman_append(s,
11375          "%s"
11376          "Event: VoicemailUserEntry\r\n"
11377          "VMContext: %s\r\n"
11378          "VoiceMailbox: %s\r\n"
11379          "Fullname: %s\r\n"
11380          "Email: %s\r\n"
11381          "Pager: %s\r\n"
11382          "ServerEmail: %s\r\n"
11383          "MailCommand: %s\r\n"
11384          "Language: %s\r\n"
11385          "TimeZone: %s\r\n"
11386          "Callback: %s\r\n"
11387          "Dialout: %s\r\n"
11388          "UniqueID: %s\r\n"
11389          "ExitContext: %s\r\n"
11390          "SayDurationMinimum: %d\r\n"
11391          "SayEnvelope: %s\r\n"
11392          "SayCID: %s\r\n"
11393          "AttachMessage: %s\r\n"
11394          "AttachmentFormat: %s\r\n"
11395          "DeleteMessage: %s\r\n"
11396          "VolumeGain: %.2f\r\n"
11397          "CanReview: %s\r\n"
11398          "CallOperator: %s\r\n"
11399          "MaxMessageCount: %d\r\n"
11400          "MaxMessageLength: %d\r\n"
11401          "NewMessageCount: %d\r\n"
11402 #ifdef IMAP_STORAGE
11403          "OldMessageCount: %d\r\n"
11404          "IMAPUser: %s\r\n"
11405 #endif
11406          "\r\n",
11407          actionid,
11408          vmu->context,
11409          vmu->mailbox,
11410          vmu->fullname,
11411          vmu->email,
11412          vmu->pager,
11413          vmu->serveremail,
11414          vmu->mailcmd,
11415          vmu->language,
11416          vmu->zonetag,
11417          vmu->callback,
11418          vmu->dialout,
11419          vmu->uniqueid,
11420          vmu->exit,
11421          vmu->saydurationm,
11422          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11423          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11424          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11425          vmu->attachfmt,
11426          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11427          vmu->volgain,
11428          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11429          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11430          vmu->maxmsg,
11431          vmu->maxsecs,
11432 #ifdef IMAP_STORAGE
11433          new, old, vmu->imapuser
11434 #else
11435          count_messages(vmu, dirname)
11436 #endif
11437          );
11438    }     
11439    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11440 
11441    AST_LIST_UNLOCK(&users);
11442 
11443    return RESULT_SUCCESS;
11444 }
11445 
11446 /*! \brief Free the users structure. */
11447 static void free_vm_users(void) 
11448 {
11449    struct ast_vm_user *current;
11450    AST_LIST_LOCK(&users);
11451    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11452       ast_set_flag(current, VM_ALLOCED);
11453       free_user(current);
11454    }
11455    AST_LIST_UNLOCK(&users);
11456 }
11457 
11458 /*! \brief Free the zones structure. */
11459 static void free_vm_zones(void)
11460 {
11461    struct vm_zone *zcur;
11462    AST_LIST_LOCK(&zones);
11463    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11464       free_zone(zcur);
11465    AST_LIST_UNLOCK(&zones);
11466 }
11467 
11468 static const char *substitute_escapes(const char *value)
11469 {
11470    char *current;
11471 
11472    /* Add 16 for fudge factor */
11473    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11474 
11475    ast_str_reset(str);
11476    
11477    /* Substitute strings \r, \n, and \t into the appropriate characters */
11478    for (current = (char *) value; *current; current++) {
11479       if (*current == '\\') {
11480          current++;
11481          if (!*current) {
11482             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11483             break;
11484          }
11485          switch (*current) {
11486          case 'r':
11487             ast_str_append(&str, 0, "\r");
11488             break;
11489          case 'n':
11490 #ifdef IMAP_STORAGE
11491             if (!str->used || str->str[str->used - 1] != '\r') {
11492                ast_str_append(&str, 0, "\r");
11493             }
11494 #endif
11495             ast_str_append(&str, 0, "\n");
11496             break;
11497          case 't':
11498             ast_str_append(&str, 0, "\t");
11499             break;
11500          default:
11501             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11502             break;
11503          }
11504       } else {
11505          ast_str_append(&str, 0, "%c", *current);
11506       }
11507    }
11508 
11509    return ast_str_buffer(str);
11510 }
11511 
11512 static int load_config(int reload)
11513 {
11514    struct ast_vm_user *current;
11515    struct ast_config *cfg, *ucfg;
11516    char *cat;
11517    struct ast_variable *var;
11518    const char *val;
11519    char *q, *stringp, *tmp;
11520    int x;
11521    int tmpadsi[4];
11522    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11523    char secretfn[PATH_MAX] = "";
11524 
11525    ast_unload_realtime("voicemail");
11526    ast_unload_realtime("voicemail_data");
11527 
11528    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11529       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11530          return 0;
11531       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11532          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11533          ucfg = NULL;
11534       }
11535       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11536       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11537          ast_config_destroy(ucfg);
11538          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11539          return 0;
11540       }
11541    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11542       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11543       return 0;
11544    } else {
11545       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11546       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11547          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11548          ucfg = NULL;
11549       }
11550    }
11551 #ifdef IMAP_STORAGE
11552    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11553 #endif
11554    /* set audio control prompts */
11555    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11556    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11557    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11558    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11559    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11560 
11561    /* Free all the users structure */  
11562    free_vm_users();
11563 
11564    /* Free all the zones structure */
11565    free_vm_zones();
11566 
11567    AST_LIST_LOCK(&users);  
11568 
11569    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11570    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11571 
11572    if (cfg) {
11573       /* General settings */
11574 
11575       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11576          val = "default";
11577       ast_copy_string(userscontext, val, sizeof(userscontext));
11578       /* Attach voice message to mail message ? */
11579       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11580          val = "yes";
11581       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11582 
11583       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11584          val = "no";
11585       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11586 
11587       volgain = 0.0;
11588       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11589          sscanf(val, "%30lf", &volgain);
11590 
11591 #ifdef ODBC_STORAGE
11592       strcpy(odbc_database, "asterisk");
11593       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11594          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11595       }
11596       strcpy(odbc_table, "voicemessages");
11597       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11598          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11599       }
11600 #endif      
11601       /* Mail command */
11602       strcpy(mailcmd, SENDMAIL);
11603       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11604          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11605 
11606       maxsilence = 0;
11607       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11608          maxsilence = atoi(val);
11609          if (maxsilence > 0)
11610             maxsilence *= 1000;
11611       }
11612       
11613       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11614          maxmsg = MAXMSG;
11615       } else {
11616          maxmsg = atoi(val);
11617          if (maxmsg < 0) {
11618             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11619             maxmsg = MAXMSG;
11620          } else if (maxmsg > MAXMSGLIMIT) {
11621             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11622             maxmsg = MAXMSGLIMIT;
11623          }
11624       }
11625 
11626       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11627          maxdeletedmsg = 0;
11628       } else {
11629          if (sscanf(val, "%30d", &x) == 1)
11630             maxdeletedmsg = x;
11631          else if (ast_true(val))
11632             maxdeletedmsg = MAXMSG;
11633          else
11634             maxdeletedmsg = 0;
11635 
11636          if (maxdeletedmsg < 0) {
11637             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11638             maxdeletedmsg = MAXMSG;
11639          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11640             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11641             maxdeletedmsg = MAXMSGLIMIT;
11642          }
11643       }
11644 
11645       /* Load date format config for voicemail mail */
11646       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11647          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11648       }
11649 
11650       /* Load date format config for voicemail pager mail */
11651       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11652          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11653       }
11654 
11655       /* External password changing command */
11656       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11657          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11658          pwdchange = PWDCHANGE_EXTERNAL;
11659       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11660          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11661          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11662       }
11663  
11664       /* External password validation command */
11665       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11666          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11667          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11668       }
11669 
11670 #ifdef IMAP_STORAGE
11671       /* IMAP server address */
11672       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11673          ast_copy_string(imapserver, val, sizeof(imapserver));
11674       } else {
11675          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11676       }
11677       /* IMAP server port */
11678       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11679          ast_copy_string(imapport, val, sizeof(imapport));
11680       } else {
11681          ast_copy_string(imapport, "143", sizeof(imapport));
11682       }
11683       /* IMAP server flags */
11684       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11685          ast_copy_string(imapflags, val, sizeof(imapflags));
11686       }
11687       /* IMAP server master username */
11688       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11689          ast_copy_string(authuser, val, sizeof(authuser));
11690       }
11691       /* IMAP server master password */
11692       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11693          ast_copy_string(authpassword, val, sizeof(authpassword));
11694       }
11695       /* Expunge on exit */
11696       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11697          if (ast_false(val))
11698             expungeonhangup = 0;
11699          else
11700             expungeonhangup = 1;
11701       } else {
11702          expungeonhangup = 1;
11703       }
11704       /* IMAP voicemail folder */
11705       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11706          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11707       } else {
11708          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11709       }
11710       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11711          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11712       }
11713       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11714          imapgreetings = ast_true(val);
11715       } else {
11716          imapgreetings = 0;
11717       }
11718       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11719          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11720       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11721          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11722          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11723       } else {
11724          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11725       }
11726 
11727       /* There is some very unorthodox casting done here. This is due
11728        * to the way c-client handles the argument passed in. It expects a 
11729        * void pointer and casts the pointer directly to a long without
11730        * first dereferencing it. */
11731       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11732          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11733       } else {
11734          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11735       }
11736 
11737       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11738          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11739       } else {
11740          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11741       }
11742 
11743       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11744          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11745       } else {
11746          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11747       }
11748 
11749       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11750          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11751       } else {
11752          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11753       }
11754 
11755       /* Increment configuration version */
11756       imapversion++;
11757 #endif
11758       /* External voicemail notify application */
11759       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11760          ast_copy_string(externnotify, val, sizeof(externnotify));
11761          ast_debug(1, "found externnotify: %s\n", externnotify);
11762       } else {
11763          externnotify[0] = '\0';
11764       }
11765 
11766       /* SMDI voicemail notification */
11767       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11768          ast_debug(1, "Enabled SMDI voicemail notification\n");
11769          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11770             smdi_iface = ast_smdi_interface_find(val);
11771          } else {
11772             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11773             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
11774          }
11775          if (!smdi_iface) {
11776             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11777          } 
11778       }
11779 
11780       /* Silence treshold */
11781       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11782       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11783          silencethreshold = atoi(val);
11784       
11785       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11786          val = ASTERISK_USERNAME;
11787       ast_copy_string(serveremail, val, sizeof(serveremail));
11788       
11789       vmmaxsecs = 0;
11790       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11791          if (sscanf(val, "%30d", &x) == 1) {
11792             vmmaxsecs = x;
11793          } else {
11794             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11795          }
11796       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11797          static int maxmessage_deprecate = 0;
11798          if (maxmessage_deprecate == 0) {
11799             maxmessage_deprecate = 1;
11800             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11801          }
11802          if (sscanf(val, "%30d", &x) == 1) {
11803             vmmaxsecs = x;
11804          } else {
11805             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11806          }
11807       }
11808 
11809       vmminsecs = 0;
11810       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11811          if (sscanf(val, "%30d", &x) == 1) {
11812             vmminsecs = x;
11813             if (maxsilence / 1000 >= vmminsecs) {
11814                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11815             }
11816          } else {
11817             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11818          }
11819       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11820          static int maxmessage_deprecate = 0;
11821          if (maxmessage_deprecate == 0) {
11822             maxmessage_deprecate = 1;
11823             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11824          }
11825          if (sscanf(val, "%30d", &x) == 1) {
11826             vmminsecs = x;
11827             if (maxsilence / 1000 >= vmminsecs) {
11828                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11829             }
11830          } else {
11831             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11832          }
11833       }
11834 
11835       val = ast_variable_retrieve(cfg, "general", "format");
11836       if (!val) {
11837          val = "wav";   
11838       } else {
11839          tmp = ast_strdupa(val);
11840          val = ast_format_str_reduce(tmp);
11841          if (!val) {
11842             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11843             val = "wav";
11844          }
11845       }
11846       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11847 
11848       skipms = 3000;
11849       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11850          if (sscanf(val, "%30d", &x) == 1) {
11851             maxgreet = x;
11852          } else {
11853             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11854          }
11855       }
11856 
11857       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11858          if (sscanf(val, "%30d", &x) == 1) {
11859             skipms = x;
11860          } else {
11861             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11862          }
11863       }
11864 
11865       maxlogins = 3;
11866       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11867          if (sscanf(val, "%30d", &x) == 1) {
11868             maxlogins = x;
11869          } else {
11870             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11871          }
11872       }
11873 
11874       minpassword = MINPASSWORD;
11875       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11876          if (sscanf(val, "%30d", &x) == 1) {
11877             minpassword = x;
11878          } else {
11879             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11880          }
11881       }
11882 
11883       /* Force new user to record name ? */
11884       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11885          val = "no";
11886       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11887 
11888       /* Force new user to record greetings ? */
11889       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11890          val = "no";
11891       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11892 
11893       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11894          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11895          stringp = ast_strdupa(val);
11896          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11897             if (!ast_strlen_zero(stringp)) {
11898                q = strsep(&stringp, ",");
11899                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11900                   q++;
11901                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11902                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11903             } else {
11904                cidinternalcontexts[x][0] = '\0';
11905             }
11906          }
11907       }
11908       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11909          ast_debug(1, "VM Review Option disabled globally\n");
11910          val = "no";
11911       }
11912       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11913 
11914       /* Temporary greeting reminder */
11915       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11916          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11917          val = "no";
11918       } else {
11919          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11920       }
11921       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11922       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11923          ast_debug(1, "VM next message wrap disabled globally\n");
11924          val = "no";
11925       }
11926       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11927 
11928       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11929          ast_debug(1, "VM Operator break disabled globally\n");
11930          val = "no";
11931       }
11932       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11933 
11934       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11935          ast_debug(1, "VM CID Info before msg disabled globally\n");
11936          val = "no";
11937       } 
11938       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11939 
11940       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
11941          ast_debug(1, "Send Voicemail msg disabled globally\n");
11942          val = "no";
11943       }
11944       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11945    
11946       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11947          ast_debug(1, "ENVELOPE before msg enabled globally\n");
11948          val = "yes";
11949       }
11950       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11951 
11952       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11953          ast_debug(1, "Move Heard enabled globally\n");
11954          val = "yes";
11955       }
11956       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11957 
11958       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11959          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11960          val = "no";
11961       }
11962       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11963 
11964       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11965          ast_debug(1, "Duration info before msg enabled globally\n");
11966          val = "yes";
11967       }
11968       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11969 
11970       saydurationminfo = 2;
11971       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11972          if (sscanf(val, "%30d", &x) == 1) {
11973             saydurationminfo = x;
11974          } else {
11975             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11976          }
11977       }
11978 
11979       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11980          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
11981          val = "no";
11982       }
11983       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11984 
11985       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11986          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11987          ast_debug(1, "found dialout context: %s\n", dialcontext);
11988       } else {
11989          dialcontext[0] = '\0';  
11990       }
11991       
11992       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11993          ast_copy_string(callcontext, val, sizeof(callcontext));
11994          ast_debug(1, "found callback context: %s\n", callcontext);
11995       } else {
11996          callcontext[0] = '\0';
11997       }
11998 
11999       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12000          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12001          ast_debug(1, "found operator context: %s\n", exitcontext);
12002       } else {
12003          exitcontext[0] = '\0';
12004       }
12005       
12006       /* load password sounds configuration */
12007       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12008          ast_copy_string(vm_password, val, sizeof(vm_password));
12009       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12010          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12011       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12012          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12013       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12014          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12015       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12016          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12017       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12018          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12019       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12020          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12021       }
12022       /* load configurable audio prompts */
12023       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12024          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12025       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12026          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12027       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12028          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12029       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12030          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12031       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12032          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12033 
12034       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12035          val = "no";
12036       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12037 
12038       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12039          val = "voicemail.conf";
12040       }
12041       if (!(strcmp(val, "spooldir"))) {
12042          passwordlocation = OPT_PWLOC_SPOOLDIR;
12043       } else {
12044          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12045       }
12046 
12047       poll_freq = DEFAULT_POLL_FREQ;
12048       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12049          if (sscanf(val, "%30u", &poll_freq) != 1) {
12050             poll_freq = DEFAULT_POLL_FREQ;
12051             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12052          }
12053       }
12054 
12055       poll_mailboxes = 0;
12056       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12057          poll_mailboxes = ast_true(val);
12058 
12059       if (ucfg) { 
12060          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12061             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12062                continue;
12063             if ((current = find_or_create(userscontext, cat))) {
12064                populate_defaults(current);
12065                apply_options_full(current, ast_variable_browse(ucfg, cat));
12066                ast_copy_string(current->context, userscontext, sizeof(current->context));
12067                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12068                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12069                }
12070 
12071                switch (current->passwordlocation) {
12072                case OPT_PWLOC_SPOOLDIR:
12073                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12074                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12075                }
12076             }
12077          }
12078          ast_config_destroy(ucfg);
12079       }
12080       cat = ast_category_browse(cfg, NULL);
12081       while (cat) {
12082          if (strcasecmp(cat, "general")) {
12083             var = ast_variable_browse(cfg, cat);
12084             if (strcasecmp(cat, "zonemessages")) {
12085                /* Process mailboxes in this context */
12086                while (var) {
12087                   append_mailbox(cat, var->name, var->value);
12088                   var = var->next;
12089                }
12090             } else {
12091                /* Timezones in this context */
12092                while (var) {
12093                   struct vm_zone *z;
12094                   if ((z = ast_malloc(sizeof(*z)))) {
12095                      char *msg_format, *tzone;
12096                      msg_format = ast_strdupa(var->value);
12097                      tzone = strsep(&msg_format, "|,");
12098                      if (msg_format) {
12099                         ast_copy_string(z->name, var->name, sizeof(z->name));
12100                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12101                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12102                         AST_LIST_LOCK(&zones);
12103                         AST_LIST_INSERT_HEAD(&zones, z, list);
12104                         AST_LIST_UNLOCK(&zones);
12105                      } else {
12106                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12107                         ast_free(z);
12108                      }
12109                   } else {
12110                      AST_LIST_UNLOCK(&users);
12111                      ast_config_destroy(cfg);
12112                      return -1;
12113                   }
12114                   var = var->next;
12115                }
12116             }
12117          }
12118          cat = ast_category_browse(cfg, cat);
12119       }
12120       memset(fromstring, 0, sizeof(fromstring));
12121       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12122       strcpy(charset, "ISO-8859-1");
12123       if (emailbody) {
12124          ast_free(emailbody);
12125          emailbody = NULL;
12126       }
12127       if (emailsubject) {
12128          ast_free(emailsubject);
12129          emailsubject = NULL;
12130       }
12131       if (pagerbody) {
12132          ast_free(pagerbody);
12133          pagerbody = NULL;
12134       }
12135       if (pagersubject) {
12136          ast_free(pagersubject);
12137          pagersubject = NULL;
12138       }
12139       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12140          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12141       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12142          ast_copy_string(fromstring, val, sizeof(fromstring));
12143       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12144          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12145       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12146          ast_copy_string(charset, val, sizeof(charset));
12147       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12148          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12149          for (x = 0; x < 4; x++) {
12150             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12151          }
12152       }
12153       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12154          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12155          for (x = 0; x < 4; x++) {
12156             memcpy(&adsisec[x], &tmpadsi[x], 1);
12157          }
12158       }
12159       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12160          if (atoi(val)) {
12161             adsiver = atoi(val);
12162          }
12163       }
12164       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12165          ast_copy_string(zonetag, val, sizeof(zonetag));
12166       }
12167       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12168          ast_copy_string(locale, val, sizeof(locale));
12169       }
12170       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12171          emailsubject = ast_strdup(val);
12172       }
12173       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12174          emailbody = ast_strdup(substitute_escapes(val));
12175       }
12176       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12177          pagersubject = ast_strdup(val);
12178       }
12179       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12180          pagerbody = ast_strdup(substitute_escapes(val));
12181       }
12182       AST_LIST_UNLOCK(&users);
12183       ast_config_destroy(cfg);
12184 
12185       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12186          start_poll_thread();
12187       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12188          stop_poll_thread();;
12189 
12190       return 0;
12191    } else {
12192       AST_LIST_UNLOCK(&users);
12193       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12194       if (ucfg)
12195          ast_config_destroy(ucfg);
12196       return 0;
12197    }
12198 }
12199 
12200 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12201 {
12202    int res = -1;
12203    char dir[PATH_MAX];
12204    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12205    ast_debug(2, "About to try retrieving name file %s\n", dir);
12206    RETRIEVE(dir, -1, mailbox, context);
12207    if (ast_fileexists(dir, NULL, NULL)) {
12208       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12209    }
12210    DISPOSE(dir, -1);
12211    return res;
12212 }
12213 
12214 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12215    struct ast_config *pwconf;
12216    struct ast_flags config_flags = { 0 };
12217 
12218    pwconf = ast_config_load(secretfn, config_flags);
12219    if (pwconf) {
12220       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12221       if (val) {
12222          ast_copy_string(password, val, passwordlen);
12223          return;
12224       }
12225    }
12226    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12227 }
12228 
12229 static int write_password_to_file(const char *secretfn, const char *password) {
12230    struct ast_config *conf;
12231    struct ast_category *cat;
12232    struct ast_variable *var;
12233 
12234    if (!(conf=ast_config_new())) {
12235       ast_log(LOG_ERROR, "Error creating new config structure\n");
12236       return -1;
12237    }
12238    if (!(cat=ast_category_new("general","",1))) {
12239       ast_log(LOG_ERROR, "Error creating new category structure\n");
12240       return -1;
12241    }
12242    if (!(var=ast_variable_new("password",password,""))) {
12243       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12244       return -1;
12245    }
12246    ast_category_append(conf,cat);
12247    ast_variable_append(cat,var);
12248    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12249       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12250       return -1;
12251    }
12252    return 0;
12253 }
12254 
12255 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12256 {
12257    char *context;
12258    char *args_copy;
12259    int res;
12260 
12261    if (ast_strlen_zero(data)) {
12262       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12263       return -1;
12264    }
12265 
12266    args_copy = ast_strdupa(data);
12267    if ((context = strchr(args_copy, '@'))) {
12268       *context++ = '\0';
12269    } else {
12270       context = "default";
12271    }
12272 
12273    if ((res = sayname(chan, args_copy, context) < 0)) {
12274       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12275       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12276       if (!res) {
12277          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12278       }
12279    }
12280 
12281    return res;
12282 }
12283 
12284 #ifdef TEST_FRAMEWORK
12285 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12286 {
12287    return 0;
12288 }
12289 
12290 static struct ast_frame *fake_read(struct ast_channel *ast)
12291 {
12292    return &ast_null_frame;
12293 }
12294 
12295 AST_TEST_DEFINE(test_voicemail_vmsayname)
12296 {
12297    char dir[PATH_MAX];
12298    char dir2[PATH_MAX];
12299    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12300    static const char TEST_EXTENSION[] = "1234";
12301 
12302    struct ast_channel *test_channel1 = NULL;
12303    int res = -1;
12304 
12305    static const struct ast_channel_tech fake_tech = {
12306       .write = fake_write,
12307       .read = fake_read,
12308    };
12309 
12310    switch (cmd) {
12311    case TEST_INIT:
12312       info->name = "vmsayname_exec";
12313       info->category = "/apps/app_voicemail/";
12314       info->summary = "Vmsayname unit test";
12315       info->description =
12316          "This tests passing various parameters to vmsayname";
12317       return AST_TEST_NOT_RUN;
12318    case TEST_EXECUTE:
12319       break;
12320    }
12321 
12322    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12323         NULL, NULL, 0, 0, "TestChannel1"))) {
12324       goto exit_vmsayname_test;
12325    }
12326 
12327    /* normally this is done in the channel driver */
12328    test_channel1->nativeformats = AST_FORMAT_GSM;
12329    test_channel1->writeformat = AST_FORMAT_GSM;
12330    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12331    test_channel1->readformat = AST_FORMAT_GSM;
12332    test_channel1->rawreadformat = AST_FORMAT_GSM;
12333    test_channel1->tech = &fake_tech;
12334 
12335    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12336    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12337    if (!(res = vmsayname_exec(test_channel1, dir))) {
12338       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12339       if (ast_fileexists(dir, NULL, NULL)) {
12340          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12341          res = -1;
12342          goto exit_vmsayname_test;
12343       } else {
12344          /* no greeting already exists as expected, let's create one to fully test sayname */
12345          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12346             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12347             goto exit_vmsayname_test;
12348          }
12349          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12350          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12351          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12352          if ((res = symlink(dir, dir2))) {
12353             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12354             goto exit_vmsayname_test;
12355          }
12356          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12357          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12358          res = vmsayname_exec(test_channel1, dir);
12359 
12360          /* TODO: there may be a better way to do this */
12361          unlink(dir2);
12362          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12363          rmdir(dir2);
12364          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12365          rmdir(dir2);
12366       }
12367    }
12368 
12369 exit_vmsayname_test:
12370 
12371    if (test_channel1) {
12372       ast_hangup(test_channel1);
12373    }
12374 
12375    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12376 }
12377 
12378 AST_TEST_DEFINE(test_voicemail_msgcount)
12379 {
12380    int i, j, res = AST_TEST_PASS, syserr;
12381    struct ast_vm_user *vmu;
12382    struct vm_state vms;
12383 #ifdef IMAP_STORAGE
12384    struct ast_channel *chan = NULL;
12385 #endif
12386    struct {
12387       char dir[256];
12388       char file[256];
12389       char txtfile[256];
12390    } tmp[3];
12391    char syscmd[256];
12392    const char origweasels[] = "tt-weasels";
12393    const char testcontext[] = "test";
12394    const char testmailbox[] = "00000000";
12395    const char testspec[] = "00000000@test";
12396    FILE *txt;
12397    int new, old, urgent;
12398    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12399    const int folder2mbox[3] = { 1, 11, 0 };
12400    const int expected_results[3][12] = {
12401       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12402       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12403       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12404       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12405    };
12406 
12407    switch (cmd) {
12408    case TEST_INIT:
12409       info->name = "test_voicemail_msgcount";
12410       info->category = "/apps/app_voicemail/";
12411       info->summary = "Test Voicemail status checks";
12412       info->description =
12413          "Verify that message counts are correct when retrieved through the public API";
12414       return AST_TEST_NOT_RUN;
12415    case TEST_EXECUTE:
12416       break;
12417    }
12418 
12419    /* Make sure the original path was completely empty */
12420    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12421    if ((syserr = ast_safe_system(syscmd))) {
12422       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12423          syserr > 0 ? strerror(syserr) : "unable to fork()");
12424       return AST_TEST_FAIL;
12425    }
12426 
12427 #ifdef IMAP_STORAGE
12428    if (!(chan = ast_dummy_channel_alloc())) {
12429       ast_test_status_update(test, "Unable to create dummy channel\n");
12430       return AST_TEST_FAIL;
12431    }
12432 #endif
12433 
12434    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12435       !(vmu = find_or_create(testcontext, testmailbox))) {
12436       ast_test_status_update(test, "Cannot create vmu structure\n");
12437       ast_unreplace_sigchld();
12438       return AST_TEST_FAIL;
12439    }
12440 
12441    populate_defaults(vmu);
12442    memset(&vms, 0, sizeof(vms));
12443 
12444    /* Create temporary voicemail */
12445    for (i = 0; i < 3; i++) {
12446       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12447       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12448       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12449 
12450       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12451          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12452             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12453          if ((syserr = ast_safe_system(syscmd))) {
12454             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12455                syserr > 0 ? strerror(syserr) : "unable to fork()");
12456             ast_unreplace_sigchld();
12457             return AST_TEST_FAIL;
12458          }
12459       }
12460 
12461       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12462          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12463          fclose(txt);
12464       } else {
12465          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12466          res = AST_TEST_FAIL;
12467          break;
12468       }
12469       open_mailbox(&vms, vmu, folder2mbox[i]);
12470       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12471 
12472       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12473       for (j = 0; j < 3; j++) {
12474          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12475          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12476             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12477                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12478             res = AST_TEST_FAIL;
12479          }
12480       }
12481 
12482       new = old = urgent = 0;
12483       if (ast_app_inboxcount(testspec, &new, &old)) {
12484          ast_test_status_update(test, "inboxcount returned failure\n");
12485          res = AST_TEST_FAIL;
12486       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12487          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12488             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12489          res = AST_TEST_FAIL;
12490       }
12491 
12492       new = old = urgent = 0;
12493       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12494          ast_test_status_update(test, "inboxcount2 returned failure\n");
12495          res = AST_TEST_FAIL;
12496       } else if (old != expected_results[i][6 + 0] ||
12497             urgent != expected_results[i][6 + 1] ||
12498                new != expected_results[i][6 + 2]    ) {
12499          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12500             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12501          res = AST_TEST_FAIL;
12502       }
12503 
12504       new = old = urgent = 0;
12505       for (j = 0; j < 3; j++) {
12506          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12507             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12508                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12509             res = AST_TEST_FAIL;
12510          }
12511       }
12512    }
12513 
12514    for (i = 0; i < 3; i++) {
12515       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12516        * server, in which case, the rm below will not affect the
12517        * voicemails. */
12518       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12519       DISPOSE(tmp[i].dir, 0);
12520    }
12521 
12522    if (vms.deleted) {
12523       ast_free(vms.deleted);
12524    }
12525    if (vms.heard) {
12526       ast_free(vms.heard);
12527    }
12528 
12529 #ifdef IMAP_STORAGE
12530    chan = ast_channel_release(chan);
12531 #endif
12532 
12533    /* And remove test directory */
12534    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12535    if ((syserr = ast_safe_system(syscmd))) {
12536       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12537          syserr > 0 ? strerror(syserr) : "unable to fork()");
12538    }
12539 
12540    return res;
12541 }
12542 
12543 AST_TEST_DEFINE(test_voicemail_notify_endl)
12544 {
12545    int res = AST_TEST_PASS;
12546    char testcontext[] = "test";
12547    char testmailbox[] = "00000000";
12548    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12549    char attach[256], attach2[256];
12550    char buf[256] = ""; /* No line should actually be longer than 80 */
12551    struct ast_channel *chan = NULL;
12552    struct ast_vm_user *vmu, vmus = {
12553       .flags = 0,
12554    };
12555    FILE *file;
12556    struct {
12557       char *name;
12558       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12559       void *location;
12560       union {
12561          int intval;
12562          char *strval;
12563       } u;
12564    } test_items[] = {
12565       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12566       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12567       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12568       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12569       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12570       { "attach2", STRPTR, attach2, .u.strval = "" },
12571       { "attach", STRPTR, attach, .u.strval = "" },
12572    };
12573    int which;
12574 
12575    switch (cmd) {
12576    case TEST_INIT:
12577       info->name = "test_voicemail_notify_endl";
12578       info->category = "/apps/app_voicemail/";
12579       info->summary = "Test Voicemail notification end-of-line";
12580       info->description =
12581          "Verify that notification emails use a consistent end-of-line character";
12582       return AST_TEST_NOT_RUN;
12583    case TEST_EXECUTE:
12584       break;
12585    }
12586 
12587    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12588    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12589 
12590    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12591       !(vmu = find_or_create(testcontext, testmailbox))) {
12592       ast_test_status_update(test, "Cannot create vmu structure\n");
12593       return AST_TEST_NOT_RUN;
12594    }
12595 
12596    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12597       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12598       return AST_TEST_NOT_RUN;
12599    }
12600 
12601    populate_defaults(vmu);
12602    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12603 #ifdef IMAP_STORAGE
12604    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12605 #endif
12606 
12607    file = tmpfile();
12608    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12609       /* Kill previous test, if any */
12610       rewind(file);
12611       if (ftruncate(fileno(file), 0)) {
12612          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12613          res = AST_TEST_FAIL;
12614          break;
12615       }
12616 
12617       /* Make each change, in order, to the test mailbox */
12618       if (test_items[which].type == INT) {
12619          *((int *) test_items[which].location) = test_items[which].u.intval;
12620       } else if (test_items[which].type == FLAGVAL) {
12621          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12622             ast_clear_flag(vmu, test_items[which].u.intval);
12623          } else {
12624             ast_set_flag(vmu, test_items[which].u.intval);
12625          }
12626       } else if (test_items[which].type == STATIC) {
12627          strcpy(test_items[which].location, test_items[which].u.strval);
12628       } else if (test_items[which].type == STRPTR) {
12629          test_items[which].location = test_items[which].u.strval;
12630       }
12631 
12632       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12633       rewind(file);
12634       while (fgets(buf, sizeof(buf), file)) {
12635          if (
12636 #ifdef IMAP_STORAGE
12637          buf[strlen(buf) - 2] != '\r'
12638 #else
12639          buf[strlen(buf) - 2] == '\r'
12640 #endif
12641          || buf[strlen(buf) - 1] != '\n') {
12642             res = AST_TEST_FAIL;
12643          }
12644       }
12645    }
12646    fclose(file);
12647    return res;
12648 }
12649 #endif /* defined(TEST_FRAMEWORK) */
12650 
12651 static int reload(void)
12652 {
12653    return load_config(1);
12654 }
12655 
12656 static int unload_module(void)
12657 {
12658    int res;
12659 
12660    res = ast_unregister_application(app);
12661    res |= ast_unregister_application(app2);
12662    res |= ast_unregister_application(app3);
12663    res |= ast_unregister_application(app4);
12664    res |= ast_unregister_application(sayname_app);
12665    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12666    res |= ast_manager_unregister("VoicemailUsersList");
12667    res |= ast_data_unregister(NULL);
12668 #ifdef TEST_FRAMEWORK
12669    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12670    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12671    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12672    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12673 #endif
12674    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12675    ast_uninstall_vm_functions();
12676    ao2_ref(inprocess_container, -1);
12677 
12678    if (poll_thread != AST_PTHREADT_NULL)
12679       stop_poll_thread();
12680 
12681    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12682    ast_unload_realtime("voicemail");
12683    ast_unload_realtime("voicemail_data");
12684 
12685    free_vm_users();
12686    free_vm_zones();
12687    return res;
12688 }
12689 
12690 static int load_module(void)
12691 {
12692    int res;
12693    my_umask = umask(0);
12694    umask(my_umask);
12695 
12696    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12697       return AST_MODULE_LOAD_DECLINE;
12698    }
12699 
12700    /* compute the location of the voicemail spool directory */
12701    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12702    
12703    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12704       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12705    }
12706 
12707    if ((res = load_config(0)))
12708       return res;
12709 
12710    res = ast_register_application_xml(app, vm_exec);
12711    res |= ast_register_application_xml(app2, vm_execmain);
12712    res |= ast_register_application_xml(app3, vm_box_exists);
12713    res |= ast_register_application_xml(app4, vmauthenticate);
12714    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12715    res |= ast_custom_function_register(&mailbox_exists_acf);
12716    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12717 #ifdef TEST_FRAMEWORK
12718    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12719    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12720    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12721    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12722 #endif
12723 
12724    if (res)
12725       return res;
12726 
12727    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12728    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12729 
12730    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12731    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12732    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12733 
12734    return res;
12735 }
12736 
12737 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
12738 {
12739    int cmd = 0;
12740    char destination[80] = "";
12741    int retries = 0;
12742 
12743    if (!num) {
12744       ast_verb(3, "Destination number will be entered manually\n");
12745       while (retries < 3 && cmd != 't') {
12746          destination[1] = '\0';
12747          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
12748          if (!cmd)
12749             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
12750          if (!cmd)
12751             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
12752          if (!cmd) {
12753             cmd = ast_waitfordigit(chan, 6000);
12754             if (cmd)
12755                destination[0] = cmd;
12756          }
12757          if (!cmd) {
12758             retries++;
12759          } else {
12760 
12761             if (cmd < 0)
12762                return 0;
12763             if (cmd == '*') {
12764                ast_verb(3, "User hit '*' to cancel outgoing call\n");
12765                return 0;
12766             }
12767             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
12768                retries++;
12769             else
12770                cmd = 't';
12771          }
12772       }
12773       if (retries >= 3) {
12774          return 0;
12775       }
12776       
12777    } else {
12778       if (option_verbose > 2)
12779          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
12780       ast_copy_string(destination, num, sizeof(destination));
12781    }
12782 
12783    if (!ast_strlen_zero(destination)) {
12784       if (destination[strlen(destination) -1 ] == '*')
12785          return 0; 
12786       if (option_verbose > 2)
12787          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
12788       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
12789       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
12790       chan->priority = 0;
12791       return 9;
12792    }
12793    return 0;
12794 }
12795 
12796 /*!
12797  * \brief The advanced options within a message.
12798  * \param chan
12799  * \param vmu 
12800  * \param vms
12801  * \param msg
12802  * \param option
12803  * \param record_gain
12804  *
12805  * Provides handling for the play message envelope, call the person back, or reply to message. 
12806  *
12807  * \return zero on success, -1 on error.
12808  */
12809 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)
12810 {
12811    int res = 0;
12812    char filename[PATH_MAX];
12813    struct ast_config *msg_cfg = NULL;
12814    const char *origtime, *context;
12815    char *name, *num;
12816    int retries = 0;
12817    char *cid;
12818    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
12819 
12820    vms->starting = 0; 
12821 
12822    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12823 
12824    /* Retrieve info from VM attribute file */
12825    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
12826    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
12827    msg_cfg = ast_config_load(filename, config_flags);
12828    DISPOSE(vms->curdir, vms->curmsg);
12829    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
12830       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
12831       return 0;
12832    }
12833 
12834    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
12835       ast_config_destroy(msg_cfg);
12836       return 0;
12837    }
12838 
12839    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
12840 
12841    context = ast_variable_retrieve(msg_cfg, "message", "context");
12842    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
12843       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
12844    switch (option) {
12845    case 3: /* Play message envelope */
12846       if (!res)
12847          res = play_message_datetime(chan, vmu, origtime, filename);
12848       if (!res)
12849          res = play_message_callerid(chan, vms, cid, context, 0);
12850 
12851       res = 't';
12852       break;
12853 
12854    case 2:  /* Call back */
12855 
12856       if (ast_strlen_zero(cid))
12857          break;
12858 
12859       ast_callerid_parse(cid, &name, &num);
12860       while ((res > -1) && (res != 't')) {
12861          switch (res) {
12862          case '1':
12863             if (num) {
12864                /* Dial the CID number */
12865                res = dialout(chan, vmu, num, vmu->callback);
12866                if (res) {
12867                   ast_config_destroy(msg_cfg);
12868                   return 9;
12869                }
12870             } else {
12871                res = '2';
12872             }
12873             break;
12874 
12875          case '2':
12876             /* Want to enter a different number, can only do this if there's a dialout context for this user */
12877             if (!ast_strlen_zero(vmu->dialout)) {
12878                res = dialout(chan, vmu, NULL, vmu->dialout);
12879                if (res) {
12880                   ast_config_destroy(msg_cfg);
12881                   return 9;
12882                }
12883             } else {
12884                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
12885                res = ast_play_and_wait(chan, "vm-sorry");
12886             }
12887             ast_config_destroy(msg_cfg);
12888             return res;
12889          case '*':
12890             res = 't';
12891             break;
12892          case '3':
12893          case '4':
12894          case '5':
12895          case '6':
12896          case '7':
12897          case '8':
12898          case '9':
12899          case '0':
12900 
12901             res = ast_play_and_wait(chan, "vm-sorry");
12902             retries++;
12903             break;
12904          default:
12905             if (num) {
12906                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
12907                res = ast_play_and_wait(chan, "vm-num-i-have");
12908                if (!res)
12909                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
12910                if (!res)
12911                   res = ast_play_and_wait(chan, "vm-tocallnum");
12912                /* Only prompt for a caller-specified number if there is a dialout context specified */
12913                if (!ast_strlen_zero(vmu->dialout)) {
12914                   if (!res)
12915                      res = ast_play_and_wait(chan, "vm-calldiffnum");
12916                }
12917             } else {
12918                res = ast_play_and_wait(chan, "vm-nonumber");
12919                if (!ast_strlen_zero(vmu->dialout)) {
12920                   if (!res)
12921                      res = ast_play_and_wait(chan, "vm-toenternumber");
12922                }
12923             }
12924             if (!res)
12925                res = ast_play_and_wait(chan, "vm-star-cancel");
12926             if (!res)
12927                res = ast_waitfordigit(chan, 6000);
12928             if (!res) {
12929                retries++;
12930                if (retries > 3)
12931                   res = 't';
12932             }
12933             break; 
12934             
12935          }
12936          if (res == 't')
12937             res = 0;
12938          else if (res == '*')
12939             res = -1;
12940       }
12941       break;
12942       
12943    case 1:  /* Reply */
12944       /* Send reply directly to sender */
12945       if (ast_strlen_zero(cid))
12946          break;
12947 
12948       ast_callerid_parse(cid, &name, &num);
12949       if (!num) {
12950          ast_verb(3, "No CID number available, no reply sent\n");
12951          if (!res)
12952             res = ast_play_and_wait(chan, "vm-nonumber");
12953          ast_config_destroy(msg_cfg);
12954          return res;
12955       } else {
12956          struct ast_vm_user vmu2;
12957          if (find_user(&vmu2, vmu->context, num)) {
12958             struct leave_vm_options leave_options;
12959             char mailbox[AST_MAX_EXTENSION * 2 + 2];
12960             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
12961 
12962             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
12963             
12964             memset(&leave_options, 0, sizeof(leave_options));
12965             leave_options.record_gain = record_gain;
12966             res = leave_voicemail(chan, mailbox, &leave_options);
12967             if (!res)
12968                res = 't';
12969             ast_config_destroy(msg_cfg);
12970             return res;
12971          } else {
12972             /* Sender has no mailbox, can't reply */
12973             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
12974             ast_play_and_wait(chan, "vm-nobox");
12975             res = 't';
12976             ast_config_destroy(msg_cfg);
12977             return res;
12978          }
12979       } 
12980       res = 0;
12981 
12982       break;
12983    }
12984 
12985 #ifndef IMAP_STORAGE
12986    ast_config_destroy(msg_cfg);
12987 
12988    if (!res) {
12989       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12990       vms->heard[msg] = 1;
12991       res = wait_file(chan, vms, vms->fn);
12992    }
12993 #endif
12994    return res;
12995 }
12996 
12997 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
12998          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
12999          signed char record_gain, struct vm_state *vms, char *flag)
13000 {
13001    /* Record message & let caller review or re-record it, or set options if applicable */
13002    int res = 0;
13003    int cmd = 0;
13004    int max_attempts = 3;
13005    int attempts = 0;
13006    int recorded = 0;
13007    int msg_exists = 0;
13008    signed char zero_gain = 0;
13009    char tempfile[PATH_MAX];
13010    char *acceptdtmf = "#";
13011    char *canceldtmf = "";
13012    int canceleddtmf = 0;
13013 
13014    /* Note that urgent and private are for flagging messages as such in the future */
13015 
13016    /* barf if no pointer passed to store duration in */
13017    if (duration == NULL) {
13018       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13019       return -1;
13020    }
13021 
13022    if (!outsidecaller)
13023       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13024    else
13025       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13026 
13027    cmd = '3';  /* Want to start by recording */
13028 
13029    while ((cmd >= 0) && (cmd != 't')) {
13030       switch (cmd) {
13031       case '1':
13032          if (!msg_exists) {
13033             /* In this case, 1 is to record a message */
13034             cmd = '3';
13035             break;
13036          } else {
13037             /* Otherwise 1 is to save the existing message */
13038             ast_verb(3, "Saving message as is\n");
13039             if (!outsidecaller) 
13040                ast_filerename(tempfile, recordfile, NULL);
13041             ast_stream_and_wait(chan, "vm-msgsaved", "");
13042             if (!outsidecaller) {
13043                /* Saves to IMAP server only if imapgreeting=yes */
13044                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13045                DISPOSE(recordfile, -1);
13046             }
13047             cmd = 't';
13048             return res;
13049          }
13050       case '2':
13051          /* Review */
13052          ast_verb(3, "Reviewing the message\n");
13053          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13054          break;
13055       case '3':
13056          msg_exists = 0;
13057          /* Record */
13058          if (recorded == 1) 
13059             ast_verb(3, "Re-recording the message\n");
13060          else  
13061             ast_verb(3, "Recording the message\n");
13062          
13063          if (recorded && outsidecaller) {
13064             cmd = ast_play_and_wait(chan, INTRO);
13065             cmd = ast_play_and_wait(chan, "beep");
13066          }
13067          recorded = 1;
13068          /* 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 */
13069          if (record_gain)
13070             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13071          if (ast_test_flag(vmu, VM_OPERATOR))
13072             canceldtmf = "0";
13073          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13074          if (strchr(canceldtmf, cmd)) {
13075          /* need this flag here to distinguish between pressing '0' during message recording or after */
13076             canceleddtmf = 1;
13077          }
13078          if (record_gain)
13079             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13080          if (cmd == -1) {
13081             /* User has hung up, no options to give */
13082             if (!outsidecaller) {
13083                /* user was recording a greeting and they hung up, so let's delete the recording. */
13084                ast_filedelete(tempfile, NULL);
13085             }     
13086             return cmd;
13087          }
13088          if (cmd == '0') {
13089             break;
13090          } else if (cmd == '*') {
13091             break;
13092 #if 0
13093          } else if (vmu->review && (*duration < 5)) {
13094             /* Message is too short */
13095             ast_verb(3, "Message too short\n");
13096             cmd = ast_play_and_wait(chan, "vm-tooshort");
13097             cmd = ast_filedelete(tempfile, NULL);
13098             break;
13099          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13100             /* Message is all silence */
13101             ast_verb(3, "Nothing recorded\n");
13102             cmd = ast_filedelete(tempfile, NULL);
13103             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13104             if (!cmd)
13105                cmd = ast_play_and_wait(chan, "vm-speakup");
13106             break;
13107 #endif
13108          } else {
13109             /* If all is well, a message exists */
13110             msg_exists = 1;
13111             cmd = 0;
13112          }
13113          break;
13114       case '4':
13115          if (outsidecaller) {  /* only mark vm messages */
13116             /* Mark Urgent */
13117             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13118                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13119                res = ast_play_and_wait(chan, "vm-marked-urgent");
13120                strcpy(flag, "Urgent");
13121             } else if (flag) {
13122                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13123                res = ast_play_and_wait(chan, "vm-urgent-removed");
13124                strcpy(flag, "");
13125             } else {
13126                ast_play_and_wait(chan, "vm-sorry");
13127             }
13128             cmd = 0;
13129          } else {
13130             cmd = ast_play_and_wait(chan, "vm-sorry");
13131          }
13132          break;
13133       case '5':
13134       case '6':
13135       case '7':
13136       case '8':
13137       case '9':
13138       case '*':
13139       case '#':
13140          cmd = ast_play_and_wait(chan, "vm-sorry");
13141          break;
13142 #if 0 
13143 /*  XXX Commented out for the moment because of the dangers of deleting
13144     a message while recording (can put the message numbers out of sync) */
13145       case '*':
13146          /* Cancel recording, delete message, offer to take another message*/
13147          cmd = ast_play_and_wait(chan, "vm-deleted");
13148          cmd = ast_filedelete(tempfile, NULL);
13149          if (outsidecaller) {
13150             res = vm_exec(chan, NULL);
13151             return res;
13152          }
13153          else
13154             return 1;
13155 #endif
13156       case '0':
13157          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13158             cmd = ast_play_and_wait(chan, "vm-sorry");
13159             break;
13160          }
13161          if (msg_exists || recorded) {
13162             cmd = ast_play_and_wait(chan, "vm-saveoper");
13163             if (!cmd)
13164                cmd = ast_waitfordigit(chan, 3000);
13165             if (cmd == '1') {
13166                ast_filerename(tempfile, recordfile, NULL);
13167                ast_play_and_wait(chan, "vm-msgsaved");
13168                cmd = '0';
13169             } else if (cmd == '4') {
13170                if (flag) {
13171                   ast_play_and_wait(chan, "vm-marked-urgent");
13172                   strcpy(flag, "Urgent");
13173                }
13174                ast_play_and_wait(chan, "vm-msgsaved");
13175                cmd = '0';
13176             } else {
13177                ast_play_and_wait(chan, "vm-deleted");
13178                DELETE(tempfile, -1, tempfile, vmu);
13179                cmd = '0';
13180             }
13181          }
13182          return cmd;
13183       default:
13184          /* If the caller is an ouside caller, and the review option is enabled,
13185             allow them to review the message, but let the owner of the box review
13186             their OGM's */
13187          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13188             return cmd;
13189          if (msg_exists) {
13190             cmd = ast_play_and_wait(chan, "vm-review");
13191             if (!cmd && outsidecaller) {
13192                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13193                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13194                } else if (flag) {
13195                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13196                }
13197             }
13198          } else {
13199             cmd = ast_play_and_wait(chan, "vm-torerecord");
13200             if (!cmd)
13201                cmd = ast_waitfordigit(chan, 600);
13202          }
13203          
13204          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13205             cmd = ast_play_and_wait(chan, "vm-reachoper");
13206             if (!cmd)
13207                cmd = ast_waitfordigit(chan, 600);
13208          }
13209 #if 0
13210          if (!cmd)
13211             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13212 #endif
13213          if (!cmd)
13214             cmd = ast_waitfordigit(chan, 6000);
13215          if (!cmd) {
13216             attempts++;
13217          }
13218          if (attempts > max_attempts) {
13219             cmd = 't';
13220          }
13221       }
13222    }
13223    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13224       /* Hang up or timeout, so delete the recording. */
13225       ast_filedelete(tempfile, NULL);
13226    }
13227 
13228    if (cmd != 't' && outsidecaller)
13229       ast_play_and_wait(chan, "vm-goodbye");
13230 
13231    return cmd;
13232 }
13233 
13234 /* This is a workaround so that menuselect displays a proper description
13235  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13236  */
13237 
13238 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13239       .load = load_module,
13240       .unload = unload_module,
13241       .reload = reload,
13242       .nonoptreq = "res_adsi,res_smdi",
13243       );

Generated on Mon Jun 27 16:50:48 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7