Wed Apr 6 11:29:40 2011

Asterisk developer's documentation


app_voicemail_odbcstorage.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, read doc/imapstorage.txt
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: 301047 $")
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 
03903    /* Reading the entire directory into a file map scales better than
03904     * doing a stat repeatedly on a predicted sequence.  I suspect this
03905     * is partially due to stat(2) internally doing a readdir(2) itself to
03906     * find each file. */
03907    if (!(msgdir = opendir(dir))) {
03908       return -1;
03909    }
03910 
03911    while ((msgdirent = readdir(msgdir))) {
03912       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03913          map[msgdirint] = 1;
03914    }
03915    closedir(msgdir);
03916 
03917    for (x = 0; x < vmu->maxmsg; x++) {
03918       if (map[x] == 0)
03919          break;
03920    }
03921 
03922    return x - 1;
03923 }
03924 
03925 #endif /* #ifndef IMAP_STORAGE */
03926 #endif /* #else of #ifdef ODBC_STORAGE */
03927 #ifndef IMAP_STORAGE
03928 /*!
03929  * \brief Utility function to copy a file.
03930  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03931  * \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.
03932  *
03933  * 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.
03934  * The copy operation copies up to 4096 bytes at once.
03935  *
03936  * \return zero on success, -1 on error.
03937  */
03938 static int copy(char *infile, char *outfile)
03939 {
03940    int ifd;
03941    int ofd;
03942    int res;
03943    int len;
03944    char buf[4096];
03945 
03946 #ifdef HARDLINK_WHEN_POSSIBLE
03947    /* Hard link if possible; saves disk space & is faster */
03948    if (link(infile, outfile)) {
03949 #endif
03950       if ((ifd = open(infile, O_RDONLY)) < 0) {
03951          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03952          return -1;
03953       }
03954       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03955          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03956          close(ifd);
03957          return -1;
03958       }
03959       do {
03960          len = read(ifd, buf, sizeof(buf));
03961          if (len < 0) {
03962             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03963             close(ifd);
03964             close(ofd);
03965             unlink(outfile);
03966          }
03967          if (len) {
03968             res = write(ofd, buf, len);
03969             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03970                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03971                close(ifd);
03972                close(ofd);
03973                unlink(outfile);
03974             }
03975          }
03976       } while (len);
03977       close(ifd);
03978       close(ofd);
03979       return 0;
03980 #ifdef HARDLINK_WHEN_POSSIBLE
03981    } else {
03982       /* Hard link succeeded */
03983       return 0;
03984    }
03985 #endif
03986 }
03987 
03988 /*!
03989  * \brief Copies a voicemail information (envelope) file.
03990  * \param frompath
03991  * \param topath 
03992  *
03993  * Every voicemail has the data (.wav) file, and the information file.
03994  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03995  * This is used by the COPY macro when not using IMAP storage.
03996  */
03997 static void copy_plain_file(char *frompath, char *topath)
03998 {
03999    char frompath2[PATH_MAX], topath2[PATH_MAX];
04000    struct ast_variable *tmp,*var = NULL;
04001    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04002    ast_filecopy(frompath, topath, NULL);
04003    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04004    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04005    if (ast_check_realtime("voicemail_data")) {
04006       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04007       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04008       for (tmp = var; tmp; tmp = tmp->next) {
04009          if (!strcasecmp(tmp->name, "origmailbox")) {
04010             origmailbox = tmp->value;
04011          } else if (!strcasecmp(tmp->name, "context")) {
04012             context = tmp->value;
04013          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04014             macrocontext = tmp->value;
04015          } else if (!strcasecmp(tmp->name, "exten")) {
04016             exten = tmp->value;
04017          } else if (!strcasecmp(tmp->name, "priority")) {
04018             priority = tmp->value;
04019          } else if (!strcasecmp(tmp->name, "callerchan")) {
04020             callerchan = tmp->value;
04021          } else if (!strcasecmp(tmp->name, "callerid")) {
04022             callerid = tmp->value;
04023          } else if (!strcasecmp(tmp->name, "origdate")) {
04024             origdate = tmp->value;
04025          } else if (!strcasecmp(tmp->name, "origtime")) {
04026             origtime = tmp->value;
04027          } else if (!strcasecmp(tmp->name, "category")) {
04028             category = tmp->value;
04029          } else if (!strcasecmp(tmp->name, "duration")) {
04030             duration = tmp->value;
04031          }
04032       }
04033       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);
04034    }
04035    copy(frompath2, topath2);
04036    ast_variables_destroy(var);
04037 }
04038 #endif
04039 
04040 /*! 
04041  * \brief Removes the voicemail sound and information file.
04042  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04043  *
04044  * This is used by the DELETE macro when voicemails are stored on the file system.
04045  *
04046  * \return zero on success, -1 on error.
04047  */
04048 static int vm_delete(char *file)
04049 {
04050    char *txt;
04051    int txtsize = 0;
04052 
04053    txtsize = (strlen(file) + 5)*sizeof(char);
04054    txt = alloca(txtsize);
04055    /* Sprintf here would safe because we alloca'd exactly the right length,
04056     * but trying to eliminate all sprintf's anyhow
04057     */
04058    if (ast_check_realtime("voicemail_data")) {
04059       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04060    }
04061    snprintf(txt, txtsize, "%s.txt", file);
04062    unlink(txt);
04063    return ast_filedelete(file, NULL);
04064 }
04065 
04066 /*!
04067  * \brief utility used by inchar(), for base_encode()
04068  */
04069 static int inbuf(struct baseio *bio, FILE *fi)
04070 {
04071    int l;
04072 
04073    if (bio->ateof)
04074       return 0;
04075 
04076    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04077       if (ferror(fi))
04078          return -1;
04079 
04080       bio->ateof = 1;
04081       return 0;
04082    }
04083 
04084    bio->iolen = l;
04085    bio->iocp = 0;
04086 
04087    return 1;
04088 }
04089 
04090 /*!
04091  * \brief utility used by base_encode()
04092  */
04093 static int inchar(struct baseio *bio, FILE *fi)
04094 {
04095    if (bio->iocp>=bio->iolen) {
04096       if (!inbuf(bio, fi))
04097          return EOF;
04098    }
04099 
04100    return bio->iobuf[bio->iocp++];
04101 }
04102 
04103 /*!
04104  * \brief utility used by base_encode()
04105  */
04106 static int ochar(struct baseio *bio, int c, FILE *so)
04107 {
04108    if (bio->linelength >= BASELINELEN) {
04109       if (fputs(ENDL, so) == EOF) {
04110          return -1;
04111       }
04112 
04113       bio->linelength = 0;
04114    }
04115 
04116    if (putc(((unsigned char) c), so) == EOF) {
04117       return -1;
04118    }
04119 
04120    bio->linelength++;
04121 
04122    return 1;
04123 }
04124 
04125 /*!
04126  * \brief Performs a base 64 encode algorithm on the contents of a File
04127  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04128  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04129  *
04130  * 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 ?
04131  *
04132  * \return zero on success, -1 on error.
04133  */
04134 static int base_encode(char *filename, FILE *so)
04135 {
04136    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04137       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04138       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04139       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04140    int i, hiteof = 0;
04141    FILE *fi;
04142    struct baseio bio;
04143 
04144    memset(&bio, 0, sizeof(bio));
04145    bio.iocp = BASEMAXINLINE;
04146 
04147    if (!(fi = fopen(filename, "rb"))) {
04148       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04149       return -1;
04150    }
04151 
04152    while (!hiteof){
04153       unsigned char igroup[3], ogroup[4];
04154       int c, n;
04155 
04156       memset(igroup, 0, sizeof(igroup));
04157 
04158       for (n = 0; n < 3; n++) {
04159          if ((c = inchar(&bio, fi)) == EOF) {
04160             hiteof = 1;
04161             break;
04162          }
04163 
04164          igroup[n] = (unsigned char) c;
04165       }
04166 
04167       if (n > 0) {
04168          ogroup[0]= dtable[igroup[0] >> 2];
04169          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04170          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04171          ogroup[3]= dtable[igroup[2] & 0x3F];
04172 
04173          if (n < 3) {
04174             ogroup[3] = '=';
04175 
04176             if (n < 2)
04177                ogroup[2] = '=';
04178          }
04179 
04180          for (i = 0; i < 4; i++)
04181             ochar(&bio, ogroup[i], so);
04182       }
04183    }
04184 
04185    fclose(fi);
04186    
04187    if (fputs(ENDL, so) == EOF) {
04188       return 0;
04189    }
04190 
04191    return 1;
04192 }
04193 
04194 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)
04195 {
04196    char callerid[256];
04197    char num[12];
04198    char fromdir[256], fromfile[256];
04199    struct ast_config *msg_cfg;
04200    const char *origcallerid, *origtime;
04201    char origcidname[80], origcidnum[80], origdate[80];
04202    int inttime;
04203    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04204 
04205    /* Prepare variables for substitution in email body and subject */
04206    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04207    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04208    snprintf(num, sizeof(num), "%d", msgnum);
04209    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04210    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04211    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04212    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04213       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04214    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04215    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04216    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04217    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04218    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04219 
04220    /* Retrieve info from VM attribute file */
04221    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04222    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04223    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04224       strcat(fromfile, ".txt");
04225    }
04226    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04227       if (option_debug > 0) {
04228          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04229       }
04230       return;
04231    }
04232 
04233    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04234       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04235       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04236       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04237       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04238    }
04239 
04240    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04241       struct timeval tv = { inttime, };
04242       struct ast_tm tm;
04243       ast_localtime(&tv, &tm, NULL);
04244       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04245       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04246    }
04247    ast_config_destroy(msg_cfg);
04248 }
04249 
04250 /*!
04251  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04252  * \param from The string to work with.
04253  * \param buf The buffer into which to write the modified quoted string.
04254  * \param maxlen Always zero, but see \see ast_str
04255  * 
04256  * \return The destination string with quotes wrapped on it (the to field).
04257  */
04258 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04259 {
04260    const char *ptr;
04261 
04262    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04263    ast_str_set(buf, maxlen, "\"");
04264    for (ptr = from; *ptr; ptr++) {
04265       if (*ptr == '"' || *ptr == '\\') {
04266          ast_str_append(buf, maxlen, "\\%c", *ptr);
04267       } else {
04268          ast_str_append(buf, maxlen, "%c", *ptr);
04269       }
04270    }
04271    ast_str_append(buf, maxlen, "\"");
04272 
04273    return ast_str_buffer(*buf);
04274 }
04275 
04276 /*! \brief
04277  * fill in *tm for current time according to the proper timezone, if any.
04278  * \return tm so it can be used as a function argument.
04279  */
04280 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04281 {
04282    const struct vm_zone *z = NULL;
04283    struct timeval t = ast_tvnow();
04284 
04285    /* Does this user have a timezone specified? */
04286    if (!ast_strlen_zero(vmu->zonetag)) {
04287       /* Find the zone in the list */
04288       AST_LIST_LOCK(&zones);
04289       AST_LIST_TRAVERSE(&zones, z, list) {
04290          if (!strcmp(z->name, vmu->zonetag))
04291             break;
04292       }
04293       AST_LIST_UNLOCK(&zones);
04294    }
04295    ast_localtime(&t, tm, z ? z->timezone : NULL);
04296    return tm;
04297 }
04298 
04299 /*!\brief Check if the string would need encoding within the MIME standard, to
04300  * avoid confusing certain mail software that expects messages to be 7-bit
04301  * clean.
04302  */
04303 static int check_mime(const char *str)
04304 {
04305    for (; *str; str++) {
04306       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04307          return 1;
04308       }
04309    }
04310    return 0;
04311 }
04312 
04313 /*!\brief Encode a string according to the MIME rules for encoding strings
04314  * that are not 7-bit clean or contain control characters.
04315  *
04316  * Additionally, if the encoded string would exceed the MIME limit of 76
04317  * characters per line, then the encoding will be broken up into multiple
04318  * sections, separated by a space character, in order to facilitate
04319  * breaking up the associated header across multiple lines.
04320  *
04321  * \param end An expandable buffer for holding the result
04322  * \param maxlen Always zero, but see \see ast_str
04323  * \param start A string to be encoded
04324  * \param preamble The length of the first line already used for this string,
04325  * to ensure that each line maintains a maximum length of 76 chars.
04326  * \param postamble the length of any additional characters appended to the
04327  * line, used to ensure proper field wrapping.
04328  * \retval The encoded string.
04329  */
04330 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04331 {
04332    struct ast_str *tmp = ast_str_alloca(80);
04333    int first_section = 1;
04334 
04335    ast_str_reset(*end);
04336    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04337    for (; *start; start++) {
04338       int need_encoding = 0;
04339       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04340          need_encoding = 1;
04341       }
04342       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04343          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04344          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04345          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04346          /* Start new line */
04347          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04348          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04349          first_section = 0;
04350       }
04351       if (need_encoding && *start == ' ') {
04352          ast_str_append(&tmp, -1, "_");
04353       } else if (need_encoding) {
04354          ast_str_append(&tmp, -1, "=%hhX", *start);
04355       } else {
04356          ast_str_append(&tmp, -1, "%c", *start);
04357       }
04358    }
04359    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04360    return ast_str_buffer(*end);
04361 }
04362 
04363 /*!
04364  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04365  * \param p The output file to generate the email contents into.
04366  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04367  * \param vmu The voicemail user who is sending the voicemail.
04368  * \param msgnum The message index in the mailbox folder.
04369  * \param context 
04370  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04371  * \param fromfolder
04372  * \param cidnum The caller ID number.
04373  * \param cidname The caller ID name.
04374  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04375  * \param attach2 
04376  * \param format The message sound file format. i.e. .wav
04377  * \param duration The time of the message content, in seconds.
04378  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04379  * \param chan
04380  * \param category
04381  * \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.
04382  * \param flag
04383  *
04384  * 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.
04385  */
04386 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)
04387 {
04388    char date[256];
04389    char host[MAXHOSTNAMELEN] = "";
04390    char who[256];
04391    char bound[256];
04392    char dur[256];
04393    struct ast_tm tm;
04394    char enc_cidnum[256] = "", enc_cidname[256] = "";
04395    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04396    char *greeting_attachment; 
04397    char filename[256];
04398 
04399    if (!str1 || !str2) {
04400       ast_free(str1);
04401       ast_free(str2);
04402       return;
04403    }
04404 
04405    if (cidnum) {
04406       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04407    }
04408    if (cidname) {
04409       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04410    }
04411    gethostname(host, sizeof(host) - 1);
04412 
04413    if (strchr(srcemail, '@')) {
04414       ast_copy_string(who, srcemail, sizeof(who));
04415    } else {
04416       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04417    }
04418 
04419    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04420    if (greeting_attachment) {
04421       *greeting_attachment++ = '\0';
04422    }
04423 
04424    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04425    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04426    fprintf(p, "Date: %s" ENDL, date);
04427 
04428    /* Set date format for voicemail mail */
04429    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04430 
04431    if (!ast_strlen_zero(fromstring)) {
04432       struct ast_channel *ast;
04433       if ((ast = ast_dummy_channel_alloc())) {
04434          char *ptr;
04435          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04436          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04437 
04438          if (check_mime(ast_str_buffer(str1))) {
04439             int first_line = 1;
04440             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04441             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04442                *ptr = '\0';
04443                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04444                first_line = 0;
04445                /* Substring is smaller, so this will never grow */
04446                ast_str_set(&str2, 0, "%s", ptr + 1);
04447             }
04448             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04449          } else {
04450             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04451          }
04452          ast = ast_channel_release(ast);
04453       } else {
04454          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04455       }
04456    } else {
04457       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04458    }
04459 
04460    if (check_mime(vmu->fullname)) {
04461       int first_line = 1;
04462       char *ptr;
04463       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04464       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04465          *ptr = '\0';
04466          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04467          first_line = 0;
04468          /* Substring is smaller, so this will never grow */
04469          ast_str_set(&str2, 0, "%s", ptr + 1);
04470       }
04471       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04472    } else {
04473       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04474    }
04475 
04476    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04477       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04478       struct ast_channel *ast;
04479       if ((ast = ast_dummy_channel_alloc())) {
04480          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04481          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04482          if (check_mime(ast_str_buffer(str1))) {
04483             int first_line = 1;
04484             char *ptr;
04485             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04486             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04487                *ptr = '\0';
04488                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04489                first_line = 0;
04490                /* Substring is smaller, so this will never grow */
04491                ast_str_set(&str2, 0, "%s", ptr + 1);
04492             }
04493             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04494          } else {
04495             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04496          }
04497          ast = ast_channel_release(ast);
04498       } else {
04499          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04500       }
04501    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04502       if (ast_strlen_zero(flag)) {
04503          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04504       } else {
04505          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04506       }
04507    } else {
04508       if (ast_strlen_zero(flag)) {
04509          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04510       } else {
04511          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04512       }
04513    }
04514 
04515    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04516       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04517    if (imap) {
04518       /* additional information needed for IMAP searching */
04519       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04520       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04521       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04522       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04523 #ifdef IMAP_STORAGE
04524       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04525 #else
04526       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04527 #endif
04528       /* flag added for Urgent */
04529       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04530       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04531       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04532       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04533       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04534       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04535       if (!ast_strlen_zero(category)) {
04536          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04537       } else {
04538          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04539       }
04540       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04541       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04542       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04543    }
04544    if (!ast_strlen_zero(cidnum)) {
04545       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04546    }
04547    if (!ast_strlen_zero(cidname)) {
04548       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04549    }
04550    fprintf(p, "MIME-Version: 1.0" ENDL);
04551    if (attach_user_voicemail) {
04552       /* Something unique. */
04553       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04554          (int) getpid(), (unsigned int) ast_random());
04555 
04556       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04557       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04558       fprintf(p, "--%s" ENDL, bound);
04559    }
04560    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04561    if (emailbody || vmu->emailbody) {
04562       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04563       struct ast_channel *ast;
04564       if ((ast = ast_dummy_channel_alloc())) {
04565          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04566          ast_str_substitute_variables(&str1, 0, ast, e_body);
04567 #ifdef IMAP_STORAGE
04568             {
04569                /* Convert body to native line terminators for IMAP backend */
04570                char *line = ast_str_buffer(str1), *next;
04571                do {
04572                   /* Terminate line before outputting it to the file */
04573                   if ((next = strchr(line, '\n'))) {
04574                      *next++ = '\0';
04575                   }
04576                   fprintf(p, "%s" ENDL, line);
04577                   line = next;
04578                } while (!ast_strlen_zero(line));
04579             }
04580 #else
04581          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04582 #endif
04583          ast = ast_channel_release(ast);
04584       } else {
04585          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04586       }
04587    } else if (msgnum > -1) {
04588       if (strcmp(vmu->mailbox, mailbox)) {
04589          /* Forwarded type */
04590          struct ast_config *msg_cfg;
04591          const char *v;
04592          int inttime;
04593          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04594          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04595          /* Retrieve info from VM attribute file */
04596          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04597          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04598          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04599             strcat(fromfile, ".txt");
04600          }
04601          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04602             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04603                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04604             }
04605 
04606             /* You might be tempted to do origdate, except that a) it's in the wrong
04607              * format, and b) it's missing for IMAP recordings. */
04608             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04609                struct timeval tv = { inttime, };
04610                struct ast_tm tm;
04611                ast_localtime(&tv, &tm, NULL);
04612                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04613             }
04614             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04615                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04616                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04617                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04618                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04619                date, origcallerid, origdate);
04620             ast_config_destroy(msg_cfg);
04621          } else {
04622             goto plain_message;
04623          }
04624       } else {
04625 plain_message:
04626          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04627             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04628             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04629             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04630             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04631       }
04632    } else {
04633       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04634             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04635    }
04636 
04637    if (imap || attach_user_voicemail) {
04638       if (!ast_strlen_zero(attach2)) {
04639          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04640          ast_debug(5, "creating second attachment filename %s\n", filename);
04641          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04642          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04643          ast_debug(5, "creating attachment filename %s\n", filename);
04644          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04645       } else {
04646          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04647          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04648          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04649       }
04650    }
04651    ast_free(str1);
04652    ast_free(str2);
04653 }
04654 
04655 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)
04656 {
04657    char tmpdir[256], newtmp[256];
04658    char fname[256];
04659    char tmpcmd[256];
04660    int tmpfd = -1;
04661 
04662    /* Eww. We want formats to tell us their own MIME type */
04663    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04664 
04665    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04666       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04667       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04668       tmpfd = mkstemp(newtmp);
04669       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04670       ast_debug(3, "newtmp: %s\n", newtmp);
04671       if (tmpfd > -1) {
04672          int soxstatus;
04673          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04674          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04675             attach = newtmp;
04676             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04677          } else {
04678             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04679                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04680             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04681          }
04682       }
04683    }
04684    fprintf(p, "--%s" ENDL, bound);
04685    if (msgnum > -1)
04686       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04687    else
04688       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04689    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04690    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04691    if (msgnum > -1)
04692       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04693    else
04694       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04695    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04696    base_encode(fname, p);
04697    if (last)
04698       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04699    if (tmpfd > -1) {
04700       unlink(fname);
04701       close(tmpfd);
04702       unlink(newtmp);
04703    }
04704    return 0;
04705 }
04706 
04707 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)
04708 {
04709    FILE *p = NULL;
04710    char tmp[80] = "/tmp/astmail-XXXXXX";
04711    char tmp2[256];
04712    char *stringp;
04713 
04714    if (vmu && ast_strlen_zero(vmu->email)) {
04715       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04716       return(0);
04717    }
04718 
04719    /* Mail only the first format */
04720    format = ast_strdupa(format);
04721    stringp = format;
04722    strsep(&stringp, "|");
04723 
04724    if (!strcmp(format, "wav49"))
04725       format = "WAV";
04726    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));
04727    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04728       command hangs */
04729    if ((p = vm_mkftemp(tmp)) == NULL) {
04730       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04731       return -1;
04732    } else {
04733       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04734       fclose(p);
04735       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04736       ast_safe_system(tmp2);
04737       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04738    }
04739    return 0;
04740 }
04741 
04742 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)
04743 {
04744    char enc_cidnum[256], enc_cidname[256];
04745    char date[256];
04746    char host[MAXHOSTNAMELEN] = "";
04747    char who[256];
04748    char dur[PATH_MAX];
04749    char tmp[80] = "/tmp/astmail-XXXXXX";
04750    char tmp2[PATH_MAX];
04751    struct ast_tm tm;
04752    FILE *p;
04753    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04754 
04755    if (!str1 || !str2) {
04756       ast_free(str1);
04757       ast_free(str2);
04758       return -1;
04759    }
04760 
04761    if (cidnum) {
04762       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04763    }
04764    if (cidname) {
04765       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04766    }
04767 
04768    if ((p = vm_mkftemp(tmp)) == NULL) {
04769       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04770       ast_free(str1);
04771       ast_free(str2);
04772       return -1;
04773    }
04774    gethostname(host, sizeof(host)-1);
04775    if (strchr(srcemail, '@')) {
04776       ast_copy_string(who, srcemail, sizeof(who));
04777    } else {
04778       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04779    }
04780    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04781    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04782    fprintf(p, "Date: %s\n", date);
04783 
04784    /* Reformat for custom pager format */
04785    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04786 
04787    if (!ast_strlen_zero(pagerfromstring)) {
04788       struct ast_channel *ast;
04789       if ((ast = ast_dummy_channel_alloc())) {
04790          char *ptr;
04791          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04792          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04793 
04794          if (check_mime(ast_str_buffer(str1))) {
04795             int first_line = 1;
04796             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04797             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04798                *ptr = '\0';
04799                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04800                first_line = 0;
04801                /* Substring is smaller, so this will never grow */
04802                ast_str_set(&str2, 0, "%s", ptr + 1);
04803             }
04804             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04805          } else {
04806             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04807          }
04808          ast = ast_channel_release(ast);
04809       } else {
04810          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04811       }
04812    } else {
04813       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04814    }
04815 
04816    if (check_mime(vmu->fullname)) {
04817       int first_line = 1;
04818       char *ptr;
04819       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04820       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04821          *ptr = '\0';
04822          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04823          first_line = 0;
04824          /* Substring is smaller, so this will never grow */
04825          ast_str_set(&str2, 0, "%s", ptr + 1);
04826       }
04827       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04828    } else {
04829       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04830    }
04831 
04832    if (!ast_strlen_zero(pagersubject)) {
04833       struct ast_channel *ast;
04834       if ((ast = ast_dummy_channel_alloc())) {
04835          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04836          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04837          if (check_mime(ast_str_buffer(str1))) {
04838             int first_line = 1;
04839             char *ptr;
04840             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04841             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04842                *ptr = '\0';
04843                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04844                first_line = 0;
04845                /* Substring is smaller, so this will never grow */
04846                ast_str_set(&str2, 0, "%s", ptr + 1);
04847             }
04848             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04849          } else {
04850             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04851          }
04852          ast = ast_channel_release(ast);
04853       } else {
04854          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04855       }
04856    } else {
04857       if (ast_strlen_zero(flag)) {
04858          fprintf(p, "Subject: New VM\n\n");
04859       } else {
04860          fprintf(p, "Subject: New %s VM\n\n", flag);
04861       }
04862    }
04863 
04864    if (pagerbody) {
04865       struct ast_channel *ast;
04866       if ((ast = ast_dummy_channel_alloc())) {
04867          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04868          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
04869          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04870          ast = ast_channel_release(ast);
04871       } else {
04872          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04873       }
04874    } else {
04875       fprintf(p, "New %s long %s msg in box %s\n"
04876             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04877    }
04878 
04879    fclose(p);
04880    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04881    ast_safe_system(tmp2);
04882    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04883    ast_free(str1);
04884    ast_free(str2);
04885    return 0;
04886 }
04887 
04888 /*!
04889  * \brief Gets the current date and time, as formatted string.
04890  * \param s The buffer to hold the output formatted date.
04891  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04892  * 
04893  * The date format string used is "%a %b %e %r UTC %Y".
04894  * 
04895  * \return zero on success, -1 on error.
04896  */
04897 static int get_date(char *s, int len)
04898 {
04899    struct ast_tm tm;
04900    struct timeval t = ast_tvnow();
04901    
04902    ast_localtime(&t, &tm, "UTC");
04903 
04904    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04905 }
04906 
04907 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04908 {
04909    int res;
04910    char fn[PATH_MAX];
04911    char dest[PATH_MAX];
04912 
04913    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04914 
04915    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04916       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04917       return -1;
04918    }
04919 
04920    RETRIEVE(fn, -1, ext, context);
04921    if (ast_fileexists(fn, NULL, NULL) > 0) {
04922       res = ast_stream_and_wait(chan, fn, ecodes);
04923       if (res) {
04924          DISPOSE(fn, -1);
04925          return res;
04926       }
04927    } else {
04928       /* Dispose just in case */
04929       DISPOSE(fn, -1);
04930       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04931       if (res)
04932          return res;
04933       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04934       if (res)
04935          return res;
04936    }
04937    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04938    return res;
04939 }
04940 
04941 static void free_zone(struct vm_zone *z)
04942 {
04943    ast_free(z);
04944 }
04945 
04946 #ifdef ODBC_STORAGE
04947 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04948 {
04949    int x = -1;
04950    int res;
04951    SQLHSTMT stmt = NULL;
04952    char sql[PATH_MAX];
04953    char rowdata[20];
04954    char tmp[PATH_MAX] = "";
04955    struct odbc_obj *obj = NULL;
04956    char *context;
04957    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04958 
04959    if (newmsgs)
04960       *newmsgs = 0;
04961    if (oldmsgs)
04962       *oldmsgs = 0;
04963    if (urgentmsgs)
04964       *urgentmsgs = 0;
04965 
04966    /* If no mailbox, return immediately */
04967    if (ast_strlen_zero(mailbox))
04968       return 0;
04969 
04970    ast_copy_string(tmp, mailbox, sizeof(tmp));
04971 
04972    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04973       int u, n, o;
04974       char *next, *remaining = tmp;
04975       while ((next = strsep(&remaining, " ,"))) {
04976          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04977             return -1;
04978          }
04979          if (urgentmsgs) {
04980             *urgentmsgs += u;
04981          }
04982          if (newmsgs) {
04983             *newmsgs += n;
04984          }
04985          if (oldmsgs) {
04986             *oldmsgs += o;
04987          }
04988       }
04989       return 0;
04990    }
04991 
04992    context = strchr(tmp, '@');
04993    if (context) {
04994       *context = '\0';
04995       context++;
04996    } else
04997       context = "default";
04998 
04999    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05000       do {
05001          if (newmsgs) {
05002             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05003             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05004                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05005                break;
05006             }
05007             res = SQLFetch(stmt);
05008             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05009                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05010                break;
05011             }
05012             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05013             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05014                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05015                break;
05016             }
05017             *newmsgs = atoi(rowdata);
05018             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05019          }
05020 
05021          if (oldmsgs) {
05022             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05023             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05024                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05025                break;
05026             }
05027             res = SQLFetch(stmt);
05028             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05029                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05030                break;
05031             }
05032             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05033             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05034                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05035                break;
05036             }
05037             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05038             *oldmsgs = atoi(rowdata);
05039          }
05040 
05041          if (urgentmsgs) {
05042             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05043             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05044                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05045                break;
05046             }
05047             res = SQLFetch(stmt);
05048             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05049                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05050                break;
05051             }
05052             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05053             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05054                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05055                break;
05056             }
05057             *urgentmsgs = atoi(rowdata);
05058          }
05059 
05060          x = 0;
05061       } while (0);
05062    } else {
05063       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05064    }
05065 
05066    if (stmt) {
05067       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05068    }
05069    if (obj) {
05070       ast_odbc_release_obj(obj);
05071    }
05072 
05073    return x;
05074 }
05075 
05076 /*!
05077  * \brief Gets the number of messages that exist in a mailbox folder.
05078  * \param context
05079  * \param mailbox
05080  * \param folder
05081  * 
05082  * This method is used when ODBC backend is used.
05083  * \return The number of messages in this mailbox folder (zero or more).
05084  */
05085 static int messagecount(const char *context, const char *mailbox, const char *folder)
05086 {
05087    struct odbc_obj *obj = NULL;
05088    int nummsgs = 0;
05089    int res;
05090    SQLHSTMT stmt = NULL;
05091    char sql[PATH_MAX];
05092    char rowdata[20];
05093    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05094    if (!folder)
05095       folder = "INBOX";
05096    /* If no mailbox, return immediately */
05097    if (ast_strlen_zero(mailbox))
05098       return 0;
05099 
05100    obj = ast_odbc_request_obj(odbc_database, 0);
05101    if (obj) {
05102       if (!strcmp(folder, "INBOX")) {
05103          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);
05104       } else {
05105          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05106       }
05107       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05108       if (!stmt) {
05109          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05110          goto yuck;
05111       }
05112       res = SQLFetch(stmt);
05113       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05114          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05115          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05116          goto yuck;
05117       }
05118       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05119       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05120          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05121          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05122          goto yuck;
05123       }
05124       nummsgs = atoi(rowdata);
05125       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05126    } else
05127       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05128 
05129 yuck:
05130    if (obj)
05131       ast_odbc_release_obj(obj);
05132    return nummsgs;
05133 }
05134 
05135 /** 
05136  * \brief Determines if the given folder has messages.
05137  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05138  * 
05139  * This function is used when the mailbox is stored in an ODBC back end.
05140  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05141  * \return 1 if the folder has one or more messages. zero otherwise.
05142  */
05143 static int has_voicemail(const char *mailbox, const char *folder)
05144 {
05145    char tmp[256], *tmp2 = tmp, *box, *context;
05146    ast_copy_string(tmp, mailbox, sizeof(tmp));
05147    while ((context = box = strsep(&tmp2, ",&"))) {
05148       strsep(&context, "@");
05149       if (ast_strlen_zero(context))
05150          context = "default";
05151       if (messagecount(context, box, folder))
05152          return 1;
05153    }
05154    return 0;
05155 }
05156 #endif
05157 #ifndef IMAP_STORAGE
05158 /*! 
05159  * \brief Copies a message from one mailbox to another.
05160  * \param chan
05161  * \param vmu
05162  * \param imbox
05163  * \param msgnum
05164  * \param duration
05165  * \param recip
05166  * \param fmt
05167  * \param dir
05168  * \param flag
05169  *
05170  * This is only used by file storage based mailboxes.
05171  *
05172  * \return zero on success, -1 on error.
05173  */
05174 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)
05175 {
05176    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05177    const char *frombox = mbox(vmu, imbox);
05178    int recipmsgnum;
05179    int res = 0;
05180 
05181    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05182 
05183    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05184       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05185    } else {
05186       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05187    }
05188    
05189    if (!dir)
05190       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05191    else
05192       ast_copy_string(fromdir, dir, sizeof(fromdir));
05193 
05194    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05195    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05196 
05197    if (vm_lock_path(todir))
05198       return ERROR_LOCK_PATH;
05199 
05200    recipmsgnum = last_message_index(recip, todir) + 1;
05201    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05202       make_file(topath, sizeof(topath), todir, recipmsgnum);
05203 #ifndef ODBC_STORAGE
05204       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05205          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05206       } else {
05207 #endif
05208          /* If we are prepending a message for ODBC, then the message already
05209           * exists in the database, but we want to force copying from the
05210           * filesystem (since only the FS contains the prepend). */
05211          copy_plain_file(frompath, topath);
05212          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05213          vm_delete(topath);
05214 #ifndef ODBC_STORAGE
05215       }
05216 #endif
05217    } else {
05218       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05219       res = -1;
05220    }
05221    ast_unlock_path(todir);
05222    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05223       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05224       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05225       flag);
05226    
05227    return res;
05228 }
05229 #endif
05230 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05231 
05232 static int messagecount(const char *context, const char *mailbox, const char *folder)
05233 {
05234    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05235 }
05236 
05237 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05238 {
05239    DIR *dir;
05240    struct dirent *de;
05241    char fn[256];
05242    int ret = 0;
05243 
05244    /* If no mailbox, return immediately */
05245    if (ast_strlen_zero(mailbox))
05246       return 0;
05247 
05248    if (ast_strlen_zero(folder))
05249       folder = "INBOX";
05250    if (ast_strlen_zero(context))
05251       context = "default";
05252 
05253    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05254 
05255    if (!(dir = opendir(fn)))
05256       return 0;
05257 
05258    while ((de = readdir(dir))) {
05259       if (!strncasecmp(de->d_name, "msg", 3)) {
05260          if (shortcircuit) {
05261             ret = 1;
05262             break;
05263          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05264             ret++;
05265          }
05266       }
05267    }
05268 
05269    closedir(dir);
05270 
05271    return ret;
05272 }
05273 
05274 /** 
05275  * \brief Determines if the given folder has messages.
05276  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05277  * \param folder the folder to look in
05278  *
05279  * This function is used when the mailbox is stored in a filesystem back end.
05280  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05281  * \return 1 if the folder has one or more messages. zero otherwise.
05282  */
05283 static int has_voicemail(const char *mailbox, const char *folder)
05284 {
05285    char tmp[256], *tmp2 = tmp, *box, *context;
05286    ast_copy_string(tmp, mailbox, sizeof(tmp));
05287    if (ast_strlen_zero(folder)) {
05288       folder = "INBOX";
05289    }
05290    while ((box = strsep(&tmp2, ",&"))) {
05291       if ((context = strchr(box, '@')))
05292          *context++ = '\0';
05293       else
05294          context = "default";
05295       if (__has_voicemail(context, box, folder, 1))
05296          return 1;
05297       /* If we are checking INBOX, we should check Urgent as well */
05298       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05299          return 1;
05300       }
05301    }
05302    return 0;
05303 }
05304 
05305 
05306 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05307 {
05308    char tmp[256];
05309    char *context;
05310 
05311    /* If no mailbox, return immediately */
05312    if (ast_strlen_zero(mailbox))
05313       return 0;
05314 
05315    if (newmsgs)
05316       *newmsgs = 0;
05317    if (oldmsgs)
05318       *oldmsgs = 0;
05319    if (urgentmsgs)
05320       *urgentmsgs = 0;
05321 
05322    if (strchr(mailbox, ',')) {
05323       int tmpnew, tmpold, tmpurgent;
05324       char *mb, *cur;
05325 
05326       ast_copy_string(tmp, mailbox, sizeof(tmp));
05327       mb = tmp;
05328       while ((cur = strsep(&mb, ", "))) {
05329          if (!ast_strlen_zero(cur)) {
05330             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05331                return -1;
05332             else {
05333                if (newmsgs)
05334                   *newmsgs += tmpnew; 
05335                if (oldmsgs)
05336                   *oldmsgs += tmpold;
05337                if (urgentmsgs)
05338                   *urgentmsgs += tmpurgent;
05339             }
05340          }
05341       }
05342       return 0;
05343    }
05344 
05345    ast_copy_string(tmp, mailbox, sizeof(tmp));
05346    
05347    if ((context = strchr(tmp, '@')))
05348       *context++ = '\0';
05349    else
05350       context = "default";
05351 
05352    if (newmsgs)
05353       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05354    if (oldmsgs)
05355       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05356    if (urgentmsgs)
05357       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05358 
05359    return 0;
05360 }
05361 
05362 #endif
05363 
05364 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05365 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05366 {
05367    int urgentmsgs = 0;
05368    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05369    if (newmsgs) {
05370       *newmsgs += urgentmsgs;
05371    }
05372    return res;
05373 }
05374 
05375 static void run_externnotify(char *context, char *extension, const char *flag)
05376 {
05377    char arguments[255];
05378    char ext_context[256] = "";
05379    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05380    struct ast_smdi_mwi_message *mwi_msg;
05381 
05382    if (!ast_strlen_zero(context))
05383       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05384    else
05385       ast_copy_string(ext_context, extension, sizeof(ext_context));
05386 
05387    if (smdi_iface) {
05388       if (ast_app_has_voicemail(ext_context, NULL)) 
05389          ast_smdi_mwi_set(smdi_iface, extension);
05390       else
05391          ast_smdi_mwi_unset(smdi_iface, extension);
05392 
05393       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05394          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05395          if (!strncmp(mwi_msg->cause, "INV", 3))
05396             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05397          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05398             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05399          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05400          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05401       } else {
05402          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05403       }
05404    }
05405 
05406    if (!ast_strlen_zero(externnotify)) {
05407       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05408          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05409       } else {
05410          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05411          ast_debug(1, "Executing %s\n", arguments);
05412          ast_safe_system(arguments);
05413       }
05414    }
05415 }
05416 
05417 /*!
05418  * \brief Variables used for saving a voicemail.
05419  *
05420  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05421  */
05422 struct leave_vm_options {
05423    unsigned int flags;
05424    signed char record_gain;
05425    char *exitcontext;
05426 };
05427 
05428 /*!
05429  * \brief Prompts the user and records a voicemail to a mailbox.
05430  * \param chan
05431  * \param ext
05432  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05433  * 
05434  * 
05435  * 
05436  * \return zero on success, -1 on error.
05437  */
05438 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05439 {
05440 #ifdef IMAP_STORAGE
05441    int newmsgs, oldmsgs;
05442 #else
05443    char urgdir[PATH_MAX];
05444 #endif
05445    char txtfile[PATH_MAX];
05446    char tmptxtfile[PATH_MAX];
05447    struct vm_state *vms = NULL;
05448    char callerid[256];
05449    FILE *txt;
05450    char date[256];
05451    int txtdes;
05452    int res = 0;
05453    int msgnum;
05454    int duration = 0;
05455    int ausemacro = 0;
05456    int ousemacro = 0;
05457    int ouseexten = 0;
05458    char tmpdur[16];
05459    char priority[16];
05460    char origtime[16];
05461    char dir[PATH_MAX];
05462    char tmpdir[PATH_MAX];
05463    char fn[PATH_MAX];
05464    char prefile[PATH_MAX] = "";
05465    char tempfile[PATH_MAX] = "";
05466    char ext_context[256] = "";
05467    char fmt[80];
05468    char *context;
05469    char ecodes[17] = "#";
05470    struct ast_str *tmp = ast_str_create(16);
05471    char *tmpptr;
05472    struct ast_vm_user *vmu;
05473    struct ast_vm_user svm;
05474    const char *category = NULL;
05475    const char *code;
05476    const char *alldtmf = "0123456789ABCD*#";
05477    char flag[80];
05478 
05479    if (!tmp) {
05480       return -1;
05481    }
05482 
05483    ast_str_set(&tmp, 0, "%s", ext);
05484    ext = ast_str_buffer(tmp);
05485    if ((context = strchr(ext, '@'))) {
05486       *context++ = '\0';
05487       tmpptr = strchr(context, '&');
05488    } else {
05489       tmpptr = strchr(ext, '&');
05490    }
05491 
05492    if (tmpptr)
05493       *tmpptr++ = '\0';
05494 
05495    ast_channel_lock(chan);
05496    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05497       category = ast_strdupa(category);
05498    }
05499    ast_channel_unlock(chan);
05500 
05501    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05502       ast_copy_string(flag, "Urgent", sizeof(flag));
05503    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05504       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05505    } else {
05506       flag[0] = '\0';
05507    }
05508 
05509    ast_debug(3, "Before find_user\n");
05510    if (!(vmu = find_user(&svm, context, ext))) {
05511       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05512       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05513       ast_free(tmp);
05514       return res;
05515    }
05516    /* Setup pre-file if appropriate */
05517    if (strcmp(vmu->context, "default"))
05518       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05519    else
05520       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05521 
05522    /* Set the path to the prefile. Will be one of 
05523       VM_SPOOL_DIRcontext/ext/busy
05524       VM_SPOOL_DIRcontext/ext/unavail
05525       Depending on the flag set in options.
05526    */
05527    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05528       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05529    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05530       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05531    }
05532    /* Set the path to the tmpfile as
05533       VM_SPOOL_DIR/context/ext/temp
05534       and attempt to create the folder structure.
05535    */
05536    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05537    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05538       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05539       ast_free(tmp);
05540       return -1;
05541    }
05542    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05543    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05544       ast_copy_string(prefile, tempfile, sizeof(prefile));
05545 
05546    DISPOSE(tempfile, -1);
05547    /* It's easier just to try to make it than to check for its existence */
05548 #ifndef IMAP_STORAGE
05549    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05550 #else
05551    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05552    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05553       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05554    }
05555 #endif
05556 
05557    /* Check current or macro-calling context for special extensions */
05558    if (ast_test_flag(vmu, VM_OPERATOR)) {
05559       if (!ast_strlen_zero(vmu->exit)) {
05560          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05561             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05562             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05563             ouseexten = 1;
05564          }
05565       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05566          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05567          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05568          ouseexten = 1;
05569       } else if (!ast_strlen_zero(chan->macrocontext)
05570          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05571             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05572          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05573          ousemacro = 1;
05574       }
05575    }
05576 
05577    if (!ast_strlen_zero(vmu->exit)) {
05578       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05579          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05580          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05581       }
05582    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05583       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05584       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05585    } else if (!ast_strlen_zero(chan->macrocontext)
05586       && ast_exists_extension(chan, chan->macrocontext, "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       ausemacro = 1;
05590    }
05591 
05592    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05593       for (code = alldtmf; *code; code++) {
05594          char e[2] = "";
05595          e[0] = *code;
05596          if (strchr(ecodes, e[0]) == NULL
05597             && ast_canmatch_extension(chan, chan->context, e, 1,
05598                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05599             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05600          }
05601       }
05602    }
05603 
05604    /* Play the beginning intro if desired */
05605    if (!ast_strlen_zero(prefile)) {
05606 #ifdef ODBC_STORAGE
05607       int success = 
05608 #endif
05609          RETRIEVE(prefile, -1, ext, context);
05610       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05611          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05612             res = ast_waitstream(chan, ecodes);
05613 #ifdef ODBC_STORAGE
05614          if (success == -1) {
05615             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05616             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05617             store_file(prefile, vmu->mailbox, vmu->context, -1);
05618          }
05619 #endif
05620       } else {
05621          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05622          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05623       }
05624       DISPOSE(prefile, -1);
05625       if (res < 0) {
05626          ast_debug(1, "Hang up during prefile playback\n");
05627          free_user(vmu);
05628          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05629          ast_free(tmp);
05630          return -1;
05631       }
05632    }
05633    if (res == '#') {
05634       /* On a '#' we skip the instructions */
05635       ast_set_flag(options, OPT_SILENT);
05636       res = 0;
05637    }
05638    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05639    if (vmu->maxmsg == 0) {
05640       if (option_debug > 2)
05641          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05642       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05643       goto leave_vm_out;
05644    }
05645    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05646       res = ast_stream_and_wait(chan, INTRO, ecodes);
05647       if (res == '#') {
05648          ast_set_flag(options, OPT_SILENT);
05649          res = 0;
05650       }
05651    }
05652    if (res > 0)
05653       ast_stopstream(chan);
05654    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05655     other than the operator -- an automated attendant or mailbox login for example */
05656    if (res == '*') {
05657       chan->exten[0] = 'a';
05658       chan->exten[1] = '\0';
05659       if (!ast_strlen_zero(vmu->exit)) {
05660          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05661       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05662          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05663       }
05664       chan->priority = 0;
05665       free_user(vmu);
05666       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05667       ast_free(tmp);
05668       return 0;
05669    }
05670 
05671    /* Check for a '0' here */
05672    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05673    transfer:
05674       if (ouseexten || ousemacro) {
05675          chan->exten[0] = 'o';
05676          chan->exten[1] = '\0';
05677          if (!ast_strlen_zero(vmu->exit)) {
05678             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05679          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05680             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05681          }
05682          ast_play_and_wait(chan, "transfer");
05683          chan->priority = 0;
05684          free_user(vmu);
05685          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05686       }
05687       ast_free(tmp);
05688       return OPERATOR_EXIT;
05689    }
05690 
05691    /* Allow all other digits to exit Voicemail and return to the dialplan */
05692    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05693       if (!ast_strlen_zero(options->exitcontext))
05694          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05695       free_user(vmu);
05696       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05697       ast_free(tmp);
05698       return res;
05699    }
05700 
05701    if (res < 0) {
05702       free_user(vmu);
05703       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05704       ast_free(tmp);
05705       return -1;
05706    }
05707    /* The meat of recording the message...  All the announcements and beeps have been played*/
05708    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05709    if (!ast_strlen_zero(fmt)) {
05710       msgnum = 0;
05711 
05712 #ifdef IMAP_STORAGE
05713       /* Is ext a mailbox? */
05714       /* must open stream for this user to get info! */
05715       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05716       if (res < 0) {
05717          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05718          ast_free(tmp);
05719          return -1;
05720       }
05721       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05722       /* It is possible under certain circumstances that inboxcount did not
05723        * create a vm_state when it was needed. This is a catchall which will
05724        * rarely be used.
05725        */
05726          if (!(vms = create_vm_state_from_user(vmu))) {
05727             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05728             ast_free(tmp);
05729             return -1;
05730          }
05731       }
05732       vms->newmessages++;
05733       
05734       /* here is a big difference! We add one to it later */
05735       msgnum = newmsgs + oldmsgs;
05736       ast_debug(3, "Messagecount set to %d\n", msgnum);
05737       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05738       /* set variable for compatibility */
05739       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05740 
05741       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05742          goto leave_vm_out;
05743       }
05744 #else
05745       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05746          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05747          if (!res)
05748             res = ast_waitstream(chan, "");
05749          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05750          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05751          inprocess_count(vmu->mailbox, vmu->context, -1);
05752          goto leave_vm_out;
05753       }
05754 
05755 #endif
05756       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05757       txtdes = mkstemp(tmptxtfile);
05758       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05759       if (txtdes < 0) {
05760          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05761          if (!res)
05762             res = ast_waitstream(chan, "");
05763          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05764          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05765          inprocess_count(vmu->mailbox, vmu->context, -1);
05766          goto leave_vm_out;
05767       }
05768 
05769       /* Now play the beep once we have the message number for our next message. */
05770       if (res >= 0) {
05771          /* Unless we're *really* silent, try to send the beep */
05772          res = ast_stream_and_wait(chan, "beep", "");
05773       }
05774             
05775       /* Store information in real-time storage */
05776       if (ast_check_realtime("voicemail_data")) {
05777          snprintf(priority, sizeof(priority), "%d", chan->priority);
05778          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05779          get_date(date, sizeof(date));
05780          ast_callerid_merge(callerid, sizeof(callerid),
05781             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05782             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05783             "Unknown");
05784          ast_store_realtime("voicemail_data",
05785             "origmailbox", ext,
05786             "context", chan->context,
05787             "macrocontext", chan->macrocontext,
05788             "exten", chan->exten,
05789             "priority", priority,
05790             "callerchan", chan->name,
05791             "callerid", callerid,
05792             "origdate", date,
05793             "origtime", origtime,
05794             "category", S_OR(category, ""),
05795             "filename", tmptxtfile,
05796             SENTINEL);
05797       }
05798 
05799       /* Store information */
05800       txt = fdopen(txtdes, "w+");
05801       if (txt) {
05802          get_date(date, sizeof(date));
05803          ast_callerid_merge(callerid, sizeof(callerid),
05804             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05805             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05806             "Unknown");
05807          fprintf(txt, 
05808             ";\n"
05809             "; Message Information file\n"
05810             ";\n"
05811             "[message]\n"
05812             "origmailbox=%s\n"
05813             "context=%s\n"
05814             "macrocontext=%s\n"
05815             "exten=%s\n"
05816             "rdnis=%s\n"
05817             "priority=%d\n"
05818             "callerchan=%s\n"
05819             "callerid=%s\n"
05820             "origdate=%s\n"
05821             "origtime=%ld\n"
05822             "category=%s\n",
05823             ext,
05824             chan->context,
05825             chan->macrocontext, 
05826             chan->exten,
05827             S_COR(chan->redirecting.from.number.valid,
05828                chan->redirecting.from.number.str, "unknown"),
05829             chan->priority,
05830             chan->name,
05831             callerid,
05832             date, (long) time(NULL),
05833             category ? category : "");
05834       } else {
05835          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05836          inprocess_count(vmu->mailbox, vmu->context, -1);
05837          if (ast_check_realtime("voicemail_data")) {
05838             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05839          }
05840          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05841          goto leave_vm_out;
05842       }
05843       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05844 
05845       if (txt) {
05846          fprintf(txt, "flag=%s\n", flag);
05847          if (duration < vmu->minsecs) {
05848             fclose(txt);
05849             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05850             ast_filedelete(tmptxtfile, NULL);
05851             unlink(tmptxtfile);
05852             if (ast_check_realtime("voicemail_data")) {
05853                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05854             }
05855             inprocess_count(vmu->mailbox, vmu->context, -1);
05856          } else {
05857             fprintf(txt, "duration=%d\n", duration);
05858             fclose(txt);
05859             if (vm_lock_path(dir)) {
05860                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05861                /* Delete files */
05862                ast_filedelete(tmptxtfile, NULL);
05863                unlink(tmptxtfile);
05864                inprocess_count(vmu->mailbox, vmu->context, -1);
05865             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05866                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05867                unlink(tmptxtfile);
05868                ast_unlock_path(dir);
05869                inprocess_count(vmu->mailbox, vmu->context, -1);
05870                if (ast_check_realtime("voicemail_data")) {
05871                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05872                }
05873             } else {
05874 #ifndef IMAP_STORAGE
05875                msgnum = last_message_index(vmu, dir) + 1;
05876 #endif
05877                make_file(fn, sizeof(fn), dir, msgnum);
05878 
05879                /* assign a variable with the name of the voicemail file */ 
05880 #ifndef IMAP_STORAGE
05881                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05882 #else
05883                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05884 #endif
05885 
05886                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05887                ast_filerename(tmptxtfile, fn, NULL);
05888                rename(tmptxtfile, txtfile);
05889                inprocess_count(vmu->mailbox, vmu->context, -1);
05890 
05891                /* Properly set permissions on voicemail text descriptor file.
05892                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05893                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05894                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05895 
05896                ast_unlock_path(dir);
05897                if (ast_check_realtime("voicemail_data")) {
05898                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05899                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
05900                }
05901                /* We must store the file first, before copying the message, because
05902                 * ODBC storage does the entire copy with SQL.
05903                 */
05904                if (ast_fileexists(fn, NULL, NULL) > 0) {
05905                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05906                }
05907 
05908                /* Are there to be more recipients of this message? */
05909                while (tmpptr) {
05910                   struct ast_vm_user recipu, *recip;
05911                   char *exten, *cntx;
05912                
05913                   exten = strsep(&tmpptr, "&");
05914                   cntx = strchr(exten, '@');
05915                   if (cntx) {
05916                      *cntx = '\0';
05917                      cntx++;
05918                   }
05919                   if ((recip = find_user(&recipu, cntx, exten))) {
05920                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05921                      free_user(recip);
05922                   }
05923                }
05924 #ifndef IMAP_STORAGE
05925                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05926                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05927                   char sfn[PATH_MAX];
05928                   char dfn[PATH_MAX];
05929                   int x;
05930                   /* It's easier just to try to make it than to check for its existence */
05931                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05932                   x = last_message_index(vmu, urgdir) + 1;
05933                   make_file(sfn, sizeof(sfn), dir, msgnum);
05934                   make_file(dfn, sizeof(dfn), urgdir, x);
05935                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05936                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05937                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05938                   ast_copy_string(fn, dfn, sizeof(fn));
05939                   msgnum = x;
05940                }
05941 #endif
05942                /* Notification needs to happen after the copy, though. */
05943                if (ast_fileexists(fn, NULL, NULL)) {
05944 #ifdef IMAP_STORAGE
05945                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
05946                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05947                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05948                      flag);
05949 #else
05950                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
05951                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05952                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05953                      flag);
05954 #endif
05955                }
05956 
05957                /* Disposal needs to happen after the optional move and copy */
05958                if (ast_fileexists(fn, NULL, NULL)) {
05959                   DISPOSE(dir, msgnum);
05960                }
05961             }
05962          }
05963       } else {
05964          inprocess_count(vmu->mailbox, vmu->context, -1);
05965       }
05966       if (res == '0') {
05967          goto transfer;
05968       } else if (res > 0 && res != 't')
05969          res = 0;
05970 
05971       if (duration < vmu->minsecs)
05972          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05973          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05974       else
05975          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05976    } else
05977       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05978 leave_vm_out:
05979    free_user(vmu);
05980 
05981 #ifdef IMAP_STORAGE
05982    /* expunge message - use UID Expunge if supported on IMAP server*/
05983    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
05984    if (expungeonhangup == 1) {
05985       ast_mutex_lock(&vms->lock);
05986 #ifdef HAVE_IMAP_TK2006
05987       if (LEVELUIDPLUS (vms->mailstream)) {
05988          mail_expunge_full(vms->mailstream, NIL, EX_UID);
05989       } else 
05990 #endif
05991          mail_expunge(vms->mailstream);
05992       ast_mutex_unlock(&vms->lock);
05993    }
05994 #endif
05995 
05996    ast_free(tmp);
05997    return res;
05998 }
05999 
06000 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06001 {
06002    int d;
06003    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06004    return d;
06005 }
06006 
06007 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06008 {
06009 #ifdef IMAP_STORAGE
06010    /* we must use mbox(x) folder names, and copy the message there */
06011    /* simple. huh? */
06012    char sequence[10];
06013    char mailbox[256];
06014    int res;
06015 
06016    /* get the real IMAP message number for this message */
06017    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06018    
06019    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06020    ast_mutex_lock(&vms->lock);
06021    /* if save to Old folder, put in INBOX as read */
06022    if (box == OLD_FOLDER) {
06023       mail_setflag(vms->mailstream, sequence, "\\Seen");
06024       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06025    } else if (box == NEW_FOLDER) {
06026       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06027       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06028    }
06029    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06030       ast_mutex_unlock(&vms->lock);
06031       return 0;
06032    }
06033    /* Create the folder if it don't exist */
06034    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06035    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06036    if (mail_create(vms->mailstream, mailbox) == NIL) 
06037       ast_debug(5, "Folder exists.\n");
06038    else
06039       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06040    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06041    ast_mutex_unlock(&vms->lock);
06042    return res;
06043 #else
06044    char *dir = vms->curdir;
06045    char *username = vms->username;
06046    char *context = vmu->context;
06047    char sfn[PATH_MAX];
06048    char dfn[PATH_MAX];
06049    char ddir[PATH_MAX];
06050    const char *dbox = mbox(vmu, box);
06051    int x, i;
06052    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06053 
06054    if (vm_lock_path(ddir))
06055       return ERROR_LOCK_PATH;
06056 
06057    x = last_message_index(vmu, ddir) + 1;
06058 
06059    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06060       x--;
06061       for (i = 1; i <= x; i++) {
06062          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06063          make_file(sfn, sizeof(sfn), ddir, i);
06064          make_file(dfn, sizeof(dfn), ddir, i - 1);
06065          if (EXISTS(ddir, i, sfn, NULL)) {
06066             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06067          } else
06068             break;
06069       }
06070    } else {
06071       if (x >= vmu->maxmsg) {
06072          ast_unlock_path(ddir);
06073          return -1;
06074       }
06075    }
06076    make_file(sfn, sizeof(sfn), dir, msg);
06077    make_file(dfn, sizeof(dfn), ddir, x);
06078    if (strcmp(sfn, dfn)) {
06079       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06080    }
06081    ast_unlock_path(ddir);
06082 #endif
06083    return 0;
06084 }
06085 
06086 static int adsi_logo(unsigned char *buf)
06087 {
06088    int bytes = 0;
06089    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06090    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06091    return bytes;
06092 }
06093 
06094 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06095 {
06096    unsigned char buf[256];
06097    int bytes = 0;
06098    int x;
06099    char num[5];
06100 
06101    *useadsi = 0;
06102    bytes += ast_adsi_data_mode(buf + bytes);
06103    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06104 
06105    bytes = 0;
06106    bytes += adsi_logo(buf);
06107    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06108 #ifdef DISPLAY
06109    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06110 #endif
06111    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06112    bytes += ast_adsi_data_mode(buf + bytes);
06113    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06114 
06115    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06116       bytes = 0;
06117       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06118       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06119       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06120       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06121       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06122       return 0;
06123    }
06124 
06125 #ifdef DISPLAY
06126    /* Add a dot */
06127    bytes = 0;
06128    bytes += ast_adsi_logo(buf);
06129    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06130    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06131    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06132    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06133 #endif
06134    bytes = 0;
06135    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06136    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06137    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06138    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06139    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06140    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06141    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06142 
06143 #ifdef DISPLAY
06144    /* Add another dot */
06145    bytes = 0;
06146    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06147    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06148 
06149    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06150    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06151 #endif
06152 
06153    bytes = 0;
06154    /* These buttons we load but don't use yet */
06155    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06156    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06157    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06158    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06159    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06160    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06161    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06162 
06163 #ifdef DISPLAY
06164    /* Add another dot */
06165    bytes = 0;
06166    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06167    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06168    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06169 #endif
06170 
06171    bytes = 0;
06172    for (x = 0; x < 5; x++) {
06173       snprintf(num, sizeof(num), "%d", x);
06174       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06175    }
06176    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06177    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06178 
06179 #ifdef DISPLAY
06180    /* Add another dot */
06181    bytes = 0;
06182    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06183    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06184    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06185 #endif
06186 
06187    if (ast_adsi_end_download(chan)) {
06188       bytes = 0;
06189       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06190       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06191       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06192       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06193       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06194       return 0;
06195    }
06196    bytes = 0;
06197    bytes += ast_adsi_download_disconnect(buf + bytes);
06198    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06199    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06200 
06201    ast_debug(1, "Done downloading scripts...\n");
06202 
06203 #ifdef DISPLAY
06204    /* Add last dot */
06205    bytes = 0;
06206    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06207    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06208 #endif
06209    ast_debug(1, "Restarting session...\n");
06210 
06211    bytes = 0;
06212    /* Load the session now */
06213    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06214       *useadsi = 1;
06215       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06216    } else
06217       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06218 
06219    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06220    return 0;
06221 }
06222 
06223 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06224 {
06225    int x;
06226    if (!ast_adsi_available(chan))
06227       return;
06228    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06229    if (x < 0)
06230       return;
06231    if (!x) {
06232       if (adsi_load_vmail(chan, useadsi)) {
06233          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06234          return;
06235       }
06236    } else
06237       *useadsi = 1;
06238 }
06239 
06240 static void adsi_login(struct ast_channel *chan)
06241 {
06242    unsigned char buf[256];
06243    int bytes = 0;
06244    unsigned char keys[8];
06245    int x;
06246    if (!ast_adsi_available(chan))
06247       return;
06248 
06249    for (x = 0; x < 8; x++)
06250       keys[x] = 0;
06251    /* Set one key for next */
06252    keys[3] = ADSI_KEY_APPS + 3;
06253 
06254    bytes += adsi_logo(buf + bytes);
06255    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06256    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06257    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06258    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06259    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06260    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06261    bytes += ast_adsi_set_keys(buf + bytes, keys);
06262    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06263    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06264 }
06265 
06266 static void adsi_password(struct ast_channel *chan)
06267 {
06268    unsigned char buf[256];
06269    int bytes = 0;
06270    unsigned char keys[8];
06271    int x;
06272    if (!ast_adsi_available(chan))
06273       return;
06274 
06275    for (x = 0; x < 8; x++)
06276       keys[x] = 0;
06277    /* Set one key for next */
06278    keys[3] = ADSI_KEY_APPS + 3;
06279 
06280    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06281    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06282    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06283    bytes += ast_adsi_set_keys(buf + bytes, keys);
06284    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06285    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06286 }
06287 
06288 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06289 {
06290    unsigned char buf[256];
06291    int bytes = 0;
06292    unsigned char keys[8];
06293    int x, y;
06294 
06295    if (!ast_adsi_available(chan))
06296       return;
06297 
06298    for (x = 0; x < 5; x++) {
06299       y = ADSI_KEY_APPS + 12 + start + x;
06300       if (y > ADSI_KEY_APPS + 12 + 4)
06301          y = 0;
06302       keys[x] = ADSI_KEY_SKT | y;
06303    }
06304    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06305    keys[6] = 0;
06306    keys[7] = 0;
06307 
06308    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06309    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06310    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06311    bytes += ast_adsi_set_keys(buf + bytes, keys);
06312    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06313 
06314    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06315 }
06316 
06317 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06318 {
06319    int bytes = 0;
06320    unsigned char buf[256]; 
06321    char buf1[256], buf2[256];
06322    char fn2[PATH_MAX];
06323 
06324    char cid[256] = "";
06325    char *val;
06326    char *name, *num;
06327    char datetime[21] = "";
06328    FILE *f;
06329 
06330    unsigned char keys[8];
06331 
06332    int x;
06333 
06334    if (!ast_adsi_available(chan))
06335       return;
06336 
06337    /* Retrieve important info */
06338    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06339    f = fopen(fn2, "r");
06340    if (f) {
06341       while (!feof(f)) {   
06342          if (!fgets((char *) buf, sizeof(buf), f)) {
06343             continue;
06344          }
06345          if (!feof(f)) {
06346             char *stringp = NULL;
06347             stringp = (char *) buf;
06348             strsep(&stringp, "=");
06349             val = strsep(&stringp, "=");
06350             if (!ast_strlen_zero(val)) {
06351                if (!strcmp((char *) buf, "callerid"))
06352                   ast_copy_string(cid, val, sizeof(cid));
06353                if (!strcmp((char *) buf, "origdate"))
06354                   ast_copy_string(datetime, val, sizeof(datetime));
06355             }
06356          }
06357       }
06358       fclose(f);
06359    }
06360    /* New meaning for keys */
06361    for (x = 0; x < 5; x++)
06362       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06363    keys[6] = 0x0;
06364    keys[7] = 0x0;
06365 
06366    if (!vms->curmsg) {
06367       /* No prev key, provide "Folder" instead */
06368       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06369    }
06370    if (vms->curmsg >= vms->lastmsg) {
06371       /* If last message ... */
06372       if (vms->curmsg) {
06373          /* but not only message, provide "Folder" instead */
06374          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06375          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06376 
06377       } else {
06378          /* Otherwise if only message, leave blank */
06379          keys[3] = 1;
06380       }
06381    }
06382 
06383    if (!ast_strlen_zero(cid)) {
06384       ast_callerid_parse(cid, &name, &num);
06385       if (!name)
06386          name = num;
06387    } else
06388       name = "Unknown Caller";
06389 
06390    /* If deleted, show "undeleted" */
06391 
06392    if (vms->deleted[vms->curmsg])
06393       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06394 
06395    /* Except "Exit" */
06396    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06397    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06398       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06399    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06400 
06401    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06402    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06403    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06404    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06405    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06406    bytes += ast_adsi_set_keys(buf + bytes, keys);
06407    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06408 
06409    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06410 }
06411 
06412 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06413 {
06414    int bytes = 0;
06415    unsigned char buf[256];
06416    unsigned char keys[8];
06417 
06418    int x;
06419 
06420    if (!ast_adsi_available(chan))
06421       return;
06422 
06423    /* New meaning for keys */
06424    for (x = 0; x < 5; x++)
06425       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06426 
06427    keys[6] = 0x0;
06428    keys[7] = 0x0;
06429 
06430    if (!vms->curmsg) {
06431       /* No prev key, provide "Folder" instead */
06432       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06433    }
06434    if (vms->curmsg >= vms->lastmsg) {
06435       /* If last message ... */
06436       if (vms->curmsg) {
06437          /* but not only message, provide "Folder" instead */
06438          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06439       } else {
06440          /* Otherwise if only message, leave blank */
06441          keys[3] = 1;
06442       }
06443    }
06444 
06445    /* If deleted, show "undeleted" */
06446    if (vms->deleted[vms->curmsg]) 
06447       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06448 
06449    /* Except "Exit" */
06450    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06451    bytes += ast_adsi_set_keys(buf + bytes, keys);
06452    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06453 
06454    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06455 }
06456 
06457 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06458 {
06459    unsigned char buf[256] = "";
06460    char buf1[256] = "", buf2[256] = "";
06461    int bytes = 0;
06462    unsigned char keys[8];
06463    int x;
06464 
06465    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06466    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06467    if (!ast_adsi_available(chan))
06468       return;
06469    if (vms->newmessages) {
06470       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06471       if (vms->oldmessages) {
06472          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06473          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06474       } else {
06475          snprintf(buf2, sizeof(buf2), "%s.", newm);
06476       }
06477    } else if (vms->oldmessages) {
06478       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06479       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06480    } else {
06481       strcpy(buf1, "You have no messages.");
06482       buf2[0] = ' ';
06483       buf2[1] = '\0';
06484    }
06485    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06486    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06487    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06488 
06489    for (x = 0; x < 6; x++)
06490       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06491    keys[6] = 0;
06492    keys[7] = 0;
06493 
06494    /* Don't let them listen if there are none */
06495    if (vms->lastmsg < 0)
06496       keys[0] = 1;
06497    bytes += ast_adsi_set_keys(buf + bytes, keys);
06498 
06499    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06500 
06501    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06502 }
06503 
06504 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06505 {
06506    unsigned char buf[256] = "";
06507    char buf1[256] = "", buf2[256] = "";
06508    int bytes = 0;
06509    unsigned char keys[8];
06510    int x;
06511 
06512    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06513 
06514    if (!ast_adsi_available(chan))
06515       return;
06516 
06517    /* Original command keys */
06518    for (x = 0; x < 6; x++)
06519       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06520 
06521    keys[6] = 0;
06522    keys[7] = 0;
06523 
06524    if ((vms->lastmsg + 1) < 1)
06525       keys[0] = 0;
06526 
06527    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06528       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06529 
06530    if (vms->lastmsg + 1)
06531       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06532    else
06533       strcpy(buf2, "no messages.");
06534    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06535    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06536    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06537    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06538    bytes += ast_adsi_set_keys(buf + bytes, keys);
06539 
06540    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06541 
06542    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06543    
06544 }
06545 
06546 /*
06547 static void adsi_clear(struct ast_channel *chan)
06548 {
06549    char buf[256];
06550    int bytes=0;
06551    if (!ast_adsi_available(chan))
06552       return;
06553    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06554    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06555 
06556    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06557 }
06558 */
06559 
06560 static void adsi_goodbye(struct ast_channel *chan)
06561 {
06562    unsigned char buf[256];
06563    int bytes = 0;
06564 
06565    if (!ast_adsi_available(chan))
06566       return;
06567    bytes += adsi_logo(buf + bytes);
06568    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06569    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06570    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06571    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06572 
06573    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06574 }
06575 
06576 /*!\brief get_folder: Folder menu
06577  * Plays "press 1 for INBOX messages" etc.
06578  * Should possibly be internationalized
06579  */
06580 static int get_folder(struct ast_channel *chan, int start)
06581 {
06582    int x;
06583    int d;
06584    char fn[PATH_MAX];
06585    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06586    if (d)
06587       return d;
06588    for (x = start; x < 5; x++) { /* For all folders */
06589       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06590          return d;
06591       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06592       if (d)
06593          return d;
06594       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06595       d = vm_play_folder_name(chan, fn);
06596       if (d)
06597          return d;
06598       d = ast_waitfordigit(chan, 500);
06599       if (d)
06600          return d;
06601    }
06602    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06603    if (d)
06604       return d;
06605    d = ast_waitfordigit(chan, 4000);
06606    return d;
06607 }
06608 
06609 /*!
06610  * \brief plays a prompt and waits for a keypress.
06611  * \param chan
06612  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06613  * \param start Does not appear to be used at this time.
06614  *
06615  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06616  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06617  * prompting for the number inputs that correspond to the available folders.
06618  * 
06619  * \return zero on success, or -1 on error.
06620  */
06621 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06622 {
06623    int res = 0;
06624    int loops = 0;
06625    res = ast_play_and_wait(chan, fn);  /* Folder name */
06626    while (((res < '0') || (res > '9')) &&
06627          (res != '#') && (res >= 0) &&
06628          loops < 4) {
06629       res = get_folder(chan, 0);
06630       loops++;
06631    }
06632    if (loops == 4) { /* give up */
06633       return '#';
06634    }
06635    return res;
06636 }
06637 
06638 /*!
06639  * \brief presents the option to prepend to an existing message when forwarding it.
06640  * \param chan
06641  * \param vmu
06642  * \param curdir
06643  * \param curmsg
06644  * \param vm_fmts
06645  * \param context
06646  * \param record_gain
06647  * \param duration
06648  * \param vms
06649  * \param flag 
06650  *
06651  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06652  *
06653  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06654  * \return zero on success, -1 on error.
06655  */
06656 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06657          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06658 {
06659 #ifdef IMAP_STORAGE
06660    int res;
06661 #endif
06662    int cmd = 0;
06663    int retries = 0, prepend_duration = 0, already_recorded = 0;
06664    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06665    char textfile[PATH_MAX];
06666    struct ast_config *msg_cfg;
06667    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06668 #ifndef IMAP_STORAGE
06669    signed char zero_gain = 0;
06670 #endif
06671    const char *duration_str;
06672 
06673    /* Must always populate duration correctly */
06674    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06675    strcpy(textfile, msgfile);
06676    strcpy(backup, msgfile);
06677    strcpy(backup_textfile, msgfile);
06678    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06679    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06680    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06681 
06682    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06683       *duration = atoi(duration_str);
06684    } else {
06685       *duration = 0;
06686    }
06687 
06688    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06689       if (cmd)
06690          retries = 0;
06691       switch (cmd) {
06692       case '1': 
06693 
06694 #ifdef IMAP_STORAGE
06695          /* Record new intro file */
06696          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06697          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06698          res = ast_play_and_wait(chan, INTRO);
06699          res = ast_play_and_wait(chan, "beep");
06700          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06701          cmd = 't';
06702 #else
06703 
06704          /* prepend a message to the current message, update the metadata and return */
06705 
06706          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06707          strcpy(textfile, msgfile);
06708          strncat(textfile, ".txt", sizeof(textfile) - 1);
06709          *duration = 0;
06710 
06711          /* if we can't read the message metadata, stop now */
06712          if (!msg_cfg) {
06713             cmd = 0;
06714             break;
06715          }
06716          
06717          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06718          if (already_recorded) {
06719             ast_filecopy(backup, msgfile, NULL);
06720             copy(backup_textfile, textfile);
06721          }
06722          else {
06723             ast_filecopy(msgfile, backup, NULL);
06724             copy(textfile,backup_textfile);
06725          }
06726          already_recorded = 1;
06727 
06728          if (record_gain)
06729             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06730 
06731          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06732          if (cmd == 'S') {
06733             ast_filerename(backup, msgfile, NULL);
06734          }
06735 
06736          if (record_gain)
06737             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06738 
06739          
06740          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06741             *duration = atoi(duration_str);
06742 
06743          if (prepend_duration) {
06744             struct ast_category *msg_cat;
06745             /* need enough space for a maximum-length message duration */
06746             char duration_buf[12];
06747 
06748             *duration += prepend_duration;
06749             msg_cat = ast_category_get(msg_cfg, "message");
06750             snprintf(duration_buf, 11, "%ld", *duration);
06751             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06752                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06753             }
06754          }
06755 
06756 #endif
06757          break;
06758       case '2': 
06759          /* NULL out introfile so we know there is no intro! */
06760 #ifdef IMAP_STORAGE
06761          *vms->introfn = '\0';
06762 #endif
06763          cmd = 't';
06764          break;
06765       case '*':
06766          cmd = '*';
06767          break;
06768       default: 
06769          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06770             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06771          if (!cmd)
06772             cmd = ast_play_and_wait(chan, "vm-starmain");
06773             /* "press star to return to the main menu" */
06774          if (!cmd)
06775             cmd = ast_waitfordigit(chan, 6000);
06776          if (!cmd)
06777             retries++;
06778          if (retries > 3)
06779             cmd = 't';
06780       }
06781    }
06782 
06783    if (msg_cfg)
06784       ast_config_destroy(msg_cfg);
06785    if (prepend_duration)
06786       *duration = prepend_duration;
06787 
06788    if (already_recorded && cmd == -1) {
06789       /* restore original message if prepention cancelled */
06790       ast_filerename(backup, msgfile, NULL);
06791       rename(backup_textfile, textfile);
06792    }
06793 
06794    if (cmd == 't' || cmd == 'S')
06795       cmd = 0;
06796    return cmd;
06797 }
06798 
06799 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06800 {
06801    struct ast_event *event;
06802    char *mailbox, *context;
06803 
06804    /* Strip off @default */
06805    context = mailbox = ast_strdupa(box);
06806    strsep(&context, "@");
06807    if (ast_strlen_zero(context))
06808       context = "default";
06809 
06810    if (!(event = ast_event_new(AST_EVENT_MWI,
06811          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06812          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06813          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06814          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06815          AST_EVENT_IE_END))) {
06816       return;
06817    }
06818 
06819    ast_event_queue_and_cache(event);
06820 }
06821 
06822 /*!
06823  * \brief Sends email notification that a user has a new voicemail waiting for them.
06824  * \param chan
06825  * \param vmu
06826  * \param vms
06827  * \param msgnum
06828  * \param duration
06829  * \param fmt
06830  * \param cidnum The Caller ID phone number value.
06831  * \param cidname The Caller ID name value.
06832  * \param flag
06833  *
06834  * \return zero on success, -1 on error.
06835  */
06836 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)
06837 {
06838    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06839    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06840    const char *category;
06841    char *myserveremail = serveremail;
06842 
06843    ast_channel_lock(chan);
06844    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06845       category = ast_strdupa(category);
06846    }
06847    ast_channel_unlock(chan);
06848 
06849 #ifndef IMAP_STORAGE
06850    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06851 #else
06852    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
06853 #endif
06854    make_file(fn, sizeof(fn), todir, msgnum);
06855    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06856 
06857    if (!ast_strlen_zero(vmu->attachfmt)) {
06858       if (strstr(fmt, vmu->attachfmt))
06859          fmt = vmu->attachfmt;
06860       else
06861          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);
06862    }
06863 
06864    /* Attach only the first format */
06865    fmt = ast_strdupa(fmt);
06866    stringp = fmt;
06867    strsep(&stringp, "|");
06868 
06869    if (!ast_strlen_zero(vmu->serveremail))
06870       myserveremail = vmu->serveremail;
06871 
06872    if (!ast_strlen_zero(vmu->email)) {
06873       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06874 
06875       if (attach_user_voicemail)
06876          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06877 
06878       /* XXX possible imap issue, should category be NULL XXX */
06879       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06880 
06881       if (attach_user_voicemail)
06882          DISPOSE(todir, msgnum);
06883    }
06884 
06885    if (!ast_strlen_zero(vmu->pager)) {
06886       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
06887    }
06888 
06889    if (ast_test_flag(vmu, VM_DELETE))
06890       DELETE(todir, msgnum, fn, vmu);
06891 
06892    /* Leave voicemail for someone */
06893    if (ast_app_has_voicemail(ext_context, NULL)) 
06894       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06895 
06896    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06897 
06898    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);
06899    run_externnotify(vmu->context, vmu->mailbox, flag);
06900 
06901 #ifdef IMAP_STORAGE
06902    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06903    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06904       vm_imap_delete(NULL, vms->curmsg, vmu);
06905       vms->newmessages--;  /* Fix new message count */
06906    }
06907 #endif
06908 
06909    return 0;
06910 }
06911 
06912 /*!
06913  * \brief Sends a voicemail message to a mailbox recipient.
06914  * \param chan
06915  * \param context
06916  * \param vms
06917  * \param sender
06918  * \param fmt
06919  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06920  *             Will be 0 when called to forward an existing message (option 8)
06921  *             Will be 1 when called to leave a message (option 3->5)
06922  * \param record_gain 
06923  * \param urgent
06924  *
06925  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06926  * 
06927  * When in the leave message mode (is_new_message == 1):
06928  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06929  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06930  *
06931  * When in the forward message mode (is_new_message == 0):
06932  *   - retreives the current message to be forwarded
06933  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06934  *   - determines the target mailbox and folders
06935  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06936  *
06937  * \return zero on success, -1 on error.
06938  */
06939 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)
06940 {
06941 #ifdef IMAP_STORAGE
06942    int todircount = 0;
06943    struct vm_state *dstvms;
06944 #endif
06945    char username[70]="";
06946    char fn[PATH_MAX]; /* for playback of name greeting */
06947    char ecodes[16] = "#";
06948    int res = 0, cmd = 0;
06949    struct ast_vm_user *receiver = NULL, *vmtmp;
06950    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06951    char *stringp;
06952    const char *s;
06953    int saved_messages = 0;
06954    int valid_extensions = 0;
06955    char *dir;
06956    int curmsg;
06957    char urgent_str[7] = "";
06958    char tmptxtfile[PATH_MAX];
06959    int prompt_played = 0;
06960 #ifndef IMAP_STORAGE
06961    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06962 #endif
06963    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06964       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06965    }
06966 
06967    if (vms == NULL) return -1;
06968    dir = vms->curdir;
06969    curmsg = vms->curmsg;
06970 
06971    tmptxtfile[0] = '\0';
06972    while (!res && !valid_extensions) {
06973       int use_directory = 0;
06974       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06975          int done = 0;
06976          int retries = 0;
06977          cmd = 0;
06978          while ((cmd >= 0) && !done ){
06979             if (cmd)
06980                retries = 0;
06981             switch (cmd) {
06982             case '1': 
06983                use_directory = 0;
06984                done = 1;
06985                break;
06986             case '2': 
06987                use_directory = 1;
06988                done = 1;
06989                break;
06990             case '*': 
06991                cmd = 't';
06992                done = 1;
06993                break;
06994             default: 
06995                /* Press 1 to enter an extension press 2 to use the directory */
06996                cmd = ast_play_and_wait(chan, "vm-forward");
06997                if (!cmd)
06998                   cmd = ast_waitfordigit(chan, 3000);
06999                if (!cmd)
07000                   retries++;
07001                if (retries > 3) {
07002                   cmd = 't';
07003                   done = 1;
07004                }
07005                
07006             }
07007          }
07008          if (cmd < 0 || cmd == 't')
07009             break;
07010       }
07011       
07012       if (use_directory) {
07013          /* use app_directory */
07014          
07015          char old_context[sizeof(chan->context)];
07016          char old_exten[sizeof(chan->exten)];
07017          int old_priority;
07018          struct ast_app* directory_app;
07019 
07020          directory_app = pbx_findapp("Directory");
07021          if (directory_app) {
07022             char vmcontext[256];
07023             /* make backup copies */
07024             memcpy(old_context, chan->context, sizeof(chan->context));
07025             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07026             old_priority = chan->priority;
07027             
07028             /* call the the Directory, changes the channel */
07029             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07030             res = pbx_exec(chan, directory_app, vmcontext);
07031             
07032             ast_copy_string(username, chan->exten, sizeof(username));
07033             
07034             /* restore the old context, exten, and priority */
07035             memcpy(chan->context, old_context, sizeof(chan->context));
07036             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07037             chan->priority = old_priority;
07038          } else {
07039             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07040             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07041          }
07042       } else {
07043          /* Ask for an extension */
07044          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07045          prompt_played++;
07046          if (res || prompt_played > 4)
07047             break;
07048          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07049             break;
07050       }
07051       
07052       /* start all over if no username */
07053       if (ast_strlen_zero(username))
07054          continue;
07055       stringp = username;
07056       s = strsep(&stringp, "*");
07057       /* start optimistic */
07058       valid_extensions = 1;
07059       while (s) {
07060          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07061             int oldmsgs;
07062             int newmsgs;
07063             int capacity;
07064             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07065                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07066                /* Shouldn't happen, but allow trying another extension if it does */
07067                res = ast_play_and_wait(chan, "pbx-invalid");
07068                valid_extensions = 0;
07069                break;
07070             }
07071             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07072             if ((newmsgs + oldmsgs) >= capacity) {
07073                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07074                res = ast_play_and_wait(chan, "vm-mailboxfull");
07075                valid_extensions = 0;
07076                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07077                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07078                   free_user(vmtmp);
07079                }
07080                inprocess_count(receiver->mailbox, receiver->context, -1);
07081                break;
07082             }
07083             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07084          } else {
07085             /* XXX Optimization for the future.  When we encounter a single bad extension,
07086              * bailing out on all of the extensions may not be the way to go.  We should
07087              * probably just bail on that single extension, then allow the user to enter
07088              * several more. XXX
07089              */
07090             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07091                free_user(receiver);
07092             }
07093             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07094             /* "I am sorry, that's not a valid extension.  Please try again." */
07095             res = ast_play_and_wait(chan, "pbx-invalid");
07096             valid_extensions = 0;
07097             break;
07098          }
07099 
07100          /* play name if available, else play extension number */
07101          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07102          RETRIEVE(fn, -1, s, receiver->context);
07103          if (ast_fileexists(fn, NULL, NULL) > 0) {
07104             res = ast_stream_and_wait(chan, fn, ecodes);
07105             if (res) {
07106                DISPOSE(fn, -1);
07107                return res;
07108             }
07109          } else {
07110             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07111          }
07112          DISPOSE(fn, -1);
07113 
07114          s = strsep(&stringp, "*");
07115       }
07116       /* break from the loop of reading the extensions */
07117       if (valid_extensions)
07118          break;
07119    }
07120    /* check if we're clear to proceed */
07121    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07122       return res;
07123    if (is_new_message == 1) {
07124       struct leave_vm_options leave_options;
07125       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07126       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07127 
07128       /* Send VoiceMail */
07129       memset(&leave_options, 0, sizeof(leave_options));
07130       leave_options.record_gain = record_gain;
07131       cmd = leave_voicemail(chan, mailbox, &leave_options);
07132    } else {
07133       /* Forward VoiceMail */
07134       long duration = 0;
07135       struct vm_state vmstmp;
07136       int copy_msg_result = 0;
07137       memcpy(&vmstmp, vms, sizeof(vmstmp));
07138 
07139       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07140 
07141       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07142       if (!cmd) {
07143          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07144 #ifdef IMAP_STORAGE
07145             int attach_user_voicemail;
07146             char *myserveremail = serveremail;
07147             
07148             /* get destination mailbox */
07149             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07150             if (!dstvms) {
07151                dstvms = create_vm_state_from_user(vmtmp);
07152             }
07153             if (dstvms) {
07154                init_mailstream(dstvms, 0);
07155                if (!dstvms->mailstream) {
07156                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07157                } else {
07158                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07159                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07160                }
07161             } else {
07162                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07163             }
07164             if (!ast_strlen_zero(vmtmp->serveremail))
07165                myserveremail = vmtmp->serveremail;
07166             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07167             /* NULL category for IMAP storage */
07168             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07169                dstvms->curbox,
07170                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07171                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07172                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07173                NULL, urgent_str);
07174 #else
07175             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07176 #endif
07177             saved_messages++;
07178             AST_LIST_REMOVE_CURRENT(list);
07179             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07180             free_user(vmtmp);
07181             if (res)
07182                break;
07183          }
07184          AST_LIST_TRAVERSE_SAFE_END;
07185          if (saved_messages > 0 && !copy_msg_result) {
07186             /* give confirmation that the message was saved */
07187             /* commented out since we can't forward batches yet
07188             if (saved_messages == 1)
07189                res = ast_play_and_wait(chan, "vm-message");
07190             else
07191                res = ast_play_and_wait(chan, "vm-messages");
07192             if (!res)
07193                res = ast_play_and_wait(chan, "vm-saved"); */
07194 #ifdef IMAP_STORAGE
07195             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07196             if (ast_strlen_zero(vmstmp.introfn))
07197 #endif
07198             res = ast_play_and_wait(chan, "vm-msgsaved");
07199          }
07200 #ifndef IMAP_STORAGE
07201          else {
07202             /* with IMAP, mailbox full warning played by imap_check_limits */
07203             res = ast_play_and_wait(chan, "vm-mailboxfull");
07204          }
07205          /* Restore original message without prepended message if backup exists */
07206          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07207          strcpy(textfile, msgfile);
07208          strcpy(backup, msgfile);
07209          strcpy(backup_textfile, msgfile);
07210          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07211          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07212          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07213          if (ast_fileexists(backup, NULL, NULL) > 0) {
07214             ast_filerename(backup, msgfile, NULL);
07215             rename(backup_textfile, textfile);
07216          }
07217 #endif
07218       }
07219       DISPOSE(dir, curmsg);
07220 #ifndef IMAP_STORAGE
07221       if (cmd) { /* assuming hangup, cleanup backup file */
07222          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07223          strcpy(textfile, msgfile);
07224          strcpy(backup_textfile, msgfile);
07225          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07226          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07227          rename(backup_textfile, textfile);
07228       }
07229 #endif
07230    }
07231 
07232    /* If anything failed above, we still have this list to free */
07233    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07234       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07235       free_user(vmtmp);
07236    }
07237    return res ? res : cmd;
07238 }
07239 
07240 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07241 {
07242    int res;
07243    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07244       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07245    return res;
07246 }
07247 
07248 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07249 {
07250    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);
07251 }
07252 
07253 static int play_message_category(struct ast_channel *chan, const char *category)
07254 {
07255    int res = 0;
07256 
07257    if (!ast_strlen_zero(category))
07258       res = ast_play_and_wait(chan, category);
07259 
07260    if (res) {
07261       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07262       res = 0;
07263    }
07264 
07265    return res;
07266 }
07267 
07268 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07269 {
07270    int res = 0;
07271    struct vm_zone *the_zone = NULL;
07272    time_t t;
07273 
07274    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07275       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07276       return 0;
07277    }
07278 
07279    /* Does this user have a timezone specified? */
07280    if (!ast_strlen_zero(vmu->zonetag)) {
07281       /* Find the zone in the list */
07282       struct vm_zone *z;
07283       AST_LIST_LOCK(&zones);
07284       AST_LIST_TRAVERSE(&zones, z, list) {
07285          if (!strcmp(z->name, vmu->zonetag)) {
07286             the_zone = z;
07287             break;
07288          }
07289       }
07290       AST_LIST_UNLOCK(&zones);
07291    }
07292 
07293 /* No internal variable parsing for now, so we'll comment it out for the time being */
07294 #if 0
07295    /* Set the DIFF_* variables */
07296    ast_localtime(&t, &time_now, NULL);
07297    tv_now = ast_tvnow();
07298    ast_localtime(&tv_now, &time_then, NULL);
07299 
07300    /* Day difference */
07301    if (time_now.tm_year == time_then.tm_year)
07302       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07303    else
07304       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07305    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07306 
07307    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07308 #endif
07309    if (the_zone) {
07310       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07311    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07312       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07313    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07314       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07315    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07316       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);
07317    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07318       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07319    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07320       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07321    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07322       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07323    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07324       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);
07325    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07326       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07327    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07328       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07329    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07330       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);
07331    } else {
07332       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07333    }
07334 #if 0
07335    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07336 #endif
07337    return res;
07338 }
07339 
07340 
07341 
07342 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07343 {
07344    int res = 0;
07345    int i;
07346    char *callerid, *name;
07347    char prefile[PATH_MAX] = "";
07348    
07349 
07350    /* If voicemail cid is not enabled, or we didn't get cid or context from
07351     * the attribute file, leave now.
07352     *
07353     * TODO Still need to change this so that if this function is called by the
07354     * message envelope (and someone is explicitly requesting to hear the CID),
07355     * it does not check to see if CID is enabled in the config file.
07356     */
07357    if ((cid == NULL)||(context == NULL))
07358       return res;
07359 
07360    /* Strip off caller ID number from name */
07361    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07362    ast_callerid_parse(cid, &name, &callerid);
07363    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07364       /* Check for internal contexts and only */
07365       /* say extension when the call didn't come from an internal context in the list */
07366       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07367          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07368          if ((strcmp(cidinternalcontexts[i], context) == 0))
07369             break;
07370       }
07371       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07372          if (!res) {
07373             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07374             if (!ast_strlen_zero(prefile)) {
07375             /* See if we can find a recorded name for this person instead of their extension number */
07376                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07377                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07378                   if (!callback)
07379                      res = wait_file2(chan, vms, "vm-from");
07380                   res = ast_stream_and_wait(chan, prefile, "");
07381                } else {
07382                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07383                   /* Say "from extension" as one saying to sound smoother */
07384                   if (!callback)
07385                      res = wait_file2(chan, vms, "vm-from-extension");
07386                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07387                }
07388             }
07389          }
07390       } else if (!res) {
07391          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07392          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07393          if (!callback)
07394             res = wait_file2(chan, vms, "vm-from-phonenumber");
07395          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07396       }
07397    } else {
07398       /* Number unknown */
07399       ast_debug(1, "VM-CID: From an unknown number\n");
07400       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07401       res = wait_file2(chan, vms, "vm-unknown-caller");
07402    }
07403    return res;
07404 }
07405 
07406 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07407 {
07408    int res = 0;
07409    int durationm;
07410    int durations;
07411    /* Verify that we have a duration for the message */
07412    if (duration == NULL)
07413       return res;
07414 
07415    /* Convert from seconds to minutes */
07416    durations = atoi(duration);
07417    durationm = (durations / 60);
07418 
07419    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07420 
07421    if ((!res) && (durationm >= minduration)) {
07422       res = wait_file2(chan, vms, "vm-duration");
07423 
07424       /* POLISH syntax */
07425       if (!strncasecmp(chan->language, "pl", 2)) {
07426          div_t num = div(durationm, 10);
07427 
07428          if (durationm == 1) {
07429             res = ast_play_and_wait(chan, "digits/1z");
07430             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07431          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07432             if (num.rem == 2) {
07433                if (!num.quot) {
07434                   res = ast_play_and_wait(chan, "digits/2-ie");
07435                } else {
07436                   res = say_and_wait(chan, durationm - 2 , chan->language);
07437                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07438                }
07439             } else {
07440                res = say_and_wait(chan, durationm, chan->language);
07441             }
07442             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07443          } else {
07444             res = say_and_wait(chan, durationm, chan->language);
07445             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07446          }
07447       /* DEFAULT syntax */
07448       } else {
07449          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07450          res = wait_file2(chan, vms, "vm-minutes");
07451       }
07452    }
07453    return res;
07454 }
07455 
07456 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07457 {
07458    int res = 0;
07459    char filename[256], *cid;
07460    const char *origtime, *context, *category, *duration, *flag;
07461    struct ast_config *msg_cfg;
07462    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07463 
07464    vms->starting = 0;
07465    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07466    adsi_message(chan, vms);
07467    if (!vms->curmsg)
07468       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07469    else if (vms->curmsg == vms->lastmsg)
07470       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07471 
07472    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07473    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07474    msg_cfg = ast_config_load(filename, config_flags);
07475    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07476       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07477       return 0;
07478    }
07479    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07480 
07481    /* Play the word urgent if we are listening to urgent messages */
07482    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07483       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07484    }
07485 
07486    if (!res) {
07487       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07488       /* POLISH syntax */
07489       if (!strncasecmp(chan->language, "pl", 2)) {
07490          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07491             int ten, one;
07492             char nextmsg[256];
07493             ten = (vms->curmsg + 1) / 10;
07494             one = (vms->curmsg + 1) % 10;
07495 
07496             if (vms->curmsg < 20) {
07497                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07498                res = wait_file2(chan, vms, nextmsg);
07499             } else {
07500                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07501                res = wait_file2(chan, vms, nextmsg);
07502                if (one > 0) {
07503                   if (!res) {
07504                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07505                      res = wait_file2(chan, vms, nextmsg);
07506                   }
07507                }
07508             }
07509          }
07510          if (!res)
07511             res = wait_file2(chan, vms, "vm-message");
07512       /* HEBREW syntax */
07513       } else if (!strncasecmp(chan->language, "he", 2)) {
07514          if (!vms->curmsg) {
07515             res = wait_file2(chan, vms, "vm-message");
07516             res = wait_file2(chan, vms, "vm-first");
07517          } else if (vms->curmsg == vms->lastmsg) {
07518             res = wait_file2(chan, vms, "vm-message");
07519             res = wait_file2(chan, vms, "vm-last");
07520          } else {
07521             res = wait_file2(chan, vms, "vm-message");
07522             res = wait_file2(chan, vms, "vm-number");
07523             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07524          }
07525       /* VIETNAMESE syntax */
07526       } else if (!strncasecmp(chan->language, "vi", 2)) {
07527          if (!vms->curmsg) {
07528             res = wait_file2(chan, vms, "vm-message");
07529             res = wait_file2(chan, vms, "vm-first");
07530          } else if (vms->curmsg == vms->lastmsg) {
07531             res = wait_file2(chan, vms, "vm-message");
07532             res = wait_file2(chan, vms, "vm-last");
07533          } else {
07534             res = wait_file2(chan, vms, "vm-message");
07535             res = wait_file2(chan, vms, "vm-number");
07536             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07537          }
07538       } else {
07539          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07540             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07541          } else { /* DEFAULT syntax */
07542             res = wait_file2(chan, vms, "vm-message");
07543          }
07544          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07545             if (!res) {
07546                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07547             }
07548          }
07549       }
07550    }
07551 
07552    if (!msg_cfg) {
07553       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07554       return 0;
07555    }
07556 
07557    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07558       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07559       DISPOSE(vms->curdir, vms->curmsg);
07560       ast_config_destroy(msg_cfg);
07561       return 0;
07562    }
07563 
07564    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07565    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07566    category = ast_variable_retrieve(msg_cfg, "message", "category");
07567 
07568    context = ast_variable_retrieve(msg_cfg, "message", "context");
07569    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07570       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07571    if (!res) {
07572       res = play_message_category(chan, category);
07573    }
07574    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07575       res = play_message_datetime(chan, vmu, origtime, filename);
07576    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07577       res = play_message_callerid(chan, vms, cid, context, 0);
07578    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07579       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07580    /* Allow pressing '1' to skip envelope / callerid */
07581    if (res == '1')
07582       res = 0;
07583    ast_config_destroy(msg_cfg);
07584 
07585    if (!res) {
07586       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07587       vms->heard[vms->curmsg] = 1;
07588 #ifdef IMAP_STORAGE
07589       /*IMAP storage stores any prepended message from a forward
07590        * as a separate file from the rest of the message
07591        */
07592       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07593          wait_file(chan, vms, vms->introfn);
07594       }
07595 #endif
07596       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07597          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07598          res = 0;
07599       }
07600    }
07601    DISPOSE(vms->curdir, vms->curmsg);
07602    return res;
07603 }
07604 
07605 #ifdef IMAP_STORAGE
07606 static int imap_remove_file(char *dir, int msgnum)
07607 {
07608    char fn[PATH_MAX];
07609    char full_fn[PATH_MAX];
07610    char intro[PATH_MAX] = {0,};
07611    
07612    if (msgnum > -1) {
07613       make_file(fn, sizeof(fn), dir, msgnum);
07614       snprintf(intro, sizeof(intro), "%sintro", fn);
07615    } else
07616       ast_copy_string(fn, dir, sizeof(fn));
07617    
07618    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07619       ast_filedelete(fn, NULL);
07620       if (!ast_strlen_zero(intro)) {
07621          ast_filedelete(intro, NULL);
07622       }
07623       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07624       unlink(full_fn);
07625    }
07626    return 0;
07627 }
07628 
07629 
07630 
07631 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07632 {
07633    char *file, *filename;
07634    char *attachment;
07635    char arg[10];
07636    int i;
07637    BODY* body;
07638 
07639    file = strrchr(ast_strdupa(dir), '/');
07640    if (file) {
07641       *file++ = '\0';
07642    } else {
07643       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07644       return -1;
07645    }
07646 
07647    ast_mutex_lock(&vms->lock);
07648    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07649       mail_fetchstructure(vms->mailstream, i + 1, &body);
07650       /* We have the body, now we extract the file name of the first attachment. */
07651       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07652          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07653       } else {
07654          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07655          ast_mutex_unlock(&vms->lock);
07656          return -1;
07657       }
07658       filename = strsep(&attachment, ".");
07659       if (!strcmp(filename, file)) {
07660          sprintf(arg, "%d", i + 1);
07661          mail_setflag(vms->mailstream, arg, "\\DELETED");
07662       }
07663    }
07664    mail_expunge(vms->mailstream);
07665    ast_mutex_unlock(&vms->lock);
07666    return 0;
07667 }
07668 
07669 #elif !defined(IMAP_STORAGE)
07670 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07671 {
07672    int count_msg, last_msg;
07673 
07674    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07675 
07676    /* Rename the member vmbox HERE so that we don't try to return before
07677     * we know what's going on.
07678     */
07679    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07680 
07681    /* Faster to make the directory than to check if it exists. */
07682    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07683 
07684    /* traverses directory using readdir (or select query for ODBC) */
07685    count_msg = count_messages(vmu, vms->curdir);
07686    if (count_msg < 0) {
07687       return count_msg;
07688    } else {
07689       vms->lastmsg = count_msg - 1;
07690    }
07691 
07692    if (vm_allocate_dh(vms, vmu, count_msg)) {
07693       return -1;
07694    }
07695 
07696    /*
07697    The following test is needed in case sequencing gets messed up.
07698    There appears to be more than one way to mess up sequence, so
07699    we will not try to find all of the root causes--just fix it when
07700    detected.
07701    */
07702 
07703    if (vm_lock_path(vms->curdir)) {
07704       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07705       return ERROR_LOCK_PATH;
07706    }
07707 
07708    /* for local storage, checks directory for messages up to maxmsg limit */
07709    last_msg = last_message_index(vmu, vms->curdir);
07710    ast_unlock_path(vms->curdir);
07711 
07712    if (last_msg < -1) {
07713       return last_msg;
07714    } else if (vms->lastmsg != last_msg) {
07715       ast_log(LOG_NOTICE, "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);
07716    }
07717 
07718    return 0;
07719 }
07720 #endif
07721 
07722 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07723 {
07724    int x = 0;
07725 #ifndef IMAP_STORAGE
07726    int res = 0, nummsg;
07727    char fn2[PATH_MAX];
07728 #endif
07729 
07730    if (vms->lastmsg <= -1) {
07731       goto done;
07732    }
07733 
07734    vms->curmsg = -1;
07735 #ifndef IMAP_STORAGE
07736    /* Get the deleted messages fixed */
07737    if (vm_lock_path(vms->curdir)) {
07738       return ERROR_LOCK_PATH;
07739    }
07740 
07741    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07742    for (x = 0; x < vms->lastmsg + 1; x++) {
07743       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07744          /* Save this message.  It's not in INBOX or hasn't been heard */
07745          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07746          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07747             break;
07748          }
07749          vms->curmsg++;
07750          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07751          if (strcmp(vms->fn, fn2)) {
07752             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07753          }
07754       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07755          /* Move to old folder before deleting */
07756          res = save_to_folder(vmu, vms, x, 1);
07757          if (res == ERROR_LOCK_PATH) {
07758             /* If save failed do not delete the message */
07759             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07760             vms->deleted[x] = 0;
07761             vms->heard[x] = 0;
07762             --x;
07763          }
07764       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07765          /* Move to deleted folder */
07766          res = save_to_folder(vmu, vms, x, 10);
07767          if (res == ERROR_LOCK_PATH) {
07768             /* If save failed do not delete the message */
07769             vms->deleted[x] = 0;
07770             vms->heard[x] = 0;
07771             --x;
07772          }
07773       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07774          /* If realtime storage enabled - we should explicitly delete this message,
07775          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07776          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07777          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07778             DELETE(vms->curdir, x, vms->fn, vmu);
07779          }
07780       }
07781    }
07782 
07783    /* Delete ALL remaining messages */
07784    nummsg = x - 1;
07785    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07786       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07787       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07788          DELETE(vms->curdir, x, vms->fn, vmu);
07789       }
07790    }
07791    ast_unlock_path(vms->curdir);
07792 #else /* defined(IMAP_STORAGE) */
07793    if (vms->deleted) {
07794       /* Since we now expunge after each delete, deleting in reverse order
07795        * ensures that no reordering occurs between each step. */
07796       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
07797          if (vms->deleted[x]) {
07798             ast_debug(3, "IMAP delete of %d\n", x);
07799             DELETE(vms->curdir, x, vms->fn, vmu);
07800          }
07801       }
07802    }
07803 #endif
07804 
07805 done:
07806    if (vms->deleted && vmu->maxmsg) {
07807       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
07808    }
07809    if (vms->heard && vmu->maxmsg) {
07810       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
07811    }
07812 
07813    return 0;
07814 }
07815 
07816 /* In Greek even though we CAN use a syntax like "friends messages"
07817  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07818  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
07819  * syntax for the above three categories which is more elegant.
07820  */
07821 
07822 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07823 {
07824    int cmd;
07825    char *buf;
07826 
07827    buf = alloca(strlen(box) + 2);
07828    strcpy(buf, box);
07829    strcat(buf, "s");
07830 
07831    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07832       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07833       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07834    } else {
07835       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07836       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07837    }
07838 }
07839 
07840 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07841 {
07842    int cmd;
07843 
07844    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07845       if (!strcasecmp(box, "vm-INBOX"))
07846          cmd = ast_play_and_wait(chan, "vm-new-e");
07847       else
07848          cmd = ast_play_and_wait(chan, "vm-old-e");
07849       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07850    } else {
07851       cmd = ast_play_and_wait(chan, "vm-messages");
07852       return cmd ? cmd : ast_play_and_wait(chan, box);
07853    }
07854 }
07855 
07856 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07857 {
07858    int cmd;
07859 
07860    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07861       cmd = ast_play_and_wait(chan, "vm-messages");
07862       return cmd ? cmd : ast_play_and_wait(chan, box);
07863    } else {
07864       cmd = ast_play_and_wait(chan, box);
07865       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07866    }
07867 }
07868 
07869 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07870 {
07871    int cmd;
07872 
07873    if (  !strncasecmp(chan->language, "it", 2) ||
07874         !strncasecmp(chan->language, "es", 2) ||
07875         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07876       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07877       return cmd ? cmd : ast_play_and_wait(chan, box);
07878    } else if (!strncasecmp(chan->language, "gr", 2)) {
07879       return vm_play_folder_name_gr(chan, box);
07880    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07881       return ast_play_and_wait(chan, box);
07882    } else if (!strncasecmp(chan->language, "pl", 2)) {
07883       return vm_play_folder_name_pl(chan, box);
07884    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07885       return vm_play_folder_name_ua(chan, box);
07886    } else if (!strncasecmp(chan->language, "vi", 2)) {
07887       return ast_play_and_wait(chan, box);
07888    } else {  /* Default English */
07889       cmd = ast_play_and_wait(chan, box);
07890       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07891    }
07892 }
07893 
07894 /* GREEK SYNTAX
07895    In greek the plural for old/new is
07896    different so we need the following files
07897    We also need vm-denExeteMynhmata because
07898    this syntax is different.
07899 
07900    -> vm-Olds.wav : "Palia"
07901    -> vm-INBOXs.wav : "Nea"
07902    -> vm-denExeteMynhmata : "den exete mynhmata"
07903 */
07904 
07905 
07906 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07907 {
07908    int res = 0;
07909 
07910    if (vms->newmessages) {
07911       res = ast_play_and_wait(chan, "vm-youhave");
07912       if (!res) 
07913          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07914       if (!res) {
07915          if ((vms->newmessages == 1)) {
07916             res = ast_play_and_wait(chan, "vm-INBOX");
07917             if (!res)
07918                res = ast_play_and_wait(chan, "vm-message");
07919          } else {
07920             res = ast_play_and_wait(chan, "vm-INBOXs");
07921             if (!res)
07922                res = ast_play_and_wait(chan, "vm-messages");
07923          }
07924       }
07925    } else if (vms->oldmessages){
07926       res = ast_play_and_wait(chan, "vm-youhave");
07927       if (!res)
07928          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07929       if ((vms->oldmessages == 1)){
07930          res = ast_play_and_wait(chan, "vm-Old");
07931          if (!res)
07932             res = ast_play_and_wait(chan, "vm-message");
07933       } else {
07934          res = ast_play_and_wait(chan, "vm-Olds");
07935          if (!res)
07936             res = ast_play_and_wait(chan, "vm-messages");
07937       }
07938    } else if (!vms->oldmessages && !vms->newmessages) 
07939       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07940    return res;
07941 }
07942 
07943 /* Version of vm_intro() designed to work for many languages.
07944  *
07945  * It is hoped that this function can prevent the proliferation of 
07946  * language-specific vm_intro() functions and in time replace the language-
07947  * specific functions which already exist.  An examination of the language-
07948  * specific functions revealed that they all corrected the same deficiencies
07949  * in vm_intro_en() (which was the default function). Namely:
07950  *
07951  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07952  *     wording of the voicemail greeting hides this problem.  For example,
07953  *     vm-INBOX contains only the word "new".  This means that both of these
07954  *     sequences produce valid utterances:
07955  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07956  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07957  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07958  *     in many languages) the first utterance becomes "you have 1 the new message".
07959  *  2) The function contains hardcoded rules for pluralizing the word "message".
07960  *     These rules are correct for English, but not for many other languages.
07961  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07962  *     required in many languages.
07963  *  4) The gender of the word for "message" is not specified. This is a problem
07964  *     because in many languages the gender of the number in phrases such
07965  *     as "you have one new message" must match the gender of the word
07966  *     meaning "message".
07967  *
07968  * Fixing these problems for each new language has meant duplication of effort.
07969  * This new function solves the problems in the following general ways:
07970  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07971  *     and vm-Old respectively for those languages where it makes sense.
07972  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07973  *     on vm-message.
07974  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07975  *     prefix on vm-new and vm-old (none for English).
07976  *  4) Pass the gender of the language's word for "message" as an agument to
07977  *     this function which is can in turn pass on to the functions which 
07978  *     say numbers and put endings on nounds and adjectives.
07979  *
07980  * All languages require these messages:
07981  *  vm-youhave    "You have..."
07982  *  vm-and     "and"
07983  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07984  *
07985  * To use it for English, you will need these additional sound files:
07986  *  vm-new     "new"
07987  *  vm-message    "message", singular
07988  *  vm-messages      "messages", plural
07989  *
07990  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07991  *
07992  *  vm-newn    "novoye" (singular, neuter)
07993  *  vm-newx    "novikh" (counting plural form, genative plural)
07994  *  vm-message    "sobsheniye" (singular form)
07995  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07996  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07997  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07998  *  digits/2n     "dva" (neuter singular)
07999  */
08000 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08001 {
08002    int res;
08003    int lastnum = 0;
08004 
08005    res = ast_play_and_wait(chan, "vm-youhave");
08006 
08007    if (!res && vms->newmessages) {
08008       lastnum = vms->newmessages;
08009 
08010       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08011          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08012       }
08013 
08014       if (!res && vms->oldmessages) {
08015          res = ast_play_and_wait(chan, "vm-and");
08016       }
08017    }
08018 
08019    if (!res && vms->oldmessages) {
08020       lastnum = vms->oldmessages;
08021 
08022       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08023          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08024       }
08025    }
08026 
08027    if (!res) {
08028       if (lastnum == 0) {
08029          res = ast_play_and_wait(chan, "vm-no");
08030       }
08031       if (!res) {
08032          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08033       }
08034    }
08035 
08036    return res;
08037 }
08038 
08039 /* Default Hebrew syntax */
08040 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08041 {
08042    int res = 0;
08043 
08044    /* Introduce messages they have */
08045    if (!res) {
08046       if ((vms->newmessages) || (vms->oldmessages)) {
08047          res = ast_play_and_wait(chan, "vm-youhave");
08048       }
08049       /*
08050        * The word "shtei" refers to the number 2 in hebrew when performing a count
08051        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08052        * an element, this is one of them.
08053        */
08054       if (vms->newmessages) {
08055          if (!res) {
08056             if (vms->newmessages == 1) {
08057                res = ast_play_and_wait(chan, "vm-INBOX1");
08058             } else {
08059                if (vms->newmessages == 2) {
08060                   res = ast_play_and_wait(chan, "vm-shtei");
08061                } else {
08062                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08063                }
08064                res = ast_play_and_wait(chan, "vm-INBOX");
08065             }
08066          }
08067          if (vms->oldmessages && !res) {
08068             res = ast_play_and_wait(chan, "vm-and");
08069             if (vms->oldmessages == 1) {
08070                res = ast_play_and_wait(chan, "vm-Old1");
08071             } else {
08072                if (vms->oldmessages == 2) {
08073                   res = ast_play_and_wait(chan, "vm-shtei");
08074                } else {
08075                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08076                }
08077                res = ast_play_and_wait(chan, "vm-Old");
08078             }
08079          }
08080       }
08081       if (!res && vms->oldmessages && !vms->newmessages) {
08082          if (!res) {
08083             if (vms->oldmessages == 1) {
08084                res = ast_play_and_wait(chan, "vm-Old1");
08085             } else {
08086                if (vms->oldmessages == 2) {
08087                   res = ast_play_and_wait(chan, "vm-shtei");
08088                } else {
08089                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08090                }
08091                res = ast_play_and_wait(chan, "vm-Old");
08092             }
08093          }
08094       }
08095       if (!res) {
08096          if (!vms->oldmessages && !vms->newmessages) {
08097             if (!res) {
08098                res = ast_play_and_wait(chan, "vm-nomessages");
08099             }
08100          }
08101       }
08102    }
08103    return res;
08104 }
08105    
08106 /* Default English syntax */
08107 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08108 {
08109    int res;
08110 
08111    /* Introduce messages they have */
08112    res = ast_play_and_wait(chan, "vm-youhave");
08113    if (!res) {
08114       if (vms->urgentmessages) {
08115          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08116          if (!res)
08117             res = ast_play_and_wait(chan, "vm-Urgent");
08118          if ((vms->oldmessages || vms->newmessages) && !res) {
08119             res = ast_play_and_wait(chan, "vm-and");
08120          } else if (!res) {
08121             if ((vms->urgentmessages == 1))
08122                res = ast_play_and_wait(chan, "vm-message");
08123             else
08124                res = ast_play_and_wait(chan, "vm-messages");
08125          }
08126       }
08127       if (vms->newmessages) {
08128          res = say_and_wait(chan, vms->newmessages, chan->language);
08129          if (!res)
08130             res = ast_play_and_wait(chan, "vm-INBOX");
08131          if (vms->oldmessages && !res)
08132             res = ast_play_and_wait(chan, "vm-and");
08133          else if (!res) {
08134             if ((vms->newmessages == 1))
08135                res = ast_play_and_wait(chan, "vm-message");
08136             else
08137                res = ast_play_and_wait(chan, "vm-messages");
08138          }
08139             
08140       }
08141       if (!res && vms->oldmessages) {
08142          res = say_and_wait(chan, vms->oldmessages, chan->language);
08143          if (!res)
08144             res = ast_play_and_wait(chan, "vm-Old");
08145          if (!res) {
08146             if (vms->oldmessages == 1)
08147                res = ast_play_and_wait(chan, "vm-message");
08148             else
08149                res = ast_play_and_wait(chan, "vm-messages");
08150          }
08151       }
08152       if (!res) {
08153          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08154             res = ast_play_and_wait(chan, "vm-no");
08155             if (!res)
08156                res = ast_play_and_wait(chan, "vm-messages");
08157          }
08158       }
08159    }
08160    return res;
08161 }
08162 
08163 /* ITALIAN syntax */
08164 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08165 {
08166    /* Introduce messages they have */
08167    int res;
08168    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08169       res = ast_play_and_wait(chan, "vm-no") ||
08170          ast_play_and_wait(chan, "vm-message");
08171    else
08172       res = ast_play_and_wait(chan, "vm-youhave");
08173    if (!res && vms->newmessages) {
08174       res = (vms->newmessages == 1) ?
08175          ast_play_and_wait(chan, "digits/un") ||
08176          ast_play_and_wait(chan, "vm-nuovo") ||
08177          ast_play_and_wait(chan, "vm-message") :
08178          /* 2 or more new messages */
08179          say_and_wait(chan, vms->newmessages, chan->language) ||
08180          ast_play_and_wait(chan, "vm-nuovi") ||
08181          ast_play_and_wait(chan, "vm-messages");
08182       if (!res && vms->oldmessages)
08183          res = ast_play_and_wait(chan, "vm-and");
08184    }
08185    if (!res && vms->oldmessages) {
08186       res = (vms->oldmessages == 1) ?
08187          ast_play_and_wait(chan, "digits/un") ||
08188          ast_play_and_wait(chan, "vm-vecchio") ||
08189          ast_play_and_wait(chan, "vm-message") :
08190          /* 2 or more old messages */
08191          say_and_wait(chan, vms->oldmessages, chan->language) ||
08192          ast_play_and_wait(chan, "vm-vecchi") ||
08193          ast_play_and_wait(chan, "vm-messages");
08194    }
08195    return res;
08196 }
08197 
08198 /* POLISH syntax */
08199 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08200 {
08201    /* Introduce messages they have */
08202    int res;
08203    div_t num;
08204 
08205    if (!vms->oldmessages && !vms->newmessages) {
08206       res = ast_play_and_wait(chan, "vm-no");
08207       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08208       return res;
08209    } else {
08210       res = ast_play_and_wait(chan, "vm-youhave");
08211    }
08212 
08213    if (vms->newmessages) {
08214       num = div(vms->newmessages, 10);
08215       if (vms->newmessages == 1) {
08216          res = ast_play_and_wait(chan, "digits/1-a");
08217          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08218          res = res ? res : ast_play_and_wait(chan, "vm-message");
08219       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08220          if (num.rem == 2) {
08221             if (!num.quot) {
08222                res = ast_play_and_wait(chan, "digits/2-ie");
08223             } else {
08224                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08225                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08226             }
08227          } else {
08228             res = say_and_wait(chan, vms->newmessages, chan->language);
08229          }
08230          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08231          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08232       } else {
08233          res = say_and_wait(chan, vms->newmessages, chan->language);
08234          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08235          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08236       }
08237       if (!res && vms->oldmessages)
08238          res = ast_play_and_wait(chan, "vm-and");
08239    }
08240    if (!res && vms->oldmessages) {
08241       num = div(vms->oldmessages, 10);
08242       if (vms->oldmessages == 1) {
08243          res = ast_play_and_wait(chan, "digits/1-a");
08244          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08245          res = res ? res : ast_play_and_wait(chan, "vm-message");
08246       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08247          if (num.rem == 2) {
08248             if (!num.quot) {
08249                res = ast_play_and_wait(chan, "digits/2-ie");
08250             } else {
08251                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08252                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08253             }
08254          } else {
08255             res = say_and_wait(chan, vms->oldmessages, chan->language);
08256          }
08257          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08258          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08259       } else {
08260          res = say_and_wait(chan, vms->oldmessages, chan->language);
08261          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08262          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08263       }
08264    }
08265 
08266    return res;
08267 }
08268 
08269 /* SWEDISH syntax */
08270 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08271 {
08272    /* Introduce messages they have */
08273    int res;
08274 
08275    res = ast_play_and_wait(chan, "vm-youhave");
08276    if (res)
08277       return res;
08278 
08279    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08280       res = ast_play_and_wait(chan, "vm-no");
08281       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08282       return res;
08283    }
08284 
08285    if (vms->newmessages) {
08286       if ((vms->newmessages == 1)) {
08287          res = ast_play_and_wait(chan, "digits/ett");
08288          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08289          res = res ? res : ast_play_and_wait(chan, "vm-message");
08290       } else {
08291          res = say_and_wait(chan, vms->newmessages, chan->language);
08292          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08293          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08294       }
08295       if (!res && vms->oldmessages)
08296          res = ast_play_and_wait(chan, "vm-and");
08297    }
08298    if (!res && vms->oldmessages) {
08299       if (vms->oldmessages == 1) {
08300          res = ast_play_and_wait(chan, "digits/ett");
08301          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08302          res = res ? res : ast_play_and_wait(chan, "vm-message");
08303       } else {
08304          res = say_and_wait(chan, vms->oldmessages, chan->language);
08305          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08306          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08307       }
08308    }
08309 
08310    return res;
08311 }
08312 
08313 /* NORWEGIAN syntax */
08314 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08315 {
08316    /* Introduce messages they have */
08317    int res;
08318 
08319    res = ast_play_and_wait(chan, "vm-youhave");
08320    if (res)
08321       return res;
08322 
08323    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08324       res = ast_play_and_wait(chan, "vm-no");
08325       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08326       return res;
08327    }
08328 
08329    if (vms->newmessages) {
08330       if ((vms->newmessages == 1)) {
08331          res = ast_play_and_wait(chan, "digits/1");
08332          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08333          res = res ? res : ast_play_and_wait(chan, "vm-message");
08334       } else {
08335          res = say_and_wait(chan, vms->newmessages, chan->language);
08336          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08337          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08338       }
08339       if (!res && vms->oldmessages)
08340          res = ast_play_and_wait(chan, "vm-and");
08341    }
08342    if (!res && vms->oldmessages) {
08343       if (vms->oldmessages == 1) {
08344          res = ast_play_and_wait(chan, "digits/1");
08345          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08346          res = res ? res : ast_play_and_wait(chan, "vm-message");
08347       } else {
08348          res = say_and_wait(chan, vms->oldmessages, chan->language);
08349          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08350          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08351       }
08352    }
08353 
08354    return res;
08355 }
08356 
08357 /* GERMAN syntax */
08358 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08359 {
08360    /* Introduce messages they have */
08361    int res;
08362    res = ast_play_and_wait(chan, "vm-youhave");
08363    if (!res) {
08364       if (vms->newmessages) {
08365          if ((vms->newmessages == 1))
08366             res = ast_play_and_wait(chan, "digits/1F");
08367          else
08368             res = say_and_wait(chan, vms->newmessages, chan->language);
08369          if (!res)
08370             res = ast_play_and_wait(chan, "vm-INBOX");
08371          if (vms->oldmessages && !res)
08372             res = ast_play_and_wait(chan, "vm-and");
08373          else if (!res) {
08374             if ((vms->newmessages == 1))
08375                res = ast_play_and_wait(chan, "vm-message");
08376             else
08377                res = ast_play_and_wait(chan, "vm-messages");
08378          }
08379             
08380       }
08381       if (!res && vms->oldmessages) {
08382          if (vms->oldmessages == 1)
08383             res = ast_play_and_wait(chan, "digits/1F");
08384          else
08385             res = say_and_wait(chan, vms->oldmessages, chan->language);
08386          if (!res)
08387             res = ast_play_and_wait(chan, "vm-Old");
08388          if (!res) {
08389             if (vms->oldmessages == 1)
08390                res = ast_play_and_wait(chan, "vm-message");
08391             else
08392                res = ast_play_and_wait(chan, "vm-messages");
08393          }
08394       }
08395       if (!res) {
08396          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08397             res = ast_play_and_wait(chan, "vm-no");
08398             if (!res)
08399                res = ast_play_and_wait(chan, "vm-messages");
08400          }
08401       }
08402    }
08403    return res;
08404 }
08405 
08406 /* SPANISH syntax */
08407 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08408 {
08409    /* Introduce messages they have */
08410    int res;
08411    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08412       res = ast_play_and_wait(chan, "vm-youhaveno");
08413       if (!res)
08414          res = ast_play_and_wait(chan, "vm-messages");
08415    } else {
08416       res = ast_play_and_wait(chan, "vm-youhave");
08417    }
08418    if (!res) {
08419       if (vms->newmessages) {
08420          if (!res) {
08421             if ((vms->newmessages == 1)) {
08422                res = ast_play_and_wait(chan, "digits/1M");
08423                if (!res)
08424                   res = ast_play_and_wait(chan, "vm-message");
08425                if (!res)
08426                   res = ast_play_and_wait(chan, "vm-INBOXs");
08427             } else {
08428                res = say_and_wait(chan, vms->newmessages, chan->language);
08429                if (!res)
08430                   res = ast_play_and_wait(chan, "vm-messages");
08431                if (!res)
08432                   res = ast_play_and_wait(chan, "vm-INBOX");
08433             }
08434          }
08435          if (vms->oldmessages && !res)
08436             res = ast_play_and_wait(chan, "vm-and");
08437       }
08438       if (vms->oldmessages) {
08439          if (!res) {
08440             if (vms->oldmessages == 1) {
08441                res = ast_play_and_wait(chan, "digits/1M");
08442                if (!res)
08443                   res = ast_play_and_wait(chan, "vm-message");
08444                if (!res)
08445                   res = ast_play_and_wait(chan, "vm-Olds");
08446             } else {
08447                res = say_and_wait(chan, vms->oldmessages, chan->language);
08448                if (!res)
08449                   res = ast_play_and_wait(chan, "vm-messages");
08450                if (!res)
08451                   res = ast_play_and_wait(chan, "vm-Old");
08452             }
08453          }
08454       }
08455    }
08456 return res;
08457 }
08458 
08459 /* BRAZILIAN PORTUGUESE syntax */
08460 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08461    /* Introduce messages they have */
08462    int res;
08463    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08464       res = ast_play_and_wait(chan, "vm-nomessages");
08465       return res;
08466    } else {
08467       res = ast_play_and_wait(chan, "vm-youhave");
08468    }
08469    if (vms->newmessages) {
08470       if (!res)
08471          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08472       if ((vms->newmessages == 1)) {
08473          if (!res)
08474             res = ast_play_and_wait(chan, "vm-message");
08475          if (!res)
08476             res = ast_play_and_wait(chan, "vm-INBOXs");
08477       } else {
08478          if (!res)
08479             res = ast_play_and_wait(chan, "vm-messages");
08480          if (!res)
08481             res = ast_play_and_wait(chan, "vm-INBOX");
08482       }
08483       if (vms->oldmessages && !res)
08484          res = ast_play_and_wait(chan, "vm-and");
08485    }
08486    if (vms->oldmessages) {
08487       if (!res)
08488          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08489       if (vms->oldmessages == 1) {
08490          if (!res)
08491             res = ast_play_and_wait(chan, "vm-message");
08492          if (!res)
08493             res = ast_play_and_wait(chan, "vm-Olds");
08494       } else {
08495          if (!res)
08496             res = ast_play_and_wait(chan, "vm-messages");
08497          if (!res)
08498             res = ast_play_and_wait(chan, "vm-Old");
08499       }
08500    }
08501    return res;
08502 }
08503 
08504 /* FRENCH syntax */
08505 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08506 {
08507    /* Introduce messages they have */
08508    int res;
08509    res = ast_play_and_wait(chan, "vm-youhave");
08510    if (!res) {
08511       if (vms->newmessages) {
08512          res = say_and_wait(chan, vms->newmessages, chan->language);
08513          if (!res)
08514             res = ast_play_and_wait(chan, "vm-INBOX");
08515          if (vms->oldmessages && !res)
08516             res = ast_play_and_wait(chan, "vm-and");
08517          else if (!res) {
08518             if ((vms->newmessages == 1))
08519                res = ast_play_and_wait(chan, "vm-message");
08520             else
08521                res = ast_play_and_wait(chan, "vm-messages");
08522          }
08523             
08524       }
08525       if (!res && vms->oldmessages) {
08526          res = say_and_wait(chan, vms->oldmessages, chan->language);
08527          if (!res)
08528             res = ast_play_and_wait(chan, "vm-Old");
08529          if (!res) {
08530             if (vms->oldmessages == 1)
08531                res = ast_play_and_wait(chan, "vm-message");
08532             else
08533                res = ast_play_and_wait(chan, "vm-messages");
08534          }
08535       }
08536       if (!res) {
08537          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08538             res = ast_play_and_wait(chan, "vm-no");
08539             if (!res)
08540                res = ast_play_and_wait(chan, "vm-messages");
08541          }
08542       }
08543    }
08544    return res;
08545 }
08546 
08547 /* DUTCH syntax */
08548 static int vm_intro_nl(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             if (vms->newmessages == 1)
08558                res = ast_play_and_wait(chan, "vm-INBOXs");
08559             else
08560                res = ast_play_and_wait(chan, "vm-INBOX");
08561          }
08562          if (vms->oldmessages && !res)
08563             res = ast_play_and_wait(chan, "vm-and");
08564          else if (!res) {
08565             if ((vms->newmessages == 1))
08566                res = ast_play_and_wait(chan, "vm-message");
08567             else
08568                res = ast_play_and_wait(chan, "vm-messages");
08569          }
08570             
08571       }
08572       if (!res && vms->oldmessages) {
08573          res = say_and_wait(chan, vms->oldmessages, chan->language);
08574          if (!res) {
08575             if (vms->oldmessages == 1)
08576                res = ast_play_and_wait(chan, "vm-Olds");
08577             else
08578                res = ast_play_and_wait(chan, "vm-Old");
08579          }
08580          if (!res) {
08581             if (vms->oldmessages == 1)
08582                res = ast_play_and_wait(chan, "vm-message");
08583             else
08584                res = ast_play_and_wait(chan, "vm-messages");
08585          }
08586       }
08587       if (!res) {
08588          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08589             res = ast_play_and_wait(chan, "vm-no");
08590             if (!res)
08591                res = ast_play_and_wait(chan, "vm-messages");
08592          }
08593       }
08594    }
08595    return res;
08596 }
08597 
08598 /* PORTUGUESE syntax */
08599 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08600 {
08601    /* Introduce messages they have */
08602    int res;
08603    res = ast_play_and_wait(chan, "vm-youhave");
08604    if (!res) {
08605       if (vms->newmessages) {
08606          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08607          if (!res) {
08608             if ((vms->newmessages == 1)) {
08609                res = ast_play_and_wait(chan, "vm-message");
08610                if (!res)
08611                   res = ast_play_and_wait(chan, "vm-INBOXs");
08612             } else {
08613                res = ast_play_and_wait(chan, "vm-messages");
08614                if (!res)
08615                   res = ast_play_and_wait(chan, "vm-INBOX");
08616             }
08617          }
08618          if (vms->oldmessages && !res)
08619             res = ast_play_and_wait(chan, "vm-and");
08620       }
08621       if (!res && vms->oldmessages) {
08622          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08623          if (!res) {
08624             if (vms->oldmessages == 1) {
08625                res = ast_play_and_wait(chan, "vm-message");
08626                if (!res)
08627                   res = ast_play_and_wait(chan, "vm-Olds");
08628             } else {
08629                res = ast_play_and_wait(chan, "vm-messages");
08630                if (!res)
08631                   res = ast_play_and_wait(chan, "vm-Old");
08632             }
08633          }
08634       }
08635       if (!res) {
08636          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08637             res = ast_play_and_wait(chan, "vm-no");
08638             if (!res)
08639                res = ast_play_and_wait(chan, "vm-messages");
08640          }
08641       }
08642    }
08643    return res;
08644 }
08645 
08646 
08647 /* CZECH syntax */
08648 /* in czech there must be declension of word new and message
08649  * czech        : english        : czech      : english
08650  * --------------------------------------------------------
08651  * vm-youhave   : you have 
08652  * vm-novou     : one new        : vm-zpravu  : message
08653  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08654  * vm-novych    : 5-infinite new : vm-zprav   : messages
08655  * vm-starou   : one old
08656  * vm-stare     : 2-4 old 
08657  * vm-starych   : 5-infinite old
08658  * jednu        : one   - falling 4. 
08659  * vm-no        : no  ( no messages )
08660  */
08661 
08662 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08663 {
08664    int res;
08665    res = ast_play_and_wait(chan, "vm-youhave");
08666    if (!res) {
08667       if (vms->newmessages) {
08668          if (vms->newmessages == 1) {
08669             res = ast_play_and_wait(chan, "digits/jednu");
08670          } else {
08671             res = say_and_wait(chan, vms->newmessages, chan->language);
08672          }
08673          if (!res) {
08674             if ((vms->newmessages == 1))
08675                res = ast_play_and_wait(chan, "vm-novou");
08676             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08677                res = ast_play_and_wait(chan, "vm-nove");
08678             if (vms->newmessages > 4)
08679                res = ast_play_and_wait(chan, "vm-novych");
08680          }
08681          if (vms->oldmessages && !res)
08682             res = ast_play_and_wait(chan, "vm-and");
08683          else if (!res) {
08684             if ((vms->newmessages == 1))
08685                res = ast_play_and_wait(chan, "vm-zpravu");
08686             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08687                res = ast_play_and_wait(chan, "vm-zpravy");
08688             if (vms->newmessages > 4)
08689                res = ast_play_and_wait(chan, "vm-zprav");
08690          }
08691       }
08692       if (!res && vms->oldmessages) {
08693          res = say_and_wait(chan, vms->oldmessages, chan->language);
08694          if (!res) {
08695             if ((vms->oldmessages == 1))
08696                res = ast_play_and_wait(chan, "vm-starou");
08697             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08698                res = ast_play_and_wait(chan, "vm-stare");
08699             if (vms->oldmessages > 4)
08700                res = ast_play_and_wait(chan, "vm-starych");
08701          }
08702          if (!res) {
08703             if ((vms->oldmessages == 1))
08704                res = ast_play_and_wait(chan, "vm-zpravu");
08705             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08706                res = ast_play_and_wait(chan, "vm-zpravy");
08707             if (vms->oldmessages > 4)
08708                res = ast_play_and_wait(chan, "vm-zprav");
08709          }
08710       }
08711       if (!res) {
08712          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08713             res = ast_play_and_wait(chan, "vm-no");
08714             if (!res)
08715                res = ast_play_and_wait(chan, "vm-zpravy");
08716          }
08717       }
08718    }
08719    return res;
08720 }
08721 
08722 /* CHINESE (Taiwan) syntax */
08723 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08724 {
08725    int res;
08726    /* Introduce messages they have */
08727    res = ast_play_and_wait(chan, "vm-you");
08728 
08729    if (!res && vms->newmessages) {
08730       res = ast_play_and_wait(chan, "vm-have");
08731       if (!res)
08732          res = say_and_wait(chan, vms->newmessages, chan->language);
08733       if (!res)
08734          res = ast_play_and_wait(chan, "vm-tong");
08735       if (!res)
08736          res = ast_play_and_wait(chan, "vm-INBOX");
08737       if (vms->oldmessages && !res)
08738          res = ast_play_and_wait(chan, "vm-and");
08739       else if (!res) 
08740          res = ast_play_and_wait(chan, "vm-messages");
08741    }
08742    if (!res && vms->oldmessages) {
08743       res = ast_play_and_wait(chan, "vm-have");
08744       if (!res)
08745          res = say_and_wait(chan, vms->oldmessages, chan->language);
08746       if (!res)
08747          res = ast_play_and_wait(chan, "vm-tong");
08748       if (!res)
08749          res = ast_play_and_wait(chan, "vm-Old");
08750       if (!res)
08751          res = ast_play_and_wait(chan, "vm-messages");
08752    }
08753    if (!res && !vms->oldmessages && !vms->newmessages) {
08754       res = ast_play_and_wait(chan, "vm-haveno");
08755       if (!res)
08756          res = ast_play_and_wait(chan, "vm-messages");
08757    }
08758    return res;
08759 }
08760 
08761 /* Vietnamese syntax */
08762 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08763 {
08764    int res;
08765 
08766    /* Introduce messages they have */
08767    res = ast_play_and_wait(chan, "vm-youhave");
08768    if (!res) {
08769       if (vms->newmessages) {
08770          res = say_and_wait(chan, vms->newmessages, chan->language);
08771          if (!res)
08772             res = ast_play_and_wait(chan, "vm-INBOX");
08773          if (vms->oldmessages && !res)
08774             res = ast_play_and_wait(chan, "vm-and");
08775       }
08776       if (!res && vms->oldmessages) {
08777          res = say_and_wait(chan, vms->oldmessages, chan->language);
08778          if (!res)
08779             res = ast_play_and_wait(chan, "vm-Old");        
08780       }
08781       if (!res) {
08782          if (!vms->oldmessages && !vms->newmessages) {
08783             res = ast_play_and_wait(chan, "vm-no");
08784             if (!res)
08785                res = ast_play_and_wait(chan, "vm-message");
08786          }
08787       }
08788    }
08789    return res;
08790 }
08791 
08792 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08793 {
08794    char prefile[256];
08795    
08796    /* Notify the user that the temp greeting is set and give them the option to remove it */
08797    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08798    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08799       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08800       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08801          ast_play_and_wait(chan, "vm-tempgreetactive");
08802       }
08803       DISPOSE(prefile, -1);
08804    }
08805 
08806    /* Play voicemail intro - syntax is different for different languages */
08807    if (0) {
08808       return 0;
08809    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08810       return vm_intro_cs(chan, vms);
08811    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08812       static int deprecation_warning = 0;
08813       if (deprecation_warning++ % 10 == 0) {
08814          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08815       }
08816       return vm_intro_cs(chan, vms);
08817    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08818       return vm_intro_de(chan, vms);
08819    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08820       return vm_intro_es(chan, vms);
08821    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08822       return vm_intro_fr(chan, vms);
08823    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08824       return vm_intro_gr(chan, vms);
08825    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08826       return vm_intro_he(chan, vms);
08827    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08828       return vm_intro_it(chan, vms);
08829    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08830       return vm_intro_nl(chan, vms);
08831    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08832       return vm_intro_no(chan, vms);
08833    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08834       return vm_intro_pl(chan, vms);
08835    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08836       return vm_intro_pt_BR(chan, vms);
08837    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08838       return vm_intro_pt(chan, vms);
08839    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08840       return vm_intro_multilang(chan, vms, "n");
08841    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08842       return vm_intro_se(chan, vms);
08843    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08844       return vm_intro_multilang(chan, vms, "n");
08845    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
08846       return vm_intro_vi(chan, vms);
08847    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08848       return vm_intro_zh(chan, vms);
08849    } else {                                             /* Default to ENGLISH */
08850       return vm_intro_en(chan, vms);
08851    }
08852 }
08853 
08854 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08855 {
08856    int res = 0;
08857    /* Play instructions and wait for new command */
08858    while (!res) {
08859       if (vms->starting) {
08860          if (vms->lastmsg > -1) {
08861             if (skipadvanced)
08862                res = ast_play_and_wait(chan, "vm-onefor-full");
08863             else
08864                res = ast_play_and_wait(chan, "vm-onefor");
08865             if (!res)
08866                res = vm_play_folder_name(chan, vms->vmbox);
08867          }
08868          if (!res) {
08869             if (skipadvanced)
08870                res = ast_play_and_wait(chan, "vm-opts-full");
08871             else
08872                res = ast_play_and_wait(chan, "vm-opts");
08873          }
08874       } else {
08875          /* Added for additional help */
08876          if (skipadvanced) {
08877             res = ast_play_and_wait(chan, "vm-onefor-full");
08878             if (!res)
08879                res = vm_play_folder_name(chan, vms->vmbox);
08880             res = ast_play_and_wait(chan, "vm-opts-full");
08881          }
08882          /* Logic:
08883           * If the current message is not the first OR
08884           * if we're listening to the first new message and there are
08885           * also urgent messages, then prompt for navigation to the
08886           * previous message
08887           */
08888          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08889             res = ast_play_and_wait(chan, "vm-prev");
08890          }
08891          if (!res && !skipadvanced)
08892             res = ast_play_and_wait(chan, "vm-advopts");
08893          if (!res)
08894             res = ast_play_and_wait(chan, "vm-repeat");
08895          /* Logic:
08896           * If we're not listening to the last message OR
08897           * we're listening to the last urgent message and there are
08898           * also new non-urgent messages, then prompt for navigation
08899           * to the next message
08900           */
08901          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08902             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08903             res = ast_play_and_wait(chan, "vm-next");
08904          }
08905          if (!res) {
08906             if (!vms->deleted[vms->curmsg])
08907                res = ast_play_and_wait(chan, "vm-delete");
08908             else
08909                res = ast_play_and_wait(chan, "vm-undelete");
08910             if (!res)
08911                res = ast_play_and_wait(chan, "vm-toforward");
08912             if (!res)
08913                res = ast_play_and_wait(chan, "vm-savemessage");
08914          }
08915       }
08916       if (!res) {
08917          res = ast_play_and_wait(chan, "vm-helpexit");
08918       }
08919       if (!res)
08920          res = ast_waitfordigit(chan, 6000);
08921       if (!res) {
08922          vms->repeats++;
08923          if (vms->repeats > 2) {
08924             res = 't';
08925          }
08926       }
08927    }
08928    return res;
08929 }
08930 
08931 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08932 {
08933    int res = 0;
08934    /* Play instructions and wait for new command */
08935    while (!res) {
08936       if (vms->lastmsg > -1) {
08937          res = ast_play_and_wait(chan, "vm-listen");
08938          if (!res)
08939             res = vm_play_folder_name(chan, vms->vmbox);
08940          if (!res)
08941             res = ast_play_and_wait(chan, "press");
08942          if (!res)
08943             res = ast_play_and_wait(chan, "digits/1");
08944       }
08945       if (!res)
08946          res = ast_play_and_wait(chan, "vm-opts");
08947       if (!res) {
08948          vms->starting = 0;
08949          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08950       }
08951    }
08952    return res;
08953 }
08954 
08955 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08956 {
08957    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08958       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08959    } else {             /* Default to ENGLISH */
08960       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08961    }
08962 }
08963 
08964 
08965 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08966 {
08967    int cmd = 0;
08968    int duration = 0;
08969    int tries = 0;
08970    char newpassword[80] = "";
08971    char newpassword2[80] = "";
08972    char prefile[PATH_MAX] = "";
08973    unsigned char buf[256];
08974    int bytes = 0;
08975 
08976    if (ast_adsi_available(chan)) {
08977       bytes += adsi_logo(buf + bytes);
08978       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08979       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08980       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08981       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08982       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08983    }
08984 
08985    /* First, have the user change their password 
08986       so they won't get here again */
08987    for (;;) {
08988       newpassword[1] = '\0';
08989       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08990       if (cmd == '#')
08991          newpassword[0] = '\0';
08992       if (cmd < 0 || cmd == 't' || cmd == '#')
08993          return cmd;
08994       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
08995       if (cmd < 0 || cmd == 't' || cmd == '#')
08996          return cmd;
08997       cmd = check_password(vmu, newpassword); /* perform password validation */
08998       if (cmd != 0) {
08999          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09000          cmd = ast_play_and_wait(chan, vm_invalid_password);
09001       } else {
09002          newpassword2[1] = '\0';
09003          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09004          if (cmd == '#')
09005             newpassword2[0] = '\0';
09006          if (cmd < 0 || cmd == 't' || cmd == '#')
09007             return cmd;
09008          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09009          if (cmd < 0 || cmd == 't' || cmd == '#')
09010             return cmd;
09011          if (!strcmp(newpassword, newpassword2))
09012             break;
09013          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09014          cmd = ast_play_and_wait(chan, vm_mismatch);
09015       }
09016       if (++tries == 3)
09017          return -1;
09018       if (cmd != 0) {
09019          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09020       }
09021    }
09022    if (pwdchange & PWDCHANGE_INTERNAL)
09023       vm_change_password(vmu, newpassword);
09024    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09025       vm_change_password_shell(vmu, newpassword);
09026 
09027    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09028    cmd = ast_play_and_wait(chan, vm_passchanged);
09029 
09030    /* If forcename is set, have the user record their name */  
09031    if (ast_test_flag(vmu, VM_FORCENAME)) {
09032       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09033       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09034          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09035          if (cmd < 0 || cmd == 't' || cmd == '#')
09036             return cmd;
09037       }
09038    }
09039 
09040    /* If forcegreetings is set, have the user record their greetings */
09041    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09042       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09043       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09044          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09045          if (cmd < 0 || cmd == 't' || cmd == '#')
09046             return cmd;
09047       }
09048 
09049       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09050       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09051          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09052          if (cmd < 0 || cmd == 't' || cmd == '#')
09053             return cmd;
09054       }
09055    }
09056 
09057    return cmd;
09058 }
09059 
09060 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09061 {
09062    int cmd = 0;
09063    int retries = 0;
09064    int duration = 0;
09065    char newpassword[80] = "";
09066    char newpassword2[80] = "";
09067    char prefile[PATH_MAX] = "";
09068    unsigned char buf[256];
09069    int bytes = 0;
09070 
09071    if (ast_adsi_available(chan)) {
09072       bytes += adsi_logo(buf + bytes);
09073       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09074       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09075       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09076       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09077       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09078    }
09079    while ((cmd >= 0) && (cmd != 't')) {
09080       if (cmd)
09081          retries = 0;
09082       switch (cmd) {
09083       case '1': /* Record your unavailable message */
09084          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09085          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09086          break;
09087       case '2':  /* Record your busy message */
09088          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09089          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09090          break;
09091       case '3': /* Record greeting */
09092          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09093          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09094          break;
09095       case '4':  /* manage the temporary greeting */
09096          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09097          break;
09098       case '5': /* change password */
09099          if (vmu->password[0] == '-') {
09100             cmd = ast_play_and_wait(chan, "vm-no");
09101             break;
09102          }
09103          newpassword[1] = '\0';
09104          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09105          if (cmd == '#')
09106             newpassword[0] = '\0';
09107          else {
09108             if (cmd < 0)
09109                break;
09110             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09111                break;
09112             }
09113          }
09114          cmd = check_password(vmu, newpassword); /* perform password validation */
09115          if (cmd != 0) {
09116             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09117             cmd = ast_play_and_wait(chan, vm_invalid_password);
09118             if (!cmd) {
09119                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09120             }
09121             break;
09122          }
09123          newpassword2[1] = '\0';
09124          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09125          if (cmd == '#')
09126             newpassword2[0] = '\0';
09127          else {
09128             if (cmd < 0)
09129                break;
09130 
09131             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09132                break;
09133             }
09134          }
09135          if (strcmp(newpassword, newpassword2)) {
09136             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09137             cmd = ast_play_and_wait(chan, vm_mismatch);
09138             if (!cmd) {
09139                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09140             }
09141             break;
09142          }
09143          if (pwdchange & PWDCHANGE_INTERNAL)
09144             vm_change_password(vmu, newpassword);
09145          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09146             vm_change_password_shell(vmu, newpassword);
09147 
09148          ast_debug(1, "User %s set password to %s of length %d\n",
09149             vms->username, newpassword, (int) strlen(newpassword));
09150          cmd = ast_play_and_wait(chan, vm_passchanged);
09151          break;
09152       case '*': 
09153          cmd = 't';
09154          break;
09155       default: 
09156          cmd = 0;
09157          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09158          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09159          if (ast_fileexists(prefile, NULL, NULL)) {
09160             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09161          }
09162          DISPOSE(prefile, -1);
09163          if (!cmd) {
09164             cmd = ast_play_and_wait(chan, "vm-options");
09165          }
09166          if (!cmd) {
09167             cmd = ast_waitfordigit(chan, 6000);
09168          }
09169          if (!cmd) {
09170             retries++;
09171          }
09172          if (retries > 3) {
09173             cmd = 't';
09174          }
09175       }
09176    }
09177    if (cmd == 't')
09178       cmd = 0;
09179    return cmd;
09180 }
09181 
09182 /*!
09183  * \brief The handler for 'record a temporary greeting'. 
09184  * \param chan
09185  * \param vmu
09186  * \param vms
09187  * \param fmtc
09188  * \param record_gain
09189  *
09190  * This is option 4 from the mailbox options menu.
09191  * This function manages the following promptings:
09192  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09193  * 2: remove (delete) the temporary greeting.
09194  * *: return to the main menu.
09195  *
09196  * \return zero on success, -1 on error.
09197  */
09198 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09199 {
09200    int cmd = 0;
09201    int retries = 0;
09202    int duration = 0;
09203    char prefile[PATH_MAX] = "";
09204    unsigned char buf[256];
09205    int bytes = 0;
09206 
09207    if (ast_adsi_available(chan)) {
09208       bytes += adsi_logo(buf + bytes);
09209       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09210       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09211       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09212       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09213       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09214    }
09215 
09216    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09217    while ((cmd >= 0) && (cmd != 't')) {
09218       if (cmd)
09219          retries = 0;
09220       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09221       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09222          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09223          cmd = 't';  
09224       } else {
09225          switch (cmd) {
09226          case '1':
09227             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09228             break;
09229          case '2':
09230             DELETE(prefile, -1, prefile, vmu);
09231             ast_play_and_wait(chan, "vm-tempremoved");
09232             cmd = 't';  
09233             break;
09234          case '*': 
09235             cmd = 't';
09236             break;
09237          default:
09238             cmd = ast_play_and_wait(chan,
09239                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09240                   "vm-tempgreeting2" : "vm-tempgreeting");
09241             if (!cmd)
09242                cmd = ast_waitfordigit(chan, 6000);
09243             if (!cmd)
09244                retries++;
09245             if (retries > 3)
09246                cmd = 't';
09247          }
09248       }
09249       DISPOSE(prefile, -1);
09250    }
09251    if (cmd == 't')
09252       cmd = 0;
09253    return cmd;
09254 }
09255 
09256 /*!
09257  * \brief Greek syntax for 'You have N messages' greeting.
09258  * \param chan
09259  * \param vms
09260  * \param vmu
09261  *
09262  * \return zero on success, -1 on error.
09263  */   
09264 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09265 {
09266    int cmd = 0;
09267 
09268    if (vms->lastmsg > -1) {
09269       cmd = play_message(chan, vmu, vms);
09270    } else {
09271       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09272       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09273          if (!cmd) {
09274             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09275             cmd = ast_play_and_wait(chan, vms->fn);
09276          }
09277          if (!cmd)
09278             cmd = ast_play_and_wait(chan, "vm-messages");
09279       } else {
09280          if (!cmd)
09281             cmd = ast_play_and_wait(chan, "vm-messages");
09282          if (!cmd) {
09283             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09284             cmd = ast_play_and_wait(chan, vms->fn);
09285          }
09286       }
09287    } 
09288    return cmd;
09289 }
09290 
09291 /* Hebrew Syntax */
09292 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09293 {
09294    int cmd = 0;
09295 
09296    if (vms->lastmsg > -1) {
09297       cmd = play_message(chan, vmu, vms);
09298    } else {
09299       if (!strcasecmp(vms->fn, "INBOX")) {
09300          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09301       } else {
09302          cmd = ast_play_and_wait(chan, "vm-nomessages");
09303       }
09304    }
09305    return cmd;
09306 }
09307 
09308 /*! 
09309  * \brief Default English syntax for 'You have N messages' greeting.
09310  * \param chan
09311  * \param vms
09312  * \param vmu
09313  *
09314  * \return zero on success, -1 on error.
09315  */
09316 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09317 {
09318    int cmd = 0;
09319 
09320    if (vms->lastmsg > -1) {
09321       cmd = play_message(chan, vmu, vms);
09322    } else {
09323       cmd = ast_play_and_wait(chan, "vm-youhave");
09324       if (!cmd) 
09325          cmd = ast_play_and_wait(chan, "vm-no");
09326       if (!cmd) {
09327          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09328          cmd = ast_play_and_wait(chan, vms->fn);
09329       }
09330       if (!cmd)
09331          cmd = ast_play_and_wait(chan, "vm-messages");
09332    }
09333    return cmd;
09334 }
09335 
09336 /*! 
09337  *\brief Italian syntax for 'You have N messages' greeting.
09338  * \param chan
09339  * \param vms
09340  * \param vmu
09341  *
09342  * \return zero on success, -1 on error.
09343  */
09344 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09345 {
09346    int cmd;
09347 
09348    if (vms->lastmsg > -1) {
09349       cmd = play_message(chan, vmu, vms);
09350    } else {
09351       cmd = ast_play_and_wait(chan, "vm-no");
09352       if (!cmd)
09353          cmd = ast_play_and_wait(chan, "vm-message");
09354       if (!cmd) {
09355          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09356          cmd = ast_play_and_wait(chan, vms->fn);
09357       }
09358    }
09359    return cmd;
09360 }
09361 
09362 /*! 
09363  * \brief Spanish syntax for 'You have N messages' greeting.
09364  * \param chan
09365  * \param vms
09366  * \param vmu
09367  *
09368  * \return zero on success, -1 on error.
09369  */
09370 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09371 {
09372    int cmd;
09373 
09374    if (vms->lastmsg > -1) {
09375       cmd = play_message(chan, vmu, vms);
09376    } else {
09377       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09378       if (!cmd)
09379          cmd = ast_play_and_wait(chan, "vm-messages");
09380       if (!cmd) {
09381          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09382          cmd = ast_play_and_wait(chan, vms->fn);
09383       }
09384    }
09385    return cmd;
09386 }
09387 
09388 /*! 
09389  * \brief Portuguese syntax for 'You have N messages' greeting.
09390  * \param chan
09391  * \param vms
09392  * \param vmu
09393  *
09394  * \return zero on success, -1 on error.
09395  */
09396 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09397 {
09398    int cmd;
09399 
09400    if (vms->lastmsg > -1) {
09401       cmd = play_message(chan, vmu, vms);
09402    } else {
09403       cmd = ast_play_and_wait(chan, "vm-no");
09404       if (!cmd) {
09405          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09406          cmd = ast_play_and_wait(chan, vms->fn);
09407       }
09408       if (!cmd)
09409          cmd = ast_play_and_wait(chan, "vm-messages");
09410    }
09411    return cmd;
09412 }
09413 
09414 /*! 
09415  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09416  * \param chan
09417  * \param vms
09418  * \param vmu
09419  *
09420  * \return zero on success, -1 on error.
09421  */
09422 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09423 {
09424    int cmd;
09425 
09426    if (vms->lastmsg > -1) {
09427       cmd = play_message(chan, vmu, vms);
09428    } else {
09429       cmd = ast_play_and_wait(chan, "vm-you");
09430       if (!cmd) 
09431          cmd = ast_play_and_wait(chan, "vm-haveno");
09432       if (!cmd)
09433          cmd = ast_play_and_wait(chan, "vm-messages");
09434       if (!cmd) {
09435          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09436          cmd = ast_play_and_wait(chan, vms->fn);
09437       }
09438    }
09439    return cmd;
09440 }
09441 
09442 /*! 
09443  * \brief Vietnamese syntax for 'You have N messages' greeting.
09444  * \param chan
09445  * \param vms
09446  * \param vmu
09447  *
09448  * \return zero on success, -1 on error.
09449  */
09450 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09451 {
09452    int cmd = 0;
09453 
09454    if (vms->lastmsg > -1) {
09455       cmd = play_message(chan, vmu, vms);
09456    } else {
09457       cmd = ast_play_and_wait(chan, "vm-no");
09458       if (!cmd) {
09459          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09460          cmd = ast_play_and_wait(chan, vms->fn);
09461       }
09462    }
09463    return cmd;
09464 }
09465 
09466 /*!
09467  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09468  * \param chan The channel for the current user. We read the language property from this.
09469  * \param vms passed into the language-specific vm_browse_messages function.
09470  * \param vmu passed into the language-specific vm_browse_messages function.
09471  * 
09472  * The method to be invoked is determined by the value of language code property in the user's channel.
09473  * The default (when unable to match) is to use english.
09474  *
09475  * \return zero on success, -1 on error.
09476  */
09477 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09478 {
09479    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09480       return vm_browse_messages_es(chan, vms, vmu);
09481    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09482       return vm_browse_messages_gr(chan, vms, vmu);
09483    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09484       return vm_browse_messages_he(chan, vms, vmu);
09485    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09486       return vm_browse_messages_it(chan, vms, vmu);
09487    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09488       return vm_browse_messages_pt(chan, vms, vmu);
09489    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09490       return vm_browse_messages_vi(chan, vms, vmu);
09491    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09492       return vm_browse_messages_zh(chan, vms, vmu);
09493    } else {                                             /* Default to English syntax */
09494       return vm_browse_messages_en(chan, vms, vmu);
09495    }
09496 }
09497 
09498 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09499          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09500          int skipuser, int max_logins, int silent)
09501 {
09502    int useadsi = 0, valid = 0, logretries = 0;
09503    char password[AST_MAX_EXTENSION]="", *passptr;
09504    struct ast_vm_user vmus, *vmu = NULL;
09505 
09506    /* If ADSI is supported, setup login screen */
09507    adsi_begin(chan, &useadsi);
09508    if (!skipuser && useadsi)
09509       adsi_login(chan);
09510    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09511       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09512       return -1;
09513    }
09514    
09515    /* Authenticate them and get their mailbox/password */
09516    
09517    while (!valid && (logretries < max_logins)) {
09518       /* Prompt for, and read in the username */
09519       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09520          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09521          return -1;
09522       }
09523       if (ast_strlen_zero(mailbox)) {
09524          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09525             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09526          } else {
09527             ast_verb(3, "Username not entered\n"); 
09528             return -1;
09529          }
09530       } else if (mailbox[0] == '*') {
09531          /* user entered '*' */
09532          if (ast_exists_extension(chan, chan->context, "a", 1,
09533             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09534             return -1;
09535          }
09536          mailbox[0] = '\0';
09537       }
09538 
09539       if (useadsi)
09540          adsi_password(chan);
09541 
09542       if (!ast_strlen_zero(prefix)) {
09543          char fullusername[80] = "";
09544          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09545          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09546          ast_copy_string(mailbox, fullusername, mailbox_size);
09547       }
09548 
09549       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09550       vmu = find_user(&vmus, context, mailbox);
09551       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09552          /* saved password is blank, so don't bother asking */
09553          password[0] = '\0';
09554       } else {
09555          if (ast_streamfile(chan, vm_password, chan->language)) {
09556             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09557             return -1;
09558          }
09559          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09560             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09561             return -1;
09562          } else if (password[0] == '*') {
09563             /* user entered '*' */
09564             if (ast_exists_extension(chan, chan->context, "a", 1,
09565                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09566                mailbox[0] = '*';
09567                return -1;
09568             }
09569             mailbox[0] = '\0';
09570          }
09571       }
09572 
09573       if (vmu) {
09574          passptr = vmu->password;
09575          if (passptr[0] == '-') passptr++;
09576       }
09577       if (vmu && !strcmp(passptr, password))
09578          valid++;
09579       else {
09580          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09581          if (!ast_strlen_zero(prefix))
09582             mailbox[0] = '\0';
09583       }
09584       logretries++;
09585       if (!valid) {
09586          if (skipuser || logretries >= max_logins) {
09587             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09588                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09589                return -1;
09590             }
09591          } else {
09592             if (useadsi)
09593                adsi_login(chan);
09594             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09595                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09596                return -1;
09597             }
09598          }
09599          if (ast_waitstream(chan, "")) /* Channel is hung up */
09600             return -1;
09601       }
09602    }
09603    if (!valid && (logretries >= max_logins)) {
09604       ast_stopstream(chan);
09605       ast_play_and_wait(chan, "vm-goodbye");
09606       return -1;
09607    }
09608    if (vmu && !skipuser) {
09609       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09610    }
09611    return 0;
09612 }
09613 
09614 static int vm_execmain(struct ast_channel *chan, const char *data)
09615 {
09616    /* XXX This is, admittedly, some pretty horrendous code.  For some
09617       reason it just seemed a lot easier to do with GOTO's.  I feel
09618       like I'm back in my GWBASIC days. XXX */
09619    int res = -1;
09620    int cmd = 0;
09621    int valid = 0;
09622    char prefixstr[80] ="";
09623    char ext_context[256]="";
09624    int box;
09625    int useadsi = 0;
09626    int skipuser = 0;
09627    struct vm_state vms;
09628    struct ast_vm_user *vmu = NULL, vmus;
09629    char *context = NULL;
09630    int silentexit = 0;
09631    struct ast_flags flags = { 0 };
09632    signed char record_gain = 0;
09633    int play_auto = 0;
09634    int play_folder = 0;
09635    int in_urgent = 0;
09636 #ifdef IMAP_STORAGE
09637    int deleted = 0;
09638 #endif
09639 
09640    /* Add the vm_state to the active list and keep it active */
09641    memset(&vms, 0, sizeof(vms));
09642 
09643    vms.lastmsg = -1;
09644 
09645    memset(&vmus, 0, sizeof(vmus));
09646 
09647    if (chan->_state != AST_STATE_UP) {
09648       ast_debug(1, "Before ast_answer\n");
09649       ast_answer(chan);
09650    }
09651 
09652    if (!ast_strlen_zero(data)) {
09653       char *opts[OPT_ARG_ARRAY_SIZE];
09654       char *parse;
09655       AST_DECLARE_APP_ARGS(args,
09656          AST_APP_ARG(argv0);
09657          AST_APP_ARG(argv1);
09658       );
09659 
09660       parse = ast_strdupa(data);
09661 
09662       AST_STANDARD_APP_ARGS(args, parse);
09663 
09664       if (args.argc == 2) {
09665          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09666             return -1;
09667          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09668             int gain;
09669             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09670                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09671                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09672                   return -1;
09673                } else {
09674                   record_gain = (signed char) gain;
09675                }
09676             } else {
09677                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09678             }
09679          }
09680          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09681             play_auto = 1;
09682             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09683                /* See if it is a folder name first */
09684                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09685                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09686                      play_folder = -1;
09687                   }
09688                } else {
09689                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09690                }
09691             } else {
09692                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09693             }
09694             if (play_folder > 9 || play_folder < 0) {
09695                ast_log(AST_LOG_WARNING,
09696                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09697                   opts[OPT_ARG_PLAYFOLDER]);
09698                play_folder = 0;
09699             }
09700          }
09701       } else {
09702          /* old style options parsing */
09703          while (*(args.argv0)) {
09704             if (*(args.argv0) == 's')
09705                ast_set_flag(&flags, OPT_SILENT);
09706             else if (*(args.argv0) == 'p')
09707                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09708             else 
09709                break;
09710             (args.argv0)++;
09711          }
09712 
09713       }
09714 
09715       valid = ast_test_flag(&flags, OPT_SILENT);
09716 
09717       if ((context = strchr(args.argv0, '@')))
09718          *context++ = '\0';
09719 
09720       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09721          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09722       else
09723          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09724 
09725       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09726          skipuser++;
09727       else
09728          valid = 0;
09729    }
09730 
09731    if (!valid)
09732       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09733 
09734    ast_debug(1, "After vm_authenticate\n");
09735 
09736    if (vms.username[0] == '*') {
09737       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09738 
09739       /* user entered '*' */
09740       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09741          res = 0; /* prevent hangup */
09742          goto out;
09743       }
09744    }
09745 
09746    if (!res) {
09747       valid = 1;
09748       if (!skipuser)
09749          vmu = &vmus;
09750    } else {
09751       res = 0;
09752    }
09753 
09754    /* If ADSI is supported, setup login screen */
09755    adsi_begin(chan, &useadsi);
09756 
09757    if (!valid) {
09758       goto out;
09759    }
09760 
09761 #ifdef IMAP_STORAGE
09762    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09763    pthread_setspecific(ts_vmstate.key, &vms);
09764 
09765    vms.interactive = 1;
09766    vms.updated = 1;
09767    if (vmu)
09768       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09769    vmstate_insert(&vms);
09770    init_vm_state(&vms);
09771 #endif
09772    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
09773    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09774       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09775       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09776       return -1;
09777    }
09778    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
09779       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09780       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09781       return -1;
09782    }
09783    
09784    /* Set language from config to override channel language */
09785    if (!ast_strlen_zero(vmu->language))
09786       ast_string_field_set(chan, language, vmu->language);
09787 
09788    /* Retrieve urgent, old and new message counts */
09789    ast_debug(1, "Before open_mailbox\n");
09790    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09791    if (res < 0)
09792       goto out;
09793    vms.oldmessages = vms.lastmsg + 1;
09794    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
09795    /* check INBOX */
09796    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09797    if (res < 0)
09798       goto out;
09799    vms.newmessages = vms.lastmsg + 1;
09800    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
09801    /* Start in Urgent */
09802    in_urgent = 1;
09803    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09804    if (res < 0)
09805       goto out;
09806    vms.urgentmessages = vms.lastmsg + 1;
09807    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
09808 
09809    /* Select proper mailbox FIRST!! */
09810    if (play_auto) {
09811       if (vms.urgentmessages) {
09812          in_urgent = 1;
09813          res = open_mailbox(&vms, vmu, 11);
09814       } else {
09815          in_urgent = 0;
09816          res = open_mailbox(&vms, vmu, play_folder);
09817       }
09818       if (res < 0)
09819          goto out;
09820 
09821       /* If there are no new messages, inform the user and hangup */
09822       if (vms.lastmsg == -1) {
09823          in_urgent = 0;
09824          cmd = vm_browse_messages(chan, &vms, vmu);
09825          res = 0;
09826          goto out;
09827       }
09828    } else {
09829       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09830          /* If we only have old messages start here */
09831          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09832          in_urgent = 0;
09833          play_folder = 1;
09834          if (res < 0)
09835             goto out;
09836       } else if (!vms.urgentmessages && vms.newmessages) {
09837          /* If we have new messages but none are urgent */
09838          in_urgent = 0;
09839          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09840          if (res < 0)
09841             goto out;
09842       }
09843    }
09844 
09845    if (useadsi)
09846       adsi_status(chan, &vms);
09847    res = 0;
09848 
09849    /* Check to see if this is a new user */
09850    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09851       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09852       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09853          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09854       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09855       if ((cmd == 't') || (cmd == '#')) {
09856          /* Timeout */
09857          res = 0;
09858          goto out;
09859       } else if (cmd < 0) {
09860          /* Hangup */
09861          res = -1;
09862          goto out;
09863       }
09864    }
09865 #ifdef IMAP_STORAGE
09866       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
09867       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09868          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09869          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09870       }
09871       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09872       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09873          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09874          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09875       }
09876 #endif
09877    if (play_auto) {
09878       cmd = '1';
09879    } else {
09880       cmd = vm_intro(chan, vmu, &vms);
09881    }
09882 
09883    vms.repeats = 0;
09884    vms.starting = 1;
09885    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09886       /* Run main menu */
09887       switch (cmd) {
09888       case '1': /* First message */
09889          vms.curmsg = 0;
09890          /* Fall through */
09891       case '5': /* Play current message */
09892          cmd = vm_browse_messages(chan, &vms, vmu);
09893          break;
09894       case '2': /* Change folders */
09895          if (useadsi)
09896             adsi_folders(chan, 0, "Change to folder...");
09897          cmd = get_folder2(chan, "vm-changeto", 0);
09898          if (cmd == '#') {
09899             cmd = 0;
09900          } else if (cmd > 0) {
09901             cmd = cmd - '0';
09902             res = close_mailbox(&vms, vmu);
09903             if (res == ERROR_LOCK_PATH)
09904                goto out;
09905             /* If folder is not urgent, set in_urgent to zero! */
09906             if (cmd != 11) in_urgent = 0;
09907             res = open_mailbox(&vms, vmu, cmd);
09908             if (res < 0)
09909                goto out;
09910             play_folder = cmd;
09911             cmd = 0;
09912          }
09913          if (useadsi)
09914             adsi_status2(chan, &vms);
09915             
09916          if (!cmd)
09917             cmd = vm_play_folder_name(chan, vms.vmbox);
09918 
09919          vms.starting = 1;
09920          break;
09921       case '3': /* Advanced options */
09922          cmd = 0;
09923          vms.repeats = 0;
09924          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09925             switch (cmd) {
09926             case '1': /* Reply */
09927                if (vms.lastmsg > -1 && !vms.starting) {
09928                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09929                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09930                      res = cmd;
09931                      goto out;
09932                   }
09933                } else
09934                   cmd = ast_play_and_wait(chan, "vm-sorry");
09935                cmd = 't';
09936                break;
09937             case '2': /* Callback */
09938                if (!vms.starting)
09939                   ast_verb(3, "Callback Requested\n");
09940                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09941                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09942                   if (cmd == 9) {
09943                      silentexit = 1;
09944                      goto out;
09945                   } else if (cmd == ERROR_LOCK_PATH) {
09946                      res = cmd;
09947                      goto out;
09948                   }
09949                } else 
09950                   cmd = ast_play_and_wait(chan, "vm-sorry");
09951                cmd = 't';
09952                break;
09953             case '3': /* Envelope */
09954                if (vms.lastmsg > -1 && !vms.starting) {
09955                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09956                   if (cmd == ERROR_LOCK_PATH) {
09957                      res = cmd;
09958                      goto out;
09959                   }
09960                } else
09961                   cmd = ast_play_and_wait(chan, "vm-sorry");
09962                cmd = 't';
09963                break;
09964             case '4': /* Dialout */
09965                if (!ast_strlen_zero(vmu->dialout)) {
09966                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09967                   if (cmd == 9) {
09968                      silentexit = 1;
09969                      goto out;
09970                   }
09971                } else 
09972                   cmd = ast_play_and_wait(chan, "vm-sorry");
09973                cmd = 't';
09974                break;
09975 
09976             case '5': /* Leave VoiceMail */
09977                if (ast_test_flag(vmu, VM_SVMAIL)) {
09978                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09979                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
09980                      res = cmd;
09981                      goto out;
09982                   }
09983                } else
09984                   cmd = ast_play_and_wait(chan, "vm-sorry");
09985                cmd = 't';
09986                break;
09987                
09988             case '*': /* Return to main menu */
09989                cmd = 't';
09990                break;
09991 
09992             default:
09993                cmd = 0;
09994                if (!vms.starting) {
09995                   cmd = ast_play_and_wait(chan, "vm-toreply");
09996                }
09997                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09998                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09999                }
10000                if (!cmd && !vms.starting) {
10001                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10002                }
10003                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10004                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10005                }
10006                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
10007                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10008                if (!cmd)
10009                   cmd = ast_play_and_wait(chan, "vm-starmain");
10010                if (!cmd)
10011                   cmd = ast_waitfordigit(chan, 6000);
10012                if (!cmd)
10013                   vms.repeats++;
10014                if (vms.repeats > 3)
10015                   cmd = 't';
10016             }
10017          }
10018          if (cmd == 't') {
10019             cmd = 0;
10020             vms.repeats = 0;
10021          }
10022          break;
10023       case '4': /* Go to the previous message */
10024          if (vms.curmsg > 0) {
10025             vms.curmsg--;
10026             cmd = play_message(chan, vmu, &vms);
10027          } else {
10028             /* Check if we were listening to new
10029                messages.  If so, go to Urgent messages
10030                instead of saying "no more messages"
10031             */
10032             if (in_urgent == 0 && vms.urgentmessages > 0) {
10033                /* Check for Urgent messages */
10034                in_urgent = 1;
10035                res = close_mailbox(&vms, vmu);
10036                if (res == ERROR_LOCK_PATH)
10037                   goto out;
10038                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10039                if (res < 0)
10040                   goto out;
10041                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10042                vms.curmsg = vms.lastmsg;
10043                if (vms.lastmsg < 0)
10044                   cmd = ast_play_and_wait(chan, "vm-nomore");
10045             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10046                vms.curmsg = vms.lastmsg;
10047                cmd = play_message(chan, vmu, &vms);
10048             } else {
10049                cmd = ast_play_and_wait(chan, "vm-nomore");
10050             }
10051          }
10052          break;
10053       case '6': /* Go to the next message */
10054          if (vms.curmsg < vms.lastmsg) {
10055             vms.curmsg++;
10056             cmd = play_message(chan, vmu, &vms);
10057          } else {
10058             if (in_urgent && vms.newmessages > 0) {
10059                /* Check if we were listening to urgent
10060                 * messages.  If so, go to regular new messages
10061                 * instead of saying "no more messages"
10062                 */
10063                in_urgent = 0;
10064                res = close_mailbox(&vms, vmu);
10065                if (res == ERROR_LOCK_PATH)
10066                   goto out;
10067                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10068                if (res < 0)
10069                   goto out;
10070                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10071                vms.curmsg = -1;
10072                if (vms.lastmsg < 0) {
10073                   cmd = ast_play_and_wait(chan, "vm-nomore");
10074                }
10075             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10076                vms.curmsg = 0;
10077                cmd = play_message(chan, vmu, &vms);
10078             } else {
10079                cmd = ast_play_and_wait(chan, "vm-nomore");
10080             }
10081          }
10082          break;
10083       case '7': /* Delete the current message */
10084          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10085             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10086             if (useadsi)
10087                adsi_delete(chan, &vms);
10088             if (vms.deleted[vms.curmsg]) {
10089                if (play_folder == 0) {
10090                   if (in_urgent) {
10091                      vms.urgentmessages--;
10092                   } else {
10093                      vms.newmessages--;
10094                   }
10095                }
10096                else if (play_folder == 1)
10097                   vms.oldmessages--;
10098                cmd = ast_play_and_wait(chan, "vm-deleted");
10099             } else {
10100                if (play_folder == 0) {
10101                   if (in_urgent) {
10102                      vms.urgentmessages++;
10103                   } else {
10104                      vms.newmessages++;
10105                   }
10106                }
10107                else if (play_folder == 1)
10108                   vms.oldmessages++;
10109                cmd = ast_play_and_wait(chan, "vm-undeleted");
10110             }
10111             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10112                if (vms.curmsg < vms.lastmsg) {
10113                   vms.curmsg++;
10114                   cmd = play_message(chan, vmu, &vms);
10115                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10116                   vms.curmsg = 0;
10117                   cmd = play_message(chan, vmu, &vms);
10118                } else {
10119                   /* Check if we were listening to urgent
10120                      messages.  If so, go to regular new messages
10121                      instead of saying "no more messages"
10122                   */
10123                   if (in_urgent == 1) {
10124                      /* Check for new messages */
10125                      in_urgent = 0;
10126                      res = close_mailbox(&vms, vmu);
10127                      if (res == ERROR_LOCK_PATH)
10128                         goto out;
10129                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10130                      if (res < 0)
10131                         goto out;
10132                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10133                      vms.curmsg = -1;
10134                      if (vms.lastmsg < 0)
10135                         cmd = ast_play_and_wait(chan, "vm-nomore");
10136                   } else {
10137                      cmd = ast_play_and_wait(chan, "vm-nomore");
10138                   }
10139                }
10140             }
10141          } else /* Delete not valid if we haven't selected a message */
10142             cmd = 0;
10143 #ifdef IMAP_STORAGE
10144          deleted = 1;
10145 #endif
10146          break;
10147    
10148       case '8': /* Forward the current messgae */
10149          if (vms.lastmsg > -1) {
10150             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10151             if (cmd == ERROR_LOCK_PATH) {
10152                res = cmd;
10153                goto out;
10154             }
10155          } else {
10156             /* Check if we were listening to urgent
10157                messages.  If so, go to regular new messages
10158                instead of saying "no more messages"
10159             */
10160             if (in_urgent == 1 && vms.newmessages > 0) {
10161                /* Check for new messages */
10162                in_urgent = 0;
10163                res = close_mailbox(&vms, vmu);
10164                if (res == ERROR_LOCK_PATH)
10165                   goto out;
10166                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10167                if (res < 0)
10168                   goto out;
10169                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10170                vms.curmsg = -1;
10171                if (vms.lastmsg < 0)
10172                   cmd = ast_play_and_wait(chan, "vm-nomore");
10173             } else {
10174                cmd = ast_play_and_wait(chan, "vm-nomore");
10175             }
10176          }
10177          break;
10178       case '9': /* Save message to folder */
10179          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10180             /* No message selected */
10181             cmd = 0;
10182             break;
10183          }
10184          if (useadsi)
10185             adsi_folders(chan, 1, "Save to folder...");
10186          cmd = get_folder2(chan, "vm-savefolder", 1);
10187          box = 0; /* Shut up compiler */
10188          if (cmd == '#') {
10189             cmd = 0;
10190             break;
10191          } else if (cmd > 0) {
10192             box = cmd = cmd - '0';
10193             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10194             if (cmd == ERROR_LOCK_PATH) {
10195                res = cmd;
10196                goto out;
10197 #ifndef IMAP_STORAGE
10198             } else if (!cmd) {
10199                vms.deleted[vms.curmsg] = 1;
10200 #endif
10201             } else {
10202                vms.deleted[vms.curmsg] = 0;
10203                vms.heard[vms.curmsg] = 0;
10204             }
10205          }
10206          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10207          if (useadsi)
10208             adsi_message(chan, &vms);
10209          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10210          if (!cmd) {
10211             cmd = ast_play_and_wait(chan, "vm-message");
10212             if (!cmd) 
10213                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10214             if (!cmd)
10215                cmd = ast_play_and_wait(chan, "vm-savedto");
10216             if (!cmd)
10217                cmd = vm_play_folder_name(chan, vms.fn);
10218          } else {
10219             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10220          }
10221          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10222             if (vms.curmsg < vms.lastmsg) {
10223                vms.curmsg++;
10224                cmd = play_message(chan, vmu, &vms);
10225             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10226                vms.curmsg = 0;
10227                cmd = play_message(chan, vmu, &vms);
10228             } else {
10229                /* Check if we were listening to urgent
10230                   messages.  If so, go to regular new messages
10231                   instead of saying "no more messages"
10232                */
10233                if (in_urgent == 1 && vms.newmessages > 0) {
10234                   /* Check for new messages */
10235                   in_urgent = 0;
10236                   res = close_mailbox(&vms, vmu);
10237                   if (res == ERROR_LOCK_PATH)
10238                      goto out;
10239                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10240                   if (res < 0)
10241                      goto out;
10242                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10243                   vms.curmsg = -1;
10244                   if (vms.lastmsg < 0)
10245                      cmd = ast_play_and_wait(chan, "vm-nomore");
10246                } else {
10247                   cmd = ast_play_and_wait(chan, "vm-nomore");
10248                }
10249             }
10250          }
10251          break;
10252       case '*': /* Help */
10253          if (!vms.starting) {
10254             cmd = ast_play_and_wait(chan, "vm-onefor");
10255             if (!strncasecmp(chan->language, "he", 2)) {
10256                cmd = ast_play_and_wait(chan, "vm-for");
10257             }
10258             if (!cmd)
10259                cmd = vm_play_folder_name(chan, vms.vmbox);
10260             if (!cmd)
10261                cmd = ast_play_and_wait(chan, "vm-opts");
10262             if (!cmd)
10263                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10264          } else
10265             cmd = 0;
10266          break;
10267       case '0': /* Mailbox options */
10268          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10269          if (useadsi)
10270             adsi_status(chan, &vms);
10271          break;
10272       default: /* Nothing */
10273          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10274          break;
10275       }
10276    }
10277    if ((cmd == 't') || (cmd == '#')) {
10278       /* Timeout */
10279       res = 0;
10280    } else {
10281       /* Hangup */
10282       res = -1;
10283    }
10284 
10285 out:
10286    if (res > -1) {
10287       ast_stopstream(chan);
10288       adsi_goodbye(chan);
10289       if (valid && res != OPERATOR_EXIT) {
10290          if (silentexit)
10291             res = ast_play_and_wait(chan, "vm-dialout");
10292          else 
10293             res = ast_play_and_wait(chan, "vm-goodbye");
10294       }
10295       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10296          res = 0;
10297       }
10298       if (useadsi)
10299          ast_adsi_unload_session(chan);
10300    }
10301    if (vmu)
10302       close_mailbox(&vms, vmu);
10303    if (valid) {
10304       int new = 0, old = 0, urgent = 0;
10305       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10306       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10307       /* Urgent flag not passwd to externnotify here */
10308       run_externnotify(vmu->context, vmu->mailbox, NULL);
10309       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10310       queue_mwi_event(ext_context, urgent, new, old);
10311    }
10312 #ifdef IMAP_STORAGE
10313    /* expunge message - use UID Expunge if supported on IMAP server*/
10314    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10315    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10316       ast_mutex_lock(&vms.lock);
10317 #ifdef HAVE_IMAP_TK2006
10318       if (LEVELUIDPLUS (vms.mailstream)) {
10319          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10320       } else 
10321 #endif
10322          mail_expunge(vms.mailstream);
10323       ast_mutex_unlock(&vms.lock);
10324    }
10325    /*  before we delete the state, we should copy pertinent info
10326     *  back to the persistent model */
10327    if (vmu) {
10328       vmstate_delete(&vms);
10329    }
10330 #endif
10331    if (vmu)
10332       free_user(vmu);
10333    if (vms.deleted)
10334       ast_free(vms.deleted);
10335    if (vms.heard)
10336       ast_free(vms.heard);
10337 
10338 #ifdef IMAP_STORAGE
10339    pthread_setspecific(ts_vmstate.key, NULL);
10340 #endif
10341    return res;
10342 }
10343 
10344 static int vm_exec(struct ast_channel *chan, const char *data)
10345 {
10346    int res = 0;
10347    char *tmp;
10348    struct leave_vm_options leave_options;
10349    struct ast_flags flags = { 0 };
10350    char *opts[OPT_ARG_ARRAY_SIZE];
10351    AST_DECLARE_APP_ARGS(args,
10352       AST_APP_ARG(argv0);
10353       AST_APP_ARG(argv1);
10354    );
10355    
10356    memset(&leave_options, 0, sizeof(leave_options));
10357 
10358    if (chan->_state != AST_STATE_UP)
10359       ast_answer(chan);
10360 
10361    if (!ast_strlen_zero(data)) {
10362       tmp = ast_strdupa(data);
10363       AST_STANDARD_APP_ARGS(args, tmp);
10364       if (args.argc == 2) {
10365          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10366             return -1;
10367          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10368          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10369             int gain;
10370 
10371             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10372                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10373                return -1;
10374             } else {
10375                leave_options.record_gain = (signed char) gain;
10376             }
10377          }
10378          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10379             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10380                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10381          }
10382       }
10383    } else {
10384       char temp[256];
10385       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10386       if (res < 0)
10387          return res;
10388       if (ast_strlen_zero(temp))
10389          return 0;
10390       args.argv0 = ast_strdupa(temp);
10391    }
10392 
10393    res = leave_voicemail(chan, args.argv0, &leave_options);
10394    if (res == 't') {
10395       ast_play_and_wait(chan, "vm-goodbye");
10396       res = 0;
10397    }
10398 
10399    if (res == OPERATOR_EXIT) {
10400       res = 0;
10401    }
10402 
10403    if (res == ERROR_LOCK_PATH) {
10404       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10405       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10406       res = 0;
10407    }
10408 
10409    return res;
10410 }
10411 
10412 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10413 {
10414    struct ast_vm_user *vmu;
10415 
10416    AST_LIST_TRAVERSE(&users, vmu, list) {
10417       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10418          if (strcasecmp(vmu->context, context)) {
10419             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10420                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10421                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10422                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10423          }
10424          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10425          return NULL;
10426       }
10427       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10428          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10429          return NULL;
10430       }
10431    }
10432    
10433    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10434       return NULL;
10435    
10436    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10437    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10438 
10439    AST_LIST_INSERT_TAIL(&users, vmu, list);
10440    
10441    return vmu;
10442 }
10443 
10444 static int append_mailbox(const char *context, const char *box, const char *data)
10445 {
10446    /* Assumes lock is already held */
10447    char *tmp;
10448    char *stringp;
10449    char *s;
10450    struct ast_vm_user *vmu;
10451    char *mailbox_full;
10452    int new = 0, old = 0, urgent = 0;
10453    char secretfn[PATH_MAX] = "";
10454 
10455    tmp = ast_strdupa(data);
10456 
10457    if (!(vmu = find_or_create(context, box)))
10458       return -1;
10459 
10460    populate_defaults(vmu);
10461 
10462    stringp = tmp;
10463    if ((s = strsep(&stringp, ","))) {
10464       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10465    }
10466    if (stringp && (s = strsep(&stringp, ","))) {
10467       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10468    }
10469    if (stringp && (s = strsep(&stringp, ","))) {
10470       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10471    }
10472    if (stringp && (s = strsep(&stringp, ","))) {
10473       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10474    }
10475    if (stringp && (s = strsep(&stringp, ","))) {
10476       apply_options(vmu, s);
10477    }
10478 
10479    switch (vmu->passwordlocation) {
10480    case OPT_PWLOC_SPOOLDIR:
10481       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10482       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10483    }
10484 
10485    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10486    strcpy(mailbox_full, box);
10487    strcat(mailbox_full, "@");
10488    strcat(mailbox_full, context);
10489 
10490    inboxcount2(mailbox_full, &urgent, &new, &old);
10491    queue_mwi_event(mailbox_full, urgent, new, old);
10492 
10493    return 0;
10494 }
10495 
10496 AST_TEST_DEFINE(test_voicemail_vmuser)
10497 {
10498    int res = 0;
10499    struct ast_vm_user *vmu;
10500    /* language parameter seems to only be used for display in manager action */
10501    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10502       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10503       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10504       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10505       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10506       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10507       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir";
10508 #ifdef IMAP_STORAGE
10509    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10510       "imapfolder=INBOX|imapvmshareid=6000";
10511 #endif
10512 
10513    switch (cmd) {
10514    case TEST_INIT:
10515       info->name = "vmuser";
10516       info->category = "/apps/app_voicemail/";
10517       info->summary = "Vmuser unit test";
10518       info->description =
10519          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10520       return AST_TEST_NOT_RUN;
10521    case TEST_EXECUTE:
10522       break;
10523    }
10524 
10525    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10526       return AST_TEST_NOT_RUN;
10527    }
10528    ast_set_flag(vmu, VM_ALLOCED);
10529 
10530    apply_options(vmu, options_string);
10531 
10532    if (!ast_test_flag(vmu, VM_ATTACH)) {
10533       ast_test_status_update(test, "Parse failure for attach option\n");
10534       res = 1;
10535    }
10536    if (strcasecmp(vmu->attachfmt, "wav49")) {
10537       ast_test_status_update(test, "Parse failure for attachftm option\n");
10538       res = 1;
10539    }
10540    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10541       ast_test_status_update(test, "Parse failure for serveremail option\n");
10542       res = 1;
10543    }
10544    if (strcasecmp(vmu->zonetag, "central")) {
10545       ast_test_status_update(test, "Parse failure for tz option\n");
10546       res = 1;
10547    }
10548    if (!ast_test_flag(vmu, VM_DELETE)) {
10549       ast_test_status_update(test, "Parse failure for delete option\n");
10550       res = 1;
10551    }
10552    if (!ast_test_flag(vmu, VM_SAYCID)) {
10553       ast_test_status_update(test, "Parse failure for saycid option\n");
10554       res = 1;
10555    }
10556    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10557       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10558       res = 1;
10559    }
10560    if (!ast_test_flag(vmu, VM_REVIEW)) {
10561       ast_test_status_update(test, "Parse failure for review option\n");
10562       res = 1;
10563    }
10564    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10565       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10566       res = 1;
10567    }
10568    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10569       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10570       res = 1;
10571    }
10572    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10573       ast_test_status_update(test, "Parse failure for operator option\n");
10574       res = 1;
10575    }
10576    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10577       ast_test_status_update(test, "Parse failure for envelope option\n");
10578       res = 1;
10579    }
10580    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10581       ast_test_status_update(test, "Parse failure for moveheard option\n");
10582       res = 1;
10583    }
10584    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10585       ast_test_status_update(test, "Parse failure for sayduration option\n");
10586       res = 1;
10587    }
10588    if (vmu->saydurationm != 5) {
10589       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10590       res = 1;
10591    }
10592    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10593       ast_test_status_update(test, "Parse failure for forcename option\n");
10594       res = 1;
10595    }
10596    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10597       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10598       res = 1;
10599    }
10600    if (strcasecmp(vmu->callback, "somecontext")) {
10601       ast_test_status_update(test, "Parse failure for callbacks option\n");
10602       res = 1;
10603    }
10604    if (strcasecmp(vmu->dialout, "somecontext2")) {
10605       ast_test_status_update(test, "Parse failure for dialout option\n");
10606       res = 1;
10607    }
10608    if (strcasecmp(vmu->exit, "somecontext3")) {
10609       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10610       res = 1;
10611    }
10612    if (vmu->minsecs != 10) {
10613       ast_test_status_update(test, "Parse failure for minsecs option\n");
10614       res = 1;
10615    }
10616    if (vmu->maxsecs != 100) {
10617       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10618       res = 1;
10619    }
10620    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10621       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10622       res = 1;
10623    }
10624    if (vmu->maxdeletedmsg != 50) {
10625       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10626       res = 1;
10627    }
10628    if (vmu->volgain != 1.3) {
10629       ast_test_status_update(test, "Parse failure for volgain option\n");
10630       res = 1;
10631    }
10632    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10633       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10634       res = 1;
10635    }
10636 #ifdef IMAP_STORAGE
10637    apply_options(vmu, option_string2);
10638 
10639    if (strcasecmp(vmu->imapuser, "imapuser")) {
10640       ast_test_status_update(test, "Parse failure for imapuser option\n");
10641       res = 1;
10642    }
10643    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10644       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10645       res = 1;
10646    }
10647    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10648       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10649       res = 1;
10650    }
10651    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10652       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10653       res = 1;
10654    }
10655 #endif
10656 
10657    free_user(vmu);
10658    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10659 }
10660 
10661 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10662 {
10663    struct ast_vm_user svm;
10664    char *context, *box;
10665    AST_DECLARE_APP_ARGS(args,
10666       AST_APP_ARG(mbox);
10667       AST_APP_ARG(options);
10668    );
10669    static int dep_warning = 0;
10670 
10671    if (ast_strlen_zero(data)) {
10672       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10673       return -1;
10674    }
10675 
10676    if (!dep_warning) {
10677       dep_warning = 1;
10678       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10679    }
10680 
10681    box = ast_strdupa(data);
10682 
10683    AST_STANDARD_APP_ARGS(args, box);
10684 
10685    if (args.options) {
10686    }
10687 
10688    if ((context = strchr(args.mbox, '@'))) {
10689       *context = '\0';
10690       context++;
10691    }
10692 
10693    if (find_user(&svm, context, args.mbox)) {
10694       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10695    } else
10696       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10697 
10698    return 0;
10699 }
10700 
10701 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10702 {
10703    struct ast_vm_user svm;
10704    AST_DECLARE_APP_ARGS(arg,
10705       AST_APP_ARG(mbox);
10706       AST_APP_ARG(context);
10707    );
10708 
10709    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10710 
10711    if (ast_strlen_zero(arg.mbox)) {
10712       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10713       return -1;
10714    }
10715 
10716    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10717    return 0;
10718 }
10719 
10720 static struct ast_custom_function mailbox_exists_acf = {
10721    .name = "MAILBOX_EXISTS",
10722    .read = acf_mailbox_exists,
10723 };
10724 
10725 static int vmauthenticate(struct ast_channel *chan, const char *data)
10726 {
10727    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
10728    struct ast_vm_user vmus;
10729    char *options = NULL;
10730    int silent = 0, skipuser = 0;
10731    int res = -1;
10732    
10733    if (data) {
10734       s = ast_strdupa(data);
10735       user = strsep(&s, ",");
10736       options = strsep(&s, ",");
10737       if (user) {
10738          s = user;
10739          user = strsep(&s, "@");
10740          context = strsep(&s, "");
10741          if (!ast_strlen_zero(user))
10742             skipuser++;
10743          ast_copy_string(mailbox, user, sizeof(mailbox));
10744       }
10745    }
10746 
10747    if (options) {
10748       silent = (strchr(options, 's')) != NULL;
10749    }
10750 
10751    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
10752       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
10753       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
10754       ast_play_and_wait(chan, "auth-thankyou");
10755       res = 0;
10756    } else if (mailbox[0] == '*') {
10757       /* user entered '*' */
10758       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10759          res = 0; /* prevent hangup */
10760       }
10761    }
10762 
10763    return res;
10764 }
10765 
10766 static char *show_users_realtime(int fd, const char *context)
10767 {
10768    struct ast_config *cfg;
10769    const char *cat = NULL;
10770 
10771    if (!(cfg = ast_load_realtime_multientry("voicemail", 
10772       "context", context, SENTINEL))) {
10773       return CLI_FAILURE;
10774    }
10775 
10776    ast_cli(fd,
10777       "\n"
10778       "=============================================================\n"
10779       "=== Configured Voicemail Users ==============================\n"
10780       "=============================================================\n"
10781       "===\n");
10782 
10783    while ((cat = ast_category_browse(cfg, cat))) {
10784       struct ast_variable *var = NULL;
10785       ast_cli(fd,
10786          "=== Mailbox ...\n"
10787          "===\n");
10788       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
10789          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
10790       ast_cli(fd,
10791          "===\n"
10792          "=== ---------------------------------------------------------\n"
10793          "===\n");
10794    }
10795 
10796    ast_cli(fd,
10797       "=============================================================\n"
10798       "\n");
10799 
10800    ast_config_destroy(cfg);
10801 
10802    return CLI_SUCCESS;
10803 }
10804 
10805 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
10806 {
10807    int which = 0;
10808    int wordlen;
10809    struct ast_vm_user *vmu;
10810    const char *context = "";
10811 
10812    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
10813    if (pos > 4)
10814       return NULL;
10815    if (pos == 3)
10816       return (state == 0) ? ast_strdup("for") : NULL;
10817    wordlen = strlen(word);
10818    AST_LIST_TRAVERSE(&users, vmu, list) {
10819       if (!strncasecmp(word, vmu->context, wordlen)) {
10820          if (context && strcmp(context, vmu->context) && ++which > state)
10821             return ast_strdup(vmu->context);
10822          /* ignore repeated contexts ? */
10823          context = vmu->context;
10824       }
10825    }
10826    return NULL;
10827 }
10828 
10829 /*! \brief Show a list of voicemail users in the CLI */
10830 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10831 {
10832    struct ast_vm_user *vmu;
10833 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
10834    const char *context = NULL;
10835    int users_counter = 0;
10836 
10837    switch (cmd) {
10838    case CLI_INIT:
10839       e->command = "voicemail show users";
10840       e->usage =
10841          "Usage: voicemail show users [for <context>]\n"
10842          "       Lists all mailboxes currently set up\n";
10843       return NULL;
10844    case CLI_GENERATE:
10845       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10846    }  
10847 
10848    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10849       return CLI_SHOWUSAGE;
10850    if (a->argc == 5) {
10851       if (strcmp(a->argv[3],"for"))
10852          return CLI_SHOWUSAGE;
10853       context = a->argv[4];
10854    }
10855 
10856    if (ast_check_realtime("voicemail")) {
10857       if (!context) {
10858          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10859          return CLI_SHOWUSAGE;
10860       }
10861       return show_users_realtime(a->fd, context);
10862    }
10863 
10864    AST_LIST_LOCK(&users);
10865    if (AST_LIST_EMPTY(&users)) {
10866       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10867       AST_LIST_UNLOCK(&users);
10868       return CLI_FAILURE;
10869    }
10870    if (a->argc == 3)
10871       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10872    else {
10873       int count = 0;
10874       AST_LIST_TRAVERSE(&users, vmu, list) {
10875          if (!strcmp(context, vmu->context))
10876             count++;
10877       }
10878       if (count) {
10879          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10880       } else {
10881          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10882          AST_LIST_UNLOCK(&users);
10883          return CLI_FAILURE;
10884       }
10885    }
10886    AST_LIST_TRAVERSE(&users, vmu, list) {
10887       int newmsgs = 0, oldmsgs = 0;
10888       char count[12], tmp[256] = "";
10889 
10890       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10891          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10892          inboxcount(tmp, &newmsgs, &oldmsgs);
10893          snprintf(count, sizeof(count), "%d", newmsgs);
10894          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10895          users_counter++;
10896       }
10897    }
10898    AST_LIST_UNLOCK(&users);
10899    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10900    return CLI_SUCCESS;
10901 }
10902 
10903 /*! \brief Show a list of voicemail zones in the CLI */
10904 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10905 {
10906    struct vm_zone *zone;
10907 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10908    char *res = CLI_SUCCESS;
10909 
10910    switch (cmd) {
10911    case CLI_INIT:
10912       e->command = "voicemail show zones";
10913       e->usage =
10914          "Usage: voicemail show zones\n"
10915          "       Lists zone message formats\n";
10916       return NULL;
10917    case CLI_GENERATE:
10918       return NULL;
10919    }
10920 
10921    if (a->argc != 3)
10922       return CLI_SHOWUSAGE;
10923 
10924    AST_LIST_LOCK(&zones);
10925    if (!AST_LIST_EMPTY(&zones)) {
10926       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10927       AST_LIST_TRAVERSE(&zones, zone, list) {
10928          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10929       }
10930    } else {
10931       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10932       res = CLI_FAILURE;
10933    }
10934    AST_LIST_UNLOCK(&zones);
10935 
10936    return res;
10937 }
10938 
10939 /*! \brief Reload voicemail configuration from the CLI */
10940 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10941 {
10942    switch (cmd) {
10943    case CLI_INIT:
10944       e->command = "voicemail reload";
10945       e->usage =
10946          "Usage: voicemail reload\n"
10947          "       Reload voicemail configuration\n";
10948       return NULL;
10949    case CLI_GENERATE:
10950       return NULL;
10951    }
10952 
10953    if (a->argc != 2)
10954       return CLI_SHOWUSAGE;
10955 
10956    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10957    load_config(1);
10958    
10959    return CLI_SUCCESS;
10960 }
10961 
10962 static struct ast_cli_entry cli_voicemail[] = {
10963    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10964    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10965    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10966 };
10967 
10968 #ifdef IMAP_STORAGE
10969    #define DATA_EXPORT_VM_USERS(USER)              \
10970       USER(ast_vm_user, context, AST_DATA_STRING)        \
10971       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
10972       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
10973       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
10974       USER(ast_vm_user, email, AST_DATA_STRING)       \
10975       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
10976       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
10977       USER(ast_vm_user, pager, AST_DATA_STRING)       \
10978       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
10979       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
10980       USER(ast_vm_user, language, AST_DATA_STRING)       \
10981       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
10982       USER(ast_vm_user, callback, AST_DATA_STRING)       \
10983       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
10984       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
10985       USER(ast_vm_user, exit, AST_DATA_STRING)        \
10986       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
10987       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
10988       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
10989       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
10990       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
10991       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
10992       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
10993       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
10994       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
10995       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
10996 #else
10997    #define DATA_EXPORT_VM_USERS(USER)              \
10998       USER(ast_vm_user, context, AST_DATA_STRING)        \
10999       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11000       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11001       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11002       USER(ast_vm_user, email, AST_DATA_STRING)       \
11003       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11004       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11005       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11006       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11007       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11008       USER(ast_vm_user, language, AST_DATA_STRING)       \
11009       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11010       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11011       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11012       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11013       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11014       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11015       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11016       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11017       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11018       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11019       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11020       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11021 #endif
11022 
11023 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11024 
11025 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11026    ZONE(vm_zone, name, AST_DATA_STRING)      \
11027    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11028    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11029 
11030 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11031 
11032 /*!
11033  * \internal
11034  * \brief Add voicemail user to the data_root.
11035  * \param[in] search The search tree.
11036  * \param[in] data_root The main result node.
11037  * \param[in] user The voicemail user.
11038  */
11039 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11040     struct ast_data *data_root, struct ast_vm_user *user)
11041 {
11042    struct ast_data *data_user, *data_zone;
11043    struct ast_data *data_state;
11044    struct vm_zone *zone = NULL;
11045    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11046    char ext_context[256] = "";
11047 
11048    data_user = ast_data_add_node(data_root, "user");
11049    if (!data_user) {
11050       return -1;
11051    }
11052 
11053    ast_data_add_structure(ast_vm_user, data_user, user);
11054 
11055    AST_LIST_LOCK(&zones);
11056    AST_LIST_TRAVERSE(&zones, zone, list) {
11057       if (!strcmp(zone->name, user->zonetag)) {
11058          break;
11059       }
11060    }
11061    AST_LIST_UNLOCK(&zones);
11062 
11063    /* state */
11064    data_state = ast_data_add_node(data_user, "state");
11065    if (!data_state) {
11066       return -1;
11067    }
11068    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11069    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11070    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11071    ast_data_add_int(data_state, "newmsg", newmsg);
11072    ast_data_add_int(data_state, "oldmsg", oldmsg);
11073 
11074    if (zone) {
11075       data_zone = ast_data_add_node(data_user, "zone");
11076       ast_data_add_structure(vm_zone, data_zone, zone);
11077    }
11078 
11079    if (!ast_data_search_match(search, data_user)) {
11080       ast_data_remove_node(data_root, data_user);
11081    }
11082 
11083    return 0;
11084 }
11085 
11086 static int vm_users_data_provider_get(const struct ast_data_search *search,
11087    struct ast_data *data_root)
11088 {
11089    struct ast_vm_user *user;
11090 
11091    AST_LIST_LOCK(&users);
11092    AST_LIST_TRAVERSE(&users, user, list) {
11093       vm_users_data_provider_get_helper(search, data_root, user);
11094    }
11095    AST_LIST_UNLOCK(&users);
11096 
11097    return 0;
11098 }
11099 
11100 static const struct ast_data_handler vm_users_data_provider = {
11101    .version = AST_DATA_HANDLER_VERSION,
11102    .get = vm_users_data_provider_get
11103 };
11104 
11105 static const struct ast_data_entry vm_data_providers[] = {
11106    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11107 };
11108 
11109 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11110 {
11111    int new = 0, old = 0, urgent = 0;
11112 
11113    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11114 
11115    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11116       mwi_sub->old_urgent = urgent;
11117       mwi_sub->old_new = new;
11118       mwi_sub->old_old = old;
11119       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11120       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11121    }
11122 }
11123 
11124 static void poll_subscribed_mailboxes(void)
11125 {
11126    struct mwi_sub *mwi_sub;
11127 
11128    AST_RWLIST_RDLOCK(&mwi_subs);
11129    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11130       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11131          poll_subscribed_mailbox(mwi_sub);
11132       }
11133    }
11134    AST_RWLIST_UNLOCK(&mwi_subs);
11135 }
11136 
11137 static void *mb_poll_thread(void *data)
11138 {
11139    while (poll_thread_run) {
11140       struct timespec ts = { 0, };
11141       struct timeval wait;
11142 
11143       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11144       ts.tv_sec = wait.tv_sec;
11145       ts.tv_nsec = wait.tv_usec * 1000;
11146 
11147       ast_mutex_lock(&poll_lock);
11148       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11149       ast_mutex_unlock(&poll_lock);
11150 
11151       if (!poll_thread_run)
11152          break;
11153 
11154       poll_subscribed_mailboxes();
11155    }
11156 
11157    return NULL;
11158 }
11159 
11160 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11161 {
11162    ast_free(mwi_sub);
11163 }
11164 
11165 static int handle_unsubscribe(void *datap)
11166 {
11167    struct mwi_sub *mwi_sub;
11168    uint32_t *uniqueid = datap;
11169    
11170    AST_RWLIST_WRLOCK(&mwi_subs);
11171    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11172       if (mwi_sub->uniqueid == *uniqueid) {
11173          AST_LIST_REMOVE_CURRENT(entry);
11174          break;
11175       }
11176    }
11177    AST_RWLIST_TRAVERSE_SAFE_END
11178    AST_RWLIST_UNLOCK(&mwi_subs);
11179 
11180    if (mwi_sub)
11181       mwi_sub_destroy(mwi_sub);
11182 
11183    ast_free(uniqueid);  
11184    return 0;
11185 }
11186 
11187 static int handle_subscribe(void *datap)
11188 {
11189    unsigned int len;
11190    struct mwi_sub *mwi_sub;
11191    struct mwi_sub_task *p = datap;
11192 
11193    len = sizeof(*mwi_sub);
11194    if (!ast_strlen_zero(p->mailbox))
11195       len += strlen(p->mailbox);
11196 
11197    if (!ast_strlen_zero(p->context))
11198       len += strlen(p->context) + 1; /* Allow for seperator */
11199 
11200    if (!(mwi_sub = ast_calloc(1, len)))
11201       return -1;
11202 
11203    mwi_sub->uniqueid = p->uniqueid;
11204    if (!ast_strlen_zero(p->mailbox))
11205       strcpy(mwi_sub->mailbox, p->mailbox);
11206 
11207    if (!ast_strlen_zero(p->context)) {
11208       strcat(mwi_sub->mailbox, "@");
11209       strcat(mwi_sub->mailbox, p->context);
11210    }
11211 
11212    AST_RWLIST_WRLOCK(&mwi_subs);
11213    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11214    AST_RWLIST_UNLOCK(&mwi_subs);
11215    ast_free((void *) p->mailbox);
11216    ast_free((void *) p->context);
11217    ast_free(p);
11218    poll_subscribed_mailbox(mwi_sub);
11219    return 0;
11220 }
11221 
11222 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11223 {
11224    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11225    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11226       return;
11227 
11228    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11229       return;
11230 
11231    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11232    *uniqueid = u;
11233    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11234       ast_free(uniqueid);
11235    }
11236 }
11237 
11238 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11239 {
11240    struct mwi_sub_task *mwist;
11241    
11242    if (ast_event_get_type(event) != AST_EVENT_SUB)
11243       return;
11244 
11245    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11246       return;
11247 
11248    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11249       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11250       return;
11251    }
11252    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11253    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11254    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11255    
11256    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11257       ast_free(mwist);
11258    }
11259 }
11260 
11261 static void start_poll_thread(void)
11262 {
11263    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11264       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11265       AST_EVENT_IE_END);
11266 
11267    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11268       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11269       AST_EVENT_IE_END);
11270 
11271    if (mwi_sub_sub)
11272       ast_event_report_subs(mwi_sub_sub);
11273 
11274    poll_thread_run = 1;
11275 
11276    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11277 }
11278 
11279 static void stop_poll_thread(void)
11280 {
11281    poll_thread_run = 0;
11282 
11283    if (mwi_sub_sub) {
11284       ast_event_unsubscribe(mwi_sub_sub);
11285       mwi_sub_sub = NULL;
11286    }
11287 
11288    if (mwi_unsub_sub) {
11289       ast_event_unsubscribe(mwi_unsub_sub);
11290       mwi_unsub_sub = NULL;
11291    }
11292 
11293    ast_mutex_lock(&poll_lock);
11294    ast_cond_signal(&poll_cond);
11295    ast_mutex_unlock(&poll_lock);
11296 
11297    pthread_join(poll_thread, NULL);
11298 
11299    poll_thread = AST_PTHREADT_NULL;
11300 }
11301 
11302 /*! \brief Manager list voicemail users command */
11303 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11304 {
11305    struct ast_vm_user *vmu = NULL;
11306    const char *id = astman_get_header(m, "ActionID");
11307    char actionid[128] = "";
11308 
11309    if (!ast_strlen_zero(id))
11310       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11311 
11312    AST_LIST_LOCK(&users);
11313 
11314    if (AST_LIST_EMPTY(&users)) {
11315       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11316       AST_LIST_UNLOCK(&users);
11317       return RESULT_SUCCESS;
11318    }
11319    
11320    astman_send_ack(s, m, "Voicemail user list will follow");
11321    
11322    AST_LIST_TRAVERSE(&users, vmu, list) {
11323       char dirname[256];
11324 
11325 #ifdef IMAP_STORAGE
11326       int new, old;
11327       inboxcount(vmu->mailbox, &new, &old);
11328 #endif
11329       
11330       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11331       astman_append(s,
11332          "%s"
11333          "Event: VoicemailUserEntry\r\n"
11334          "VMContext: %s\r\n"
11335          "VoiceMailbox: %s\r\n"
11336          "Fullname: %s\r\n"
11337          "Email: %s\r\n"
11338          "Pager: %s\r\n"
11339          "ServerEmail: %s\r\n"
11340          "MailCommand: %s\r\n"
11341          "Language: %s\r\n"
11342          "TimeZone: %s\r\n"
11343          "Callback: %s\r\n"
11344          "Dialout: %s\r\n"
11345          "UniqueID: %s\r\n"
11346          "ExitContext: %s\r\n"
11347          "SayDurationMinimum: %d\r\n"
11348          "SayEnvelope: %s\r\n"
11349          "SayCID: %s\r\n"
11350          "AttachMessage: %s\r\n"
11351          "AttachmentFormat: %s\r\n"
11352          "DeleteMessage: %s\r\n"
11353          "VolumeGain: %.2f\r\n"
11354          "CanReview: %s\r\n"
11355          "CallOperator: %s\r\n"
11356          "MaxMessageCount: %d\r\n"
11357          "MaxMessageLength: %d\r\n"
11358          "NewMessageCount: %d\r\n"
11359 #ifdef IMAP_STORAGE
11360          "OldMessageCount: %d\r\n"
11361          "IMAPUser: %s\r\n"
11362 #endif
11363          "\r\n",
11364          actionid,
11365          vmu->context,
11366          vmu->mailbox,
11367          vmu->fullname,
11368          vmu->email,
11369          vmu->pager,
11370          vmu->serveremail,
11371          vmu->mailcmd,
11372          vmu->language,
11373          vmu->zonetag,
11374          vmu->callback,
11375          vmu->dialout,
11376          vmu->uniqueid,
11377          vmu->exit,
11378          vmu->saydurationm,
11379          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11380          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11381          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11382          vmu->attachfmt,
11383          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11384          vmu->volgain,
11385          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11386          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11387          vmu->maxmsg,
11388          vmu->maxsecs,
11389 #ifdef IMAP_STORAGE
11390          new, old, vmu->imapuser
11391 #else
11392          count_messages(vmu, dirname)
11393 #endif
11394          );
11395    }     
11396    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11397 
11398    AST_LIST_UNLOCK(&users);
11399 
11400    return RESULT_SUCCESS;
11401 }
11402 
11403 /*! \brief Free the users structure. */
11404 static void free_vm_users(void) 
11405 {
11406    struct ast_vm_user *current;
11407    AST_LIST_LOCK(&users);
11408    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11409       ast_set_flag(current, VM_ALLOCED);
11410       free_user(current);
11411    }
11412    AST_LIST_UNLOCK(&users);
11413 }
11414 
11415 /*! \brief Free the zones structure. */
11416 static void free_vm_zones(void)
11417 {
11418    struct vm_zone *zcur;
11419    AST_LIST_LOCK(&zones);
11420    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11421       free_zone(zcur);
11422    AST_LIST_UNLOCK(&zones);
11423 }
11424 
11425 static const char *substitute_escapes(const char *value)
11426 {
11427    char *current;
11428 
11429    /* Add 16 for fudge factor */
11430    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11431 
11432    ast_str_reset(str);
11433    
11434    /* Substitute strings \r, \n, and \t into the appropriate characters */
11435    for (current = (char *) value; *current; current++) {
11436       if (*current == '\\') {
11437          current++;
11438          if (!*current) {
11439             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11440             break;
11441          }
11442          switch (*current) {
11443          case 'r':
11444             ast_str_append(&str, 0, "\r");
11445             break;
11446          case 'n':
11447 #ifdef IMAP_STORAGE
11448             if (!str->used || str->str[str->used - 1] != '\r') {
11449                ast_str_append(&str, 0, "\r");
11450             }
11451 #endif
11452             ast_str_append(&str, 0, "\n");
11453             break;
11454          case 't':
11455             ast_str_append(&str, 0, "\t");
11456             break;
11457          default:
11458             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11459             break;
11460          }
11461       } else {
11462          ast_str_append(&str, 0, "%c", *current);
11463       }
11464    }
11465 
11466    return ast_str_buffer(str);
11467 }
11468 
11469 static int load_config(int reload)
11470 {
11471    struct ast_vm_user *current;
11472    struct ast_config *cfg, *ucfg;
11473    char *cat;
11474    struct ast_variable *var;
11475    const char *val;
11476    char *q, *stringp, *tmp;
11477    int x;
11478    int tmpadsi[4];
11479    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11480    char secretfn[PATH_MAX] = "";
11481 
11482    ast_unload_realtime("voicemail");
11483    ast_unload_realtime("voicemail_data");
11484 
11485    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11486       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11487          return 0;
11488       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11489          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11490          ucfg = NULL;
11491       }
11492       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11493       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11494          ast_config_destroy(ucfg);
11495          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11496          return 0;
11497       }
11498    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11499       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11500       return 0;
11501    } else {
11502       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11503       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11504          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11505          ucfg = NULL;
11506       }
11507    }
11508 #ifdef IMAP_STORAGE
11509    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11510 #endif
11511    /* set audio control prompts */
11512    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11513    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11514    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11515    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11516    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11517 
11518    /* Free all the users structure */  
11519    free_vm_users();
11520 
11521    /* Free all the zones structure */
11522    free_vm_zones();
11523 
11524    AST_LIST_LOCK(&users);  
11525 
11526    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11527    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11528 
11529    if (cfg) {
11530       /* General settings */
11531 
11532       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11533          val = "default";
11534       ast_copy_string(userscontext, val, sizeof(userscontext));
11535       /* Attach voice message to mail message ? */
11536       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11537          val = "yes";
11538       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11539 
11540       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11541          val = "no";
11542       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11543 
11544       volgain = 0.0;
11545       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11546          sscanf(val, "%30lf", &volgain);
11547 
11548 #ifdef ODBC_STORAGE
11549       strcpy(odbc_database, "asterisk");
11550       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11551          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11552       }
11553       strcpy(odbc_table, "voicemessages");
11554       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11555          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11556       }
11557 #endif      
11558       /* Mail command */
11559       strcpy(mailcmd, SENDMAIL);
11560       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11561          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11562 
11563       maxsilence = 0;
11564       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11565          maxsilence = atoi(val);
11566          if (maxsilence > 0)
11567             maxsilence *= 1000;
11568       }
11569       
11570       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11571          maxmsg = MAXMSG;
11572       } else {
11573          maxmsg = atoi(val);
11574          if (maxmsg < 0) {
11575             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11576             maxmsg = MAXMSG;
11577          } else if (maxmsg > MAXMSGLIMIT) {
11578             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11579             maxmsg = MAXMSGLIMIT;
11580          }
11581       }
11582 
11583       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11584          maxdeletedmsg = 0;
11585       } else {
11586          if (sscanf(val, "%30d", &x) == 1)
11587             maxdeletedmsg = x;
11588          else if (ast_true(val))
11589             maxdeletedmsg = MAXMSG;
11590          else
11591             maxdeletedmsg = 0;
11592 
11593          if (maxdeletedmsg < 0) {
11594             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11595             maxdeletedmsg = MAXMSG;
11596          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11597             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11598             maxdeletedmsg = MAXMSGLIMIT;
11599          }
11600       }
11601 
11602       /* Load date format config for voicemail mail */
11603       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11604          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11605       }
11606 
11607       /* Load date format config for voicemail pager mail */
11608       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11609          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11610       }
11611 
11612       /* External password changing command */
11613       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11614          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11615          pwdchange = PWDCHANGE_EXTERNAL;
11616       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11617          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11618          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11619       }
11620  
11621       /* External password validation command */
11622       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11623          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11624          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11625       }
11626 
11627 #ifdef IMAP_STORAGE
11628       /* IMAP server address */
11629       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11630          ast_copy_string(imapserver, val, sizeof(imapserver));
11631       } else {
11632          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11633       }
11634       /* IMAP server port */
11635       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11636          ast_copy_string(imapport, val, sizeof(imapport));
11637       } else {
11638          ast_copy_string(imapport, "143", sizeof(imapport));
11639       }
11640       /* IMAP server flags */
11641       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11642          ast_copy_string(imapflags, val, sizeof(imapflags));
11643       }
11644       /* IMAP server master username */
11645       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11646          ast_copy_string(authuser, val, sizeof(authuser));
11647       }
11648       /* IMAP server master password */
11649       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11650          ast_copy_string(authpassword, val, sizeof(authpassword));
11651       }
11652       /* Expunge on exit */
11653       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11654          if (ast_false(val))
11655             expungeonhangup = 0;
11656          else
11657             expungeonhangup = 1;
11658       } else {
11659          expungeonhangup = 1;
11660       }
11661       /* IMAP voicemail folder */
11662       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11663          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11664       } else {
11665          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11666       }
11667       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11668          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11669       }
11670       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11671          imapgreetings = ast_true(val);
11672       } else {
11673          imapgreetings = 0;
11674       }
11675       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11676          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11677       } else {
11678          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11679       }
11680 
11681       /* There is some very unorthodox casting done here. This is due
11682        * to the way c-client handles the argument passed in. It expects a 
11683        * void pointer and casts the pointer directly to a long without
11684        * first dereferencing it. */
11685       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11686          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11687       } else {
11688          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11689       }
11690 
11691       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11692          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11693       } else {
11694          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11695       }
11696 
11697       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
11698          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
11699       } else {
11700          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
11701       }
11702 
11703       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
11704          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
11705       } else {
11706          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
11707       }
11708 
11709       /* Increment configuration version */
11710       imapversion++;
11711 #endif
11712       /* External voicemail notify application */
11713       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
11714          ast_copy_string(externnotify, val, sizeof(externnotify));
11715          ast_debug(1, "found externnotify: %s\n", externnotify);
11716       } else {
11717          externnotify[0] = '\0';
11718       }
11719 
11720       /* SMDI voicemail notification */
11721       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
11722          ast_debug(1, "Enabled SMDI voicemail notification\n");
11723          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
11724             smdi_iface = ast_smdi_interface_find(val);
11725          } else {
11726             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
11727             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
11728          }
11729          if (!smdi_iface) {
11730             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
11731          } 
11732       }
11733 
11734       /* Silence treshold */
11735       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
11736       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
11737          silencethreshold = atoi(val);
11738       
11739       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
11740          val = ASTERISK_USERNAME;
11741       ast_copy_string(serveremail, val, sizeof(serveremail));
11742       
11743       vmmaxsecs = 0;
11744       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
11745          if (sscanf(val, "%30d", &x) == 1) {
11746             vmmaxsecs = x;
11747          } else {
11748             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11749          }
11750       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
11751          static int maxmessage_deprecate = 0;
11752          if (maxmessage_deprecate == 0) {
11753             maxmessage_deprecate = 1;
11754             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
11755          }
11756          if (sscanf(val, "%30d", &x) == 1) {
11757             vmmaxsecs = x;
11758          } else {
11759             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
11760          }
11761       }
11762 
11763       vmminsecs = 0;
11764       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
11765          if (sscanf(val, "%30d", &x) == 1) {
11766             vmminsecs = x;
11767             if (maxsilence / 1000 >= vmminsecs) {
11768                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
11769             }
11770          } else {
11771             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11772          }
11773       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
11774          static int maxmessage_deprecate = 0;
11775          if (maxmessage_deprecate == 0) {
11776             maxmessage_deprecate = 1;
11777             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
11778          }
11779          if (sscanf(val, "%30d", &x) == 1) {
11780             vmminsecs = x;
11781             if (maxsilence / 1000 >= vmminsecs) {
11782                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
11783             }
11784          } else {
11785             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
11786          }
11787       }
11788 
11789       val = ast_variable_retrieve(cfg, "general", "format");
11790       if (!val) {
11791          val = "wav";   
11792       } else {
11793          tmp = ast_strdupa(val);
11794          val = ast_format_str_reduce(tmp);
11795          if (!val) {
11796             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
11797             val = "wav";
11798          }
11799       }
11800       ast_copy_string(vmfmts, val, sizeof(vmfmts));
11801 
11802       skipms = 3000;
11803       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
11804          if (sscanf(val, "%30d", &x) == 1) {
11805             maxgreet = x;
11806          } else {
11807             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
11808          }
11809       }
11810 
11811       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
11812          if (sscanf(val, "%30d", &x) == 1) {
11813             skipms = x;
11814          } else {
11815             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
11816          }
11817       }
11818 
11819       maxlogins = 3;
11820       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
11821          if (sscanf(val, "%30d", &x) == 1) {
11822             maxlogins = x;
11823          } else {
11824             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
11825          }
11826       }
11827 
11828       minpassword = MINPASSWORD;
11829       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
11830          if (sscanf(val, "%30d", &x) == 1) {
11831             minpassword = x;
11832          } else {
11833             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
11834          }
11835       }
11836 
11837       /* Force new user to record name ? */
11838       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
11839          val = "no";
11840       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
11841 
11842       /* Force new user to record greetings ? */
11843       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
11844          val = "no";
11845       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
11846 
11847       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
11848          ast_debug(1, "VM_CID Internal context string: %s\n", val);
11849          stringp = ast_strdupa(val);
11850          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
11851             if (!ast_strlen_zero(stringp)) {
11852                q = strsep(&stringp, ",");
11853                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
11854                   q++;
11855                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
11856                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
11857             } else {
11858                cidinternalcontexts[x][0] = '\0';
11859             }
11860          }
11861       }
11862       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
11863          ast_debug(1, "VM Review Option disabled globally\n");
11864          val = "no";
11865       }
11866       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
11867 
11868       /* Temporary greeting reminder */
11869       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
11870          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
11871          val = "no";
11872       } else {
11873          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
11874       }
11875       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
11876       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
11877          ast_debug(1, "VM next message wrap disabled globally\n");
11878          val = "no";
11879       }
11880       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
11881 
11882       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
11883          ast_debug(1, "VM Operator break disabled globally\n");
11884          val = "no";
11885       }
11886       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
11887 
11888       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
11889          ast_debug(1, "VM CID Info before msg disabled globally\n");
11890          val = "no";
11891       } 
11892       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
11893 
11894       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
11895          ast_debug(1, "Send Voicemail msg disabled globally\n");
11896          val = "no";
11897       }
11898       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
11899    
11900       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
11901          ast_debug(1, "ENVELOPE before msg enabled globally\n");
11902          val = "yes";
11903       }
11904       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
11905 
11906       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
11907          ast_debug(1, "Move Heard enabled globally\n");
11908          val = "yes";
11909       }
11910       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
11911 
11912       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
11913          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
11914          val = "no";
11915       }
11916       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
11917 
11918       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
11919          ast_debug(1, "Duration info before msg enabled globally\n");
11920          val = "yes";
11921       }
11922       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
11923 
11924       saydurationminfo = 2;
11925       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
11926          if (sscanf(val, "%30d", &x) == 1) {
11927             saydurationminfo = x;
11928          } else {
11929             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
11930          }
11931       }
11932 
11933       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
11934          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
11935          val = "no";
11936       }
11937       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
11938 
11939       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
11940          ast_copy_string(dialcontext, val, sizeof(dialcontext));
11941          ast_debug(1, "found dialout context: %s\n", dialcontext);
11942       } else {
11943          dialcontext[0] = '\0';  
11944       }
11945       
11946       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
11947          ast_copy_string(callcontext, val, sizeof(callcontext));
11948          ast_debug(1, "found callback context: %s\n", callcontext);
11949       } else {
11950          callcontext[0] = '\0';
11951       }
11952 
11953       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
11954          ast_copy_string(exitcontext, val, sizeof(exitcontext));
11955          ast_debug(1, "found operator context: %s\n", exitcontext);
11956       } else {
11957          exitcontext[0] = '\0';
11958       }
11959       
11960       /* load password sounds configuration */
11961       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
11962          ast_copy_string(vm_password, val, sizeof(vm_password));
11963       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
11964          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
11965       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
11966          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
11967       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
11968          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
11969       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
11970          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
11971       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
11972          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
11973       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
11974          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
11975       }
11976       /* load configurable audio prompts */
11977       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
11978          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
11979       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
11980          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
11981       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
11982          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
11983       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11984          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11985       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11986          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11987 
11988       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
11989          val = "no";
11990       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
11991 
11992       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
11993          val = "voicemail.conf";
11994       }
11995       if (!(strcmp(val, "spooldir"))) {
11996          passwordlocation = OPT_PWLOC_SPOOLDIR;
11997       } else {
11998          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
11999       }
12000 
12001       poll_freq = DEFAULT_POLL_FREQ;
12002       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12003          if (sscanf(val, "%30u", &poll_freq) != 1) {
12004             poll_freq = DEFAULT_POLL_FREQ;
12005             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12006          }
12007       }
12008 
12009       poll_mailboxes = 0;
12010       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12011          poll_mailboxes = ast_true(val);
12012 
12013       if (ucfg) { 
12014          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12015             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12016                continue;
12017             if ((current = find_or_create(userscontext, cat))) {
12018                populate_defaults(current);
12019                apply_options_full(current, ast_variable_browse(ucfg, cat));
12020                ast_copy_string(current->context, userscontext, sizeof(current->context));
12021                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12022                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12023                }
12024 
12025                switch (current->passwordlocation) {
12026                case OPT_PWLOC_SPOOLDIR:
12027                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12028                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12029                }
12030             }
12031          }
12032          ast_config_destroy(ucfg);
12033       }
12034       cat = ast_category_browse(cfg, NULL);
12035       while (cat) {
12036          if (strcasecmp(cat, "general")) {
12037             var = ast_variable_browse(cfg, cat);
12038             if (strcasecmp(cat, "zonemessages")) {
12039                /* Process mailboxes in this context */
12040                while (var) {
12041                   append_mailbox(cat, var->name, var->value);
12042                   var = var->next;
12043                }
12044             } else {
12045                /* Timezones in this context */
12046                while (var) {
12047                   struct vm_zone *z;
12048                   if ((z = ast_malloc(sizeof(*z)))) {
12049                      char *msg_format, *tzone;
12050                      msg_format = ast_strdupa(var->value);
12051                      tzone = strsep(&msg_format, "|,");
12052                      if (msg_format) {
12053                         ast_copy_string(z->name, var->name, sizeof(z->name));
12054                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12055                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12056                         AST_LIST_LOCK(&zones);
12057                         AST_LIST_INSERT_HEAD(&zones, z, list);
12058                         AST_LIST_UNLOCK(&zones);
12059                      } else {
12060                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12061                         ast_free(z);
12062                      }
12063                   } else {
12064                      AST_LIST_UNLOCK(&users);
12065                      ast_config_destroy(cfg);
12066                      return -1;
12067                   }
12068                   var = var->next;
12069                }
12070             }
12071          }
12072          cat = ast_category_browse(cfg, cat);
12073       }
12074       memset(fromstring, 0, sizeof(fromstring));
12075       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12076       strcpy(charset, "ISO-8859-1");
12077       if (emailbody) {
12078          ast_free(emailbody);
12079          emailbody = NULL;
12080       }
12081       if (emailsubject) {
12082          ast_free(emailsubject);
12083          emailsubject = NULL;
12084       }
12085       if (pagerbody) {
12086          ast_free(pagerbody);
12087          pagerbody = NULL;
12088       }
12089       if (pagersubject) {
12090          ast_free(pagersubject);
12091          pagersubject = NULL;
12092       }
12093       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12094          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12095       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12096          ast_copy_string(fromstring, val, sizeof(fromstring));
12097       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12098          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12099       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12100          ast_copy_string(charset, val, sizeof(charset));
12101       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12102          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12103          for (x = 0; x < 4; x++) {
12104             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12105          }
12106       }
12107       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12108          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12109          for (x = 0; x < 4; x++) {
12110             memcpy(&adsisec[x], &tmpadsi[x], 1);
12111          }
12112       }
12113       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12114          if (atoi(val)) {
12115             adsiver = atoi(val);
12116          }
12117       }
12118       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12119          ast_copy_string(zonetag, val, sizeof(zonetag));
12120       }
12121       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12122          ast_copy_string(locale, val, sizeof(locale));
12123       }
12124       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12125          emailsubject = ast_strdup(val);
12126       }
12127       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12128          emailbody = ast_strdup(substitute_escapes(val));
12129       }
12130       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12131          pagersubject = ast_strdup(val);
12132       }
12133       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12134          pagerbody = ast_strdup(substitute_escapes(val));
12135       }
12136       AST_LIST_UNLOCK(&users);
12137       ast_config_destroy(cfg);
12138 
12139       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12140          start_poll_thread();
12141       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12142          stop_poll_thread();;
12143 
12144       return 0;
12145    } else {
12146       AST_LIST_UNLOCK(&users);
12147       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12148       if (ucfg)
12149          ast_config_destroy(ucfg);
12150       return 0;
12151    }
12152 }
12153 
12154 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12155 {
12156    int res = -1;
12157    char dir[PATH_MAX];
12158    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12159    ast_debug(2, "About to try retrieving name file %s\n", dir);
12160    RETRIEVE(dir, -1, mailbox, context);
12161    if (ast_fileexists(dir, NULL, NULL)) {
12162       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12163    }
12164    DISPOSE(dir, -1);
12165    return res;
12166 }
12167 
12168 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12169    struct ast_config *pwconf;
12170    struct ast_flags config_flags = { 0 };
12171 
12172    pwconf = ast_config_load(secretfn, config_flags);
12173    if (pwconf) {
12174       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12175       if (val) {
12176          ast_copy_string(password, val, passwordlen);
12177          return;
12178       }
12179    }
12180    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12181 }
12182 
12183 static int write_password_to_file(const char *secretfn, const char *password) {
12184    struct ast_config *conf;
12185    struct ast_category *cat;
12186    struct ast_variable *var;
12187 
12188    if (!(conf=ast_config_new())) {
12189       ast_log(LOG_ERROR, "Error creating new config structure\n");
12190       return -1;
12191    }
12192    if (!(cat=ast_category_new("general","",1))) {
12193       ast_log(LOG_ERROR, "Error creating new category structure\n");
12194       return -1;
12195    }
12196    if (!(var=ast_variable_new("password",password,""))) {
12197       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12198       return -1;
12199    }
12200    ast_category_append(conf,cat);
12201    ast_variable_append(cat,var);
12202    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12203       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12204       return -1;
12205    }
12206    return 0;
12207 }
12208 
12209 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12210 {
12211    char *context;
12212    char *args_copy;
12213    int res;
12214 
12215    if (ast_strlen_zero(data)) {
12216       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12217       return -1;
12218    }
12219 
12220    args_copy = ast_strdupa(data);
12221    if ((context = strchr(args_copy, '@'))) {
12222       *context++ = '\0';
12223    } else {
12224       context = "default";
12225    }
12226 
12227    if ((res = sayname(chan, args_copy, context) < 0)) {
12228       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12229       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12230       if (!res) {
12231          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12232       }
12233    }
12234 
12235    return res;
12236 }
12237 
12238 #ifdef TEST_FRAMEWORK
12239 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12240 {
12241    return 0;
12242 }
12243 
12244 static struct ast_frame *fake_read(struct ast_channel *ast)
12245 {
12246    return &ast_null_frame;
12247 }
12248 
12249 AST_TEST_DEFINE(test_voicemail_vmsayname)
12250 {
12251    char dir[PATH_MAX];
12252    char dir2[PATH_MAX];
12253    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12254    static const char TEST_EXTENSION[] = "1234";
12255 
12256    struct ast_channel *test_channel1 = NULL;
12257    int res = -1;
12258 
12259    static const struct ast_channel_tech fake_tech = {
12260       .write = fake_write,
12261       .read = fake_read,
12262    };
12263 
12264    switch (cmd) {
12265    case TEST_INIT:
12266       info->name = "vmsayname_exec";
12267       info->category = "/apps/app_voicemail/";
12268       info->summary = "Vmsayname unit test";
12269       info->description =
12270          "This tests passing various parameters to vmsayname";
12271       return AST_TEST_NOT_RUN;
12272    case TEST_EXECUTE:
12273       break;
12274    }
12275 
12276    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12277         NULL, NULL, 0, 0, "TestChannel1"))) {
12278       goto exit_vmsayname_test;
12279    }
12280 
12281    /* normally this is done in the channel driver */
12282    test_channel1->nativeformats = AST_FORMAT_GSM;
12283    test_channel1->writeformat = AST_FORMAT_GSM;
12284    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12285    test_channel1->readformat = AST_FORMAT_GSM;
12286    test_channel1->rawreadformat = AST_FORMAT_GSM;
12287    test_channel1->tech = &fake_tech;
12288 
12289    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12290    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12291    if (!(res = vmsayname_exec(test_channel1, dir))) {
12292       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12293       if (ast_fileexists(dir, NULL, NULL)) {
12294          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12295          res = -1;
12296          goto exit_vmsayname_test;
12297       } else {
12298          /* no greeting already exists as expected, let's create one to fully test sayname */
12299          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12300             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12301             goto exit_vmsayname_test;
12302          }
12303          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12304          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12305          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12306          if ((res = symlink(dir, dir2))) {
12307             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12308             goto exit_vmsayname_test;
12309          }
12310          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12311          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12312          res = vmsayname_exec(test_channel1, dir);
12313 
12314          /* TODO: there may be a better way to do this */
12315          unlink(dir2);
12316          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12317          rmdir(dir2);
12318          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12319          rmdir(dir2);
12320       }
12321    }
12322 
12323 exit_vmsayname_test:
12324 
12325    if (test_channel1) {
12326       ast_hangup(test_channel1);
12327    }
12328 
12329    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12330 }
12331 
12332 AST_TEST_DEFINE(test_voicemail_msgcount)
12333 {
12334    int i, j, res = AST_TEST_PASS, syserr;
12335    struct ast_vm_user *vmu;
12336    struct vm_state vms;
12337 #ifdef IMAP_STORAGE
12338    struct ast_channel *chan = NULL;
12339 #endif
12340    struct {
12341       char dir[256];
12342       char file[256];
12343       char txtfile[256];
12344    } tmp[3];
12345    char syscmd[256];
12346    const char origweasels[] = "tt-weasels";
12347    const char testcontext[] = "test";
12348    const char testmailbox[] = "00000000";
12349    const char testspec[] = "00000000@test";
12350    FILE *txt;
12351    int new, old, urgent;
12352    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12353    const int folder2mbox[3] = { 1, 11, 0 };
12354    const int expected_results[3][12] = {
12355       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12356       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12357       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12358       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12359    };
12360 
12361    switch (cmd) {
12362    case TEST_INIT:
12363       info->name = "test_voicemail_msgcount";
12364       info->category = "/apps/app_voicemail/";
12365       info->summary = "Test Voicemail status checks";
12366       info->description =
12367          "Verify that message counts are correct when retrieved through the public API";
12368       return AST_TEST_NOT_RUN;
12369    case TEST_EXECUTE:
12370       break;
12371    }
12372 
12373    /* Make sure the original path was completely empty */
12374    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12375    if ((syserr = ast_safe_system(syscmd))) {
12376       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12377          syserr > 0 ? strerror(syserr) : "unable to fork()");
12378       return AST_TEST_FAIL;
12379    }
12380 
12381 #ifdef IMAP_STORAGE
12382    if (!(chan = ast_dummy_channel_alloc())) {
12383       ast_test_status_update(test, "Unable to create dummy channel\n");
12384       return AST_TEST_FAIL;
12385    }
12386 #endif
12387 
12388    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12389       !(vmu = find_or_create(testcontext, testmailbox))) {
12390       ast_test_status_update(test, "Cannot create vmu structure\n");
12391       ast_unreplace_sigchld();
12392       return AST_TEST_FAIL;
12393    }
12394 
12395    populate_defaults(vmu);
12396    memset(&vms, 0, sizeof(vms));
12397 
12398    /* Create temporary voicemail */
12399    for (i = 0; i < 3; i++) {
12400       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12401       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12402       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12403 
12404       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12405          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12406             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12407          if ((syserr = ast_safe_system(syscmd))) {
12408             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12409                syserr > 0 ? strerror(syserr) : "unable to fork()");
12410             ast_unreplace_sigchld();
12411             return AST_TEST_FAIL;
12412          }
12413       }
12414 
12415       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12416          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12417          fclose(txt);
12418       } else {
12419          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12420          res = AST_TEST_FAIL;
12421          break;
12422       }
12423       open_mailbox(&vms, vmu, folder2mbox[i]);
12424       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12425 
12426       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12427       for (j = 0; j < 3; j++) {
12428          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12429          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12430             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12431                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12432             res = AST_TEST_FAIL;
12433          }
12434       }
12435 
12436       new = old = urgent = 0;
12437       if (ast_app_inboxcount(testspec, &new, &old)) {
12438          ast_test_status_update(test, "inboxcount returned failure\n");
12439          res = AST_TEST_FAIL;
12440       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12441          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12442             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12443          res = AST_TEST_FAIL;
12444       }
12445 
12446       new = old = urgent = 0;
12447       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12448          ast_test_status_update(test, "inboxcount2 returned failure\n");
12449          res = AST_TEST_FAIL;
12450       } else if (old != expected_results[i][6 + 0] ||
12451             urgent != expected_results[i][6 + 1] ||
12452                new != expected_results[i][6 + 2]    ) {
12453          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12454             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12455          res = AST_TEST_FAIL;
12456       }
12457 
12458       new = old = urgent = 0;
12459       for (j = 0; j < 3; j++) {
12460          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12461             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12462                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12463             res = AST_TEST_FAIL;
12464          }
12465       }
12466    }
12467 
12468    for (i = 0; i < 3; i++) {
12469       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12470        * server, in which case, the rm below will not affect the
12471        * voicemails. */
12472       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12473       DISPOSE(tmp[i].dir, 0);
12474    }
12475 
12476    if (vms.deleted) {
12477       ast_free(vms.deleted);
12478    }
12479    if (vms.heard) {
12480       ast_free(vms.heard);
12481    }
12482 
12483 #ifdef IMAP_STORAGE
12484    chan = ast_channel_release(chan);
12485 #endif
12486 
12487    /* And remove test directory */
12488    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12489    if ((syserr = ast_safe_system(syscmd))) {
12490       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12491          syserr > 0 ? strerror(syserr) : "unable to fork()");
12492    }
12493 
12494    return res;
12495 }
12496 
12497 AST_TEST_DEFINE(test_voicemail_notify_endl)
12498 {
12499    int res = AST_TEST_PASS;
12500    char testcontext[] = "test";
12501    char testmailbox[] = "00000000";
12502    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12503    char attach[256], attach2[256];
12504    char buf[256] = ""; /* No line should actually be longer than 80 */
12505    struct ast_channel *chan = NULL;
12506    struct ast_vm_user *vmu, vmus = {
12507       .flags = 0,
12508    };
12509    FILE *file;
12510    struct {
12511       char *name;
12512       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12513       void *location;
12514       union {
12515          int intval;
12516          char *strval;
12517       } u;
12518    } test_items[] = {
12519       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12520       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12521       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12522       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12523       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12524       { "attach2", STRPTR, attach2, .u.strval = "" },
12525       { "attach", STRPTR, attach, .u.strval = "" },
12526    };
12527    int which;
12528 
12529    switch (cmd) {
12530    case TEST_INIT:
12531       info->name = "test_voicemail_notify_endl";
12532       info->category = "/apps/app_voicemail/";
12533       info->summary = "Test Voicemail notification end-of-line";
12534       info->description =
12535          "Verify that notification emails use a consistent end-of-line character";
12536       return AST_TEST_NOT_RUN;
12537    case TEST_EXECUTE:
12538       break;
12539    }
12540 
12541    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12542    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12543 
12544    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12545       !(vmu = find_or_create(testcontext, testmailbox))) {
12546       ast_test_status_update(test, "Cannot create vmu structure\n");
12547       return AST_TEST_NOT_RUN;
12548    }
12549 
12550    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12551       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12552       return AST_TEST_NOT_RUN;
12553    }
12554 
12555    populate_defaults(vmu);
12556    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12557 #ifdef IMAP_STORAGE
12558    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12559 #endif
12560 
12561    file = tmpfile();
12562    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12563       /* Kill previous test, if any */
12564       rewind(file);
12565       if (ftruncate(fileno(file), 0)) {
12566          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12567          res = AST_TEST_FAIL;
12568          break;
12569       }
12570 
12571       /* Make each change, in order, to the test mailbox */
12572       if (test_items[which].type == INT) {
12573          *((int *) test_items[which].location) = test_items[which].u.intval;
12574       } else if (test_items[which].type == FLAGVAL) {
12575          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12576             ast_clear_flag(vmu, test_items[which].u.intval);
12577          } else {
12578             ast_set_flag(vmu, test_items[which].u.intval);
12579          }
12580       } else if (test_items[which].type == STATIC) {
12581          strcpy(test_items[which].location, test_items[which].u.strval);
12582       } else if (test_items[which].type == STRPTR) {
12583          test_items[which].location = test_items[which].u.strval;
12584       }
12585 
12586       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12587       rewind(file);
12588       while (fgets(buf, sizeof(buf), file)) {
12589          if (
12590 #ifdef IMAP_STORAGE
12591          buf[strlen(buf) - 2] != '\r'
12592 #else
12593          buf[strlen(buf) - 2] == '\r'
12594 #endif
12595          || buf[strlen(buf) - 1] != '\n') {
12596             res = AST_TEST_FAIL;
12597          }
12598       }
12599    }
12600    fclose(file);
12601    return res;
12602 }
12603 #endif /* defined(TEST_FRAMEWORK) */
12604 
12605 static int reload(void)
12606 {
12607    return load_config(1);
12608 }
12609 
12610 static int unload_module(void)
12611 {
12612    int res;
12613 
12614    res = ast_unregister_application(app);
12615    res |= ast_unregister_application(app2);
12616    res |= ast_unregister_application(app3);
12617    res |= ast_unregister_application(app4);
12618    res |= ast_unregister_application(sayname_app);
12619    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12620    res |= ast_manager_unregister("VoicemailUsersList");
12621    res |= ast_data_unregister(NULL);
12622 #ifdef TEST_FRAMEWORK
12623    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12624    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12625    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12626    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12627 #endif
12628    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12629    ast_uninstall_vm_functions();
12630    ao2_ref(inprocess_container, -1);
12631 
12632    if (poll_thread != AST_PTHREADT_NULL)
12633       stop_poll_thread();
12634 
12635    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12636    ast_unload_realtime("voicemail");
12637    ast_unload_realtime("voicemail_data");
12638 
12639    free_vm_users();
12640    free_vm_zones();
12641    return res;
12642 }
12643 
12644 static int load_module(void)
12645 {
12646    int res;
12647    my_umask = umask(0);
12648    umask(my_umask);
12649 
12650    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12651       return AST_MODULE_LOAD_DECLINE;
12652    }
12653 
12654    /* compute the location of the voicemail spool directory */
12655    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12656    
12657    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12658       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12659    }
12660 
12661    if ((res = load_config(0)))
12662       return res;
12663 
12664    res = ast_register_application_xml(app, vm_exec);
12665    res |= ast_register_application_xml(app2, vm_execmain);
12666    res |= ast_register_application_xml(app3, vm_box_exists);
12667    res |= ast_register_application_xml(app4, vmauthenticate);
12668    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12669    res |= ast_custom_function_register(&mailbox_exists_acf);
12670    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12671 #ifdef TEST_FRAMEWORK
12672    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12673    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12674    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12675    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12676 #endif
12677 
12678    if (res)
12679       return res;
12680 
12681    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12682    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12683 
12684    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12685    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12686    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12687 
12688    return res;
12689 }
12690 
12691 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
12692 {
12693    int cmd = 0;
12694    char destination[80] = "";
12695    int retries = 0;
12696 
12697    if (!num) {
12698       ast_verb(3, "Destination number will be entered manually\n");
12699       while (retries < 3 && cmd != 't') {
12700          destination[1] = '\0';
12701          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
12702          if (!cmd)
12703             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
12704          if (!cmd)
12705             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
12706          if (!cmd) {
12707             cmd = ast_waitfordigit(chan, 6000);
12708             if (cmd)
12709                destination[0] = cmd;
12710          }
12711          if (!cmd) {
12712             retries++;
12713          } else {
12714 
12715             if (cmd < 0)
12716                return 0;
12717             if (cmd == '*') {
12718                ast_verb(3, "User hit '*' to cancel outgoing call\n");
12719                return 0;
12720             }
12721             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
12722                retries++;
12723             else
12724                cmd = 't';
12725          }
12726       }
12727       if (retries >= 3) {
12728          return 0;
12729       }
12730       
12731    } else {
12732       if (option_verbose > 2)
12733          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
12734       ast_copy_string(destination, num, sizeof(destination));
12735    }
12736 
12737    if (!ast_strlen_zero(destination)) {
12738       if (destination[strlen(destination) -1 ] == '*')
12739          return 0; 
12740       if (option_verbose > 2)
12741          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
12742       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
12743       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
12744       chan->priority = 0;
12745       return 9;
12746    }
12747    return 0;
12748 }
12749 
12750 /*!
12751  * \brief The advanced options within a message.
12752  * \param chan
12753  * \param vmu 
12754  * \param vms
12755  * \param msg
12756  * \param option
12757  * \param record_gain
12758  *
12759  * Provides handling for the play message envelope, call the person back, or reply to message. 
12760  *
12761  * \return zero on success, -1 on error.
12762  */
12763 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)
12764 {
12765    int res = 0;
12766    char filename[PATH_MAX];
12767    struct ast_config *msg_cfg = NULL;
12768    const char *origtime, *context;
12769    char *name, *num;
12770    int retries = 0;
12771    char *cid;
12772    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
12773 
12774    vms->starting = 0; 
12775 
12776    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12777 
12778    /* Retrieve info from VM attribute file */
12779    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
12780    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
12781    msg_cfg = ast_config_load(filename, config_flags);
12782    DISPOSE(vms->curdir, vms->curmsg);
12783    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
12784       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
12785       return 0;
12786    }
12787 
12788    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
12789       ast_config_destroy(msg_cfg);
12790       return 0;
12791    }
12792 
12793    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
12794 
12795    context = ast_variable_retrieve(msg_cfg, "message", "context");
12796    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
12797       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
12798    switch (option) {
12799    case 3: /* Play message envelope */
12800       if (!res)
12801          res = play_message_datetime(chan, vmu, origtime, filename);
12802       if (!res)
12803          res = play_message_callerid(chan, vms, cid, context, 0);
12804 
12805       res = 't';
12806       break;
12807 
12808    case 2:  /* Call back */
12809 
12810       if (ast_strlen_zero(cid))
12811          break;
12812 
12813       ast_callerid_parse(cid, &name, &num);
12814       while ((res > -1) && (res != 't')) {
12815          switch (res) {
12816          case '1':
12817             if (num) {
12818                /* Dial the CID number */
12819                res = dialout(chan, vmu, num, vmu->callback);
12820                if (res) {
12821                   ast_config_destroy(msg_cfg);
12822                   return 9;
12823                }
12824             } else {
12825                res = '2';
12826             }
12827             break;
12828 
12829          case '2':
12830             /* Want to enter a different number, can only do this if there's a dialout context for this user */
12831             if (!ast_strlen_zero(vmu->dialout)) {
12832                res = dialout(chan, vmu, NULL, vmu->dialout);
12833                if (res) {
12834                   ast_config_destroy(msg_cfg);
12835                   return 9;
12836                }
12837             } else {
12838                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
12839                res = ast_play_and_wait(chan, "vm-sorry");
12840             }
12841             ast_config_destroy(msg_cfg);
12842             return res;
12843          case '*':
12844             res = 't';
12845             break;
12846          case '3':
12847          case '4':
12848          case '5':
12849          case '6':
12850          case '7':
12851          case '8':
12852          case '9':
12853          case '0':
12854 
12855             res = ast_play_and_wait(chan, "vm-sorry");
12856             retries++;
12857             break;
12858          default:
12859             if (num) {
12860                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
12861                res = ast_play_and_wait(chan, "vm-num-i-have");
12862                if (!res)
12863                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
12864                if (!res)
12865                   res = ast_play_and_wait(chan, "vm-tocallnum");
12866                /* Only prompt for a caller-specified number if there is a dialout context specified */
12867                if (!ast_strlen_zero(vmu->dialout)) {
12868                   if (!res)
12869                      res = ast_play_and_wait(chan, "vm-calldiffnum");
12870                }
12871             } else {
12872                res = ast_play_and_wait(chan, "vm-nonumber");
12873                if (!ast_strlen_zero(vmu->dialout)) {
12874                   if (!res)
12875                      res = ast_play_and_wait(chan, "vm-toenternumber");
12876                }
12877             }
12878             if (!res)
12879                res = ast_play_and_wait(chan, "vm-star-cancel");
12880             if (!res)
12881                res = ast_waitfordigit(chan, 6000);
12882             if (!res) {
12883                retries++;
12884                if (retries > 3)
12885                   res = 't';
12886             }
12887             break; 
12888             
12889          }
12890          if (res == 't')
12891             res = 0;
12892          else if (res == '*')
12893             res = -1;
12894       }
12895       break;
12896       
12897    case 1:  /* Reply */
12898       /* Send reply directly to sender */
12899       if (ast_strlen_zero(cid))
12900          break;
12901 
12902       ast_callerid_parse(cid, &name, &num);
12903       if (!num) {
12904          ast_verb(3, "No CID number available, no reply sent\n");
12905          if (!res)
12906             res = ast_play_and_wait(chan, "vm-nonumber");
12907          ast_config_destroy(msg_cfg);
12908          return res;
12909       } else {
12910          struct ast_vm_user vmu2;
12911          if (find_user(&vmu2, vmu->context, num)) {
12912             struct leave_vm_options leave_options;
12913             char mailbox[AST_MAX_EXTENSION * 2 + 2];
12914             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
12915 
12916             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
12917             
12918             memset(&leave_options, 0, sizeof(leave_options));
12919             leave_options.record_gain = record_gain;
12920             res = leave_voicemail(chan, mailbox, &leave_options);
12921             if (!res)
12922                res = 't';
12923             ast_config_destroy(msg_cfg);
12924             return res;
12925          } else {
12926             /* Sender has no mailbox, can't reply */
12927             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
12928             ast_play_and_wait(chan, "vm-nobox");
12929             res = 't';
12930             ast_config_destroy(msg_cfg);
12931             return res;
12932          }
12933       } 
12934       res = 0;
12935 
12936       break;
12937    }
12938 
12939 #ifndef IMAP_STORAGE
12940    ast_config_destroy(msg_cfg);
12941 
12942    if (!res) {
12943       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
12944       vms->heard[msg] = 1;
12945       res = wait_file(chan, vms, vms->fn);
12946    }
12947 #endif
12948    return res;
12949 }
12950 
12951 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
12952          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
12953          signed char record_gain, struct vm_state *vms, char *flag)
12954 {
12955    /* Record message & let caller review or re-record it, or set options if applicable */
12956    int res = 0;
12957    int cmd = 0;
12958    int max_attempts = 3;
12959    int attempts = 0;
12960    int recorded = 0;
12961    int msg_exists = 0;
12962    signed char zero_gain = 0;
12963    char tempfile[PATH_MAX];
12964    char *acceptdtmf = "#";
12965    char *canceldtmf = "";
12966    int canceleddtmf = 0;
12967 
12968    /* Note that urgent and private are for flagging messages as such in the future */
12969 
12970    /* barf if no pointer passed to store duration in */
12971    if (duration == NULL) {
12972       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
12973       return -1;
12974    }
12975 
12976    if (!outsidecaller)
12977       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
12978    else
12979       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
12980 
12981    cmd = '3';  /* Want to start by recording */
12982 
12983    while ((cmd >= 0) && (cmd != 't')) {
12984       switch (cmd) {
12985       case '1':
12986          if (!msg_exists) {
12987             /* In this case, 1 is to record a message */
12988             cmd = '3';
12989             break;
12990          } else {
12991             /* Otherwise 1 is to save the existing message */
12992             ast_verb(3, "Saving message as is\n");
12993             if (!outsidecaller) 
12994                ast_filerename(tempfile, recordfile, NULL);
12995             ast_stream_and_wait(chan, "vm-msgsaved", "");
12996             if (!outsidecaller) {
12997                /* Saves to IMAP server only if imapgreeting=yes */
12998                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
12999                DISPOSE(recordfile, -1);
13000             }
13001             cmd = 't';
13002             return res;
13003          }
13004       case '2':
13005          /* Review */
13006          ast_verb(3, "Reviewing the message\n");
13007          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13008          break;
13009       case '3':
13010          msg_exists = 0;
13011          /* Record */
13012          if (recorded == 1) 
13013             ast_verb(3, "Re-recording the message\n");
13014          else  
13015             ast_verb(3, "Recording the message\n");
13016          
13017          if (recorded && outsidecaller) {
13018             cmd = ast_play_and_wait(chan, INTRO);
13019             cmd = ast_play_and_wait(chan, "beep");
13020          }
13021          recorded = 1;
13022          /* 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 */
13023          if (record_gain)
13024             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13025          if (ast_test_flag(vmu, VM_OPERATOR))
13026             canceldtmf = "0";
13027          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13028          if (strchr(canceldtmf, cmd)) {
13029          /* need this flag here to distinguish between pressing '0' during message recording or after */
13030             canceleddtmf = 1;
13031          }
13032          if (record_gain)
13033             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13034          if (cmd == -1) {
13035             /* User has hung up, no options to give */
13036             if (!outsidecaller) {
13037                /* user was recording a greeting and they hung up, so let's delete the recording. */
13038                ast_filedelete(tempfile, NULL);
13039             }     
13040             return cmd;
13041          }
13042          if (cmd == '0') {
13043             break;
13044          } else if (cmd == '*') {
13045             break;
13046 #if 0
13047          } else if (vmu->review && (*duration < 5)) {
13048             /* Message is too short */
13049             ast_verb(3, "Message too short\n");
13050             cmd = ast_play_and_wait(chan, "vm-tooshort");
13051             cmd = ast_filedelete(tempfile, NULL);
13052             break;
13053          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13054             /* Message is all silence */
13055             ast_verb(3, "Nothing recorded\n");
13056             cmd = ast_filedelete(tempfile, NULL);
13057             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13058             if (!cmd)
13059                cmd = ast_play_and_wait(chan, "vm-speakup");
13060             break;
13061 #endif
13062          } else {
13063             /* If all is well, a message exists */
13064             msg_exists = 1;
13065             cmd = 0;
13066          }
13067          break;
13068       case '4':
13069          if (outsidecaller) {  /* only mark vm messages */
13070             /* Mark Urgent */
13071             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13072                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13073                res = ast_play_and_wait(chan, "vm-marked-urgent");
13074                strcpy(flag, "Urgent");
13075             } else if (flag) {
13076                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13077                res = ast_play_and_wait(chan, "vm-urgent-removed");
13078                strcpy(flag, "");
13079             } else {
13080                ast_play_and_wait(chan, "vm-sorry");
13081             }
13082             cmd = 0;
13083          } else {
13084             cmd = ast_play_and_wait(chan, "vm-sorry");
13085          }
13086          break;
13087       case '5':
13088       case '6':
13089       case '7':
13090       case '8':
13091       case '9':
13092       case '*':
13093       case '#':
13094          cmd = ast_play_and_wait(chan, "vm-sorry");
13095          break;
13096 #if 0 
13097 /*  XXX Commented out for the moment because of the dangers of deleting
13098     a message while recording (can put the message numbers out of sync) */
13099       case '*':
13100          /* Cancel recording, delete message, offer to take another message*/
13101          cmd = ast_play_and_wait(chan, "vm-deleted");
13102          cmd = ast_filedelete(tempfile, NULL);
13103          if (outsidecaller) {
13104             res = vm_exec(chan, NULL);
13105             return res;
13106          }
13107          else
13108             return 1;
13109 #endif
13110       case '0':
13111          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13112             cmd = ast_play_and_wait(chan, "vm-sorry");
13113             break;
13114          }
13115          if (msg_exists || recorded) {
13116             cmd = ast_play_and_wait(chan, "vm-saveoper");
13117             if (!cmd)
13118                cmd = ast_waitfordigit(chan, 3000);
13119             if (cmd == '1') {
13120                ast_filerename(tempfile, recordfile, NULL);
13121                ast_play_and_wait(chan, "vm-msgsaved");
13122                cmd = '0';
13123             } else if (cmd == '4') {
13124                if (flag) {
13125                   ast_play_and_wait(chan, "vm-marked-urgent");
13126                   strcpy(flag, "Urgent");
13127                }
13128                ast_play_and_wait(chan, "vm-msgsaved");
13129                cmd = '0';
13130             } else {
13131                ast_play_and_wait(chan, "vm-deleted");
13132                DELETE(tempfile, -1, tempfile, vmu);
13133                cmd = '0';
13134             }
13135          }
13136          return cmd;
13137       default:
13138          /* If the caller is an ouside caller, and the review option is enabled,
13139             allow them to review the message, but let the owner of the box review
13140             their OGM's */
13141          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13142             return cmd;
13143          if (msg_exists) {
13144             cmd = ast_play_and_wait(chan, "vm-review");
13145             if (!cmd && outsidecaller) {
13146                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13147                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13148                } else if (flag) {
13149                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13150                }
13151             }
13152          } else {
13153             cmd = ast_play_and_wait(chan, "vm-torerecord");
13154             if (!cmd)
13155                cmd = ast_waitfordigit(chan, 600);
13156          }
13157          
13158          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13159             cmd = ast_play_and_wait(chan, "vm-reachoper");
13160             if (!cmd)
13161                cmd = ast_waitfordigit(chan, 600);
13162          }
13163 #if 0
13164          if (!cmd)
13165             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13166 #endif
13167          if (!cmd)
13168             cmd = ast_waitfordigit(chan, 6000);
13169          if (!cmd) {
13170             attempts++;
13171          }
13172          if (attempts > max_attempts) {
13173             cmd = 't';
13174          }
13175       }
13176    }
13177    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13178       /* Hang up or timeout, so delete the recording. */
13179       ast_filedelete(tempfile, NULL);
13180    }
13181 
13182    if (cmd != 't' && outsidecaller)
13183       ast_play_and_wait(chan, "vm-goodbye");
13184 
13185    return cmd;
13186 }
13187 
13188 /* This is a workaround so that menuselect displays a proper description
13189  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13190  */
13191 
13192 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13193       .load = load_module,
13194       .unload = unload_module,
13195       .reload = reload,
13196       .nonoptreq = "res_adsi,res_smdi",
13197       );

Generated on Wed Apr 6 11:29:40 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7