Sat Feb 11 06:33:03 2012

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use type="module">res_adsi</use>
00042    <use type="module">res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00048    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00049       <conflict>ODBC_STORAGE</conflict>
00050       <conflict>IMAP_STORAGE</conflict>
00051       <defaultenabled>yes</defaultenabled>
00052       <support_level>core</support_level>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>generic_odbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00063       <depend>imap_tk</depend>
00064       <conflict>ODBC_STORAGE</conflict>
00065       <conflict>FILE_STORAGE</conflict>
00066       <use type="external">openssl</use>
00067       <defaultenabled>no</defaultenabled>
00068       <support_level>core</support_level>
00069    </member>
00070 </category>
00071 ***/
00072 
00073 #include "asterisk.h"
00074 
00075 #ifdef IMAP_STORAGE
00076 #include <ctype.h>
00077 #include <signal.h>
00078 #include <pwd.h>
00079 #ifdef USE_SYSTEM_IMAP
00080 #include <imap/c-client.h>
00081 #include <imap/imap4r1.h>
00082 #include <imap/linkage.h>
00083 #elif defined (USE_SYSTEM_CCLIENT)
00084 #include <c-client/c-client.h>
00085 #include <c-client/imap4r1.h>
00086 #include <c-client/linkage.h>
00087 #else
00088 #include "c-client.h"
00089 #include "imap4r1.h"
00090 #include "linkage.h"
00091 #endif
00092 #endif
00093 
00094 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 354903 $")
00095 
00096 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00097 #include <sys/time.h>
00098 #include <sys/stat.h>
00099 #include <sys/mman.h>
00100 #include <time.h>
00101 #include <dirent.h>
00102 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00103 #include <sys/wait.h>
00104 #endif
00105 
00106 #include "asterisk/logger.h"
00107 #include "asterisk/lock.h"
00108 #include "asterisk/file.h"
00109 #include "asterisk/channel.h"
00110 #include "asterisk/pbx.h"
00111 #include "asterisk/config.h"
00112 #include "asterisk/say.h"
00113 #include "asterisk/module.h"
00114 #include "asterisk/adsi.h"
00115 #include "asterisk/app.h"
00116 #include "asterisk/manager.h"
00117 #include "asterisk/dsp.h"
00118 #include "asterisk/localtime.h"
00119 #include "asterisk/cli.h"
00120 #include "asterisk/utils.h"
00121 #include "asterisk/stringfields.h"
00122 #include "asterisk/smdi.h"
00123 #include "asterisk/astobj2.h"
00124 #include "asterisk/event.h"
00125 #include "asterisk/taskprocessor.h"
00126 #include "asterisk/test.h"
00127 
00128 #ifdef ODBC_STORAGE
00129 #include "asterisk/res_odbc.h"
00130 #endif
00131 
00132 #ifdef IMAP_STORAGE
00133 #include "asterisk/threadstorage.h"
00134 #endif
00135 
00136 /*** DOCUMENTATION
00137    <application name="VoiceMail" language="en_US">
00138       <synopsis>
00139          Leave a Voicemail message.
00140       </synopsis>
00141       <syntax>
00142          <parameter name="mailboxs" argsep="&amp;" required="true">
00143             <argument name="mailbox1" argsep="@" required="true">
00144                <argument name="mailbox" required="true" />
00145                <argument name="context" />
00146             </argument>
00147             <argument name="mailbox2" argsep="@" multiple="true">
00148                <argument name="mailbox" required="true" />
00149                <argument name="context" />
00150             </argument>
00151          </parameter>
00152          <parameter name="options">
00153             <optionlist>
00154                <option name="b">
00155                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00156                </option>
00157                <option name="d">
00158                   <argument name="c" />
00159                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00160                   if played during the greeting. Context defaults to the current context.</para>
00161                </option>
00162                <option name="g">
00163                   <argument name="#" required="true" />
00164                   <para>Use the specified amount of gain when recording the voicemail
00165                   message. The units are whole-number decibels (dB). Only works on supported
00166                   technologies, which is DAHDI only.</para>
00167                </option>
00168                <option name="s">
00169                   <para>Skip the playback of instructions for leaving a message to the
00170                   calling party.</para>
00171                </option>
00172                <option name="u">
00173                   <para>Play the <literal>unavailable</literal> greeting.</para>
00174                </option>
00175                <option name="U">
00176                   <para>Mark message as <literal>URGENT</literal>.</para>
00177                </option>
00178                <option name="P">
00179                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00180                </option>
00181             </optionlist>
00182          </parameter>
00183       </syntax>
00184       <description>
00185          <para>This application allows the calling party to leave a message for the specified
00186          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00187          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00188          exist.</para>
00189          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00190          <enumlist>
00191             <enum name="0">
00192                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00193             </enum>
00194             <enum name="*">
00195                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00196             </enum>
00197          </enumlist>
00198          <para>This application will set the following channel variable upon completion:</para>
00199          <variablelist>
00200             <variable name="VMSTATUS">
00201                <para>This indicates the status of the execution of the VoiceMail application.</para>
00202                <value name="SUCCESS" />
00203                <value name="USEREXIT" />
00204                <value name="FAILED" />
00205             </variable>
00206          </variablelist>
00207       </description>
00208       <see-also>
00209          <ref type="application">VoiceMailMain</ref>
00210       </see-also>
00211    </application>
00212    <application name="VoiceMailMain" language="en_US">
00213       <synopsis>
00214          Check Voicemail messages.
00215       </synopsis>
00216       <syntax>
00217          <parameter name="mailbox" required="true" argsep="@">
00218             <argument name="mailbox" />
00219             <argument name="context" />
00220          </parameter>
00221          <parameter name="options">
00222             <optionlist>
00223                <option name="p">
00224                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00225                   the mailbox that is entered by the caller.</para>
00226                </option>
00227                <option name="g">
00228                   <argument name="#" required="true" />
00229                   <para>Use the specified amount of gain when recording a voicemail message.
00230                   The units are whole-number decibels (dB).</para>
00231                </option>
00232                <option name="s">
00233                   <para>Skip checking the passcode for the mailbox.</para>
00234                </option>
00235                <option name="a">
00236                   <argument name="folder" required="true" />
00237                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00238                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00239                   <enumlist>
00240                      <enum name="0"><para>INBOX</para></enum>
00241                      <enum name="1"><para>Old</para></enum>
00242                      <enum name="2"><para>Work</para></enum>
00243                      <enum name="3"><para>Family</para></enum>
00244                      <enum name="4"><para>Friends</para></enum>
00245                      <enum name="5"><para>Cust1</para></enum>
00246                      <enum name="6"><para>Cust2</para></enum>
00247                      <enum name="7"><para>Cust3</para></enum>
00248                      <enum name="8"><para>Cust4</para></enum>
00249                      <enum name="9"><para>Cust5</para></enum>
00250                   </enumlist>
00251                </option>
00252             </optionlist>
00253          </parameter>
00254       </syntax>
00255       <description>
00256          <para>This application allows the calling party to check voicemail messages. A specific
00257          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00258          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00259          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00260          <literal>default</literal> context will be used.</para>
00261          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00262          or Password, and the extension exists:</para>
00263          <enumlist>
00264             <enum name="*">
00265                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00266             </enum>
00267          </enumlist>
00268       </description>
00269       <see-also>
00270          <ref type="application">VoiceMail</ref>
00271       </see-also>
00272    </application>
00273    <application name="MailboxExists" language="en_US">
00274       <synopsis>
00275          Check to see if Voicemail mailbox exists.
00276       </synopsis>
00277       <syntax>
00278          <parameter name="mailbox" required="true" argsep="@">
00279             <argument name="mailbox" required="true" />
00280             <argument name="context" />
00281          </parameter>
00282          <parameter name="options">
00283             <para>None options.</para>
00284          </parameter>
00285       </syntax>
00286       <description>
00287          <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
00288          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00289          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00290          will be used.</para>
00291          <para>This application will set the following channel variable upon completion:</para>
00292          <variablelist>
00293             <variable name="VMBOXEXISTSSTATUS">
00294                <para>This will contain the status of the execution of the MailboxExists application.
00295                Possible values include:</para>
00296                <value name="SUCCESS" />
00297                <value name="FAILED" />
00298             </variable>
00299          </variablelist>
00300       </description>
00301       <see-also>
00302          <ref type="function">VM_INFO</ref>
00303       </see-also>
00304    </application>
00305    <application name="VMAuthenticate" language="en_US">
00306       <synopsis>
00307          Authenticate with Voicemail passwords.
00308       </synopsis>
00309       <syntax>
00310          <parameter name="mailbox" required="true" argsep="@">
00311             <argument name="mailbox" />
00312             <argument name="context" />
00313          </parameter>
00314          <parameter name="options">
00315             <optionlist>
00316                <option name="s">
00317                   <para>Skip playing the initial prompts.</para>
00318                </option>
00319             </optionlist>
00320          </parameter>
00321       </syntax>
00322       <description>
00323          <para>This application behaves the same way as the Authenticate application, but the passwords
00324          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00325          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00326          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00327          mailbox.</para>
00328          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00329          or Password, and the extension exists:</para>
00330          <enumlist>
00331             <enum name="*">
00332                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00333             </enum>
00334          </enumlist>
00335       </description>
00336    </application>
00337    <application name="VMSayName" language="en_US">
00338       <synopsis>
00339          Play the name of a voicemail user
00340       </synopsis>
00341       <syntax>
00342          <parameter name="mailbox" required="true" argsep="@">
00343             <argument name="mailbox" />
00344             <argument name="context" />
00345          </parameter>
00346       </syntax>
00347       <description>
00348          <para>This application will say the recorded name of the voicemail user specified as the
00349          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00350       </description>
00351    </application>
00352    <function name="MAILBOX_EXISTS" language="en_US">
00353       <synopsis>
00354          Tell if a mailbox is configured.
00355       </synopsis>
00356       <syntax argsep="@">
00357          <parameter name="mailbox" required="true" />
00358          <parameter name="context" />
00359       </syntax>
00360       <description>
00361          <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
00362          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00363          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00364          context.</para>
00365       </description>
00366       <see-also>
00367          <ref type="function">VM_INFO</ref>
00368       </see-also>
00369    </function>
00370    <function name="VM_INFO" language="en_US">
00371       <synopsis>
00372          Returns the selected attribute from a mailbox.
00373       </synopsis>
00374       <syntax argsep=",">
00375          <parameter name="mailbox" argsep="@" required="true">
00376             <argument name="mailbox" required="true" />
00377             <argument name="context" />
00378          </parameter>
00379          <parameter name="attribute" required="true">
00380             <optionlist>
00381                <option name="count">
00382                   <para>Count of messages in specified <replaceable>folder</replaceable>.
00383                   If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
00384                </option>
00385                <option name="email">
00386                   <para>E-mail address associated with the mailbox.</para>
00387                </option>
00388                <option name="exists">
00389                   <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
00390                </option>
00391                <option name="fullname">
00392                   <para>Full name associated with the mailbox.</para>
00393                </option>
00394                <option name="language">
00395                   <para>Mailbox language if overridden, otherwise the language of the channel.</para>
00396                </option>
00397                <option name="locale">
00398                   <para>Mailbox locale if overridden, otherwise global locale.</para>
00399                </option>
00400                <option name="pager">
00401                   <para>Pager e-mail address associated with the mailbox.</para>
00402                </option>
00403                <option name="password">
00404                   <para>Mailbox access password.</para>
00405                </option>
00406                <option name="tz">
00407                   <para>Mailbox timezone if overridden, otherwise global timezone</para>
00408                </option>
00409             </optionlist>
00410          </parameter>
00411          <parameter name="folder" required="false">
00412             <para>If not specified, <literal>INBOX</literal> is assumed.</para>
00413          </parameter>
00414       </syntax>
00415       <description>
00416          <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
00417          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00418          context. Where the <replaceable>folder</replaceable> can be specified, common folders
00419          include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
00420          <literal>Family</literal> and <literal>Friends</literal>.</para>
00421       </description>
00422    </function>
00423    <manager name="VoicemailUsersList" language="en_US">
00424       <synopsis>
00425          List All Voicemail User Information.
00426       </synopsis>
00427       <syntax>
00428          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00429       </syntax>
00430       <description>
00431       </description>
00432    </manager>
00433  ***/
00434 
00435 #ifdef IMAP_STORAGE
00436 static char imapserver[48];
00437 static char imapport[8];
00438 static char imapflags[128];
00439 static char imapfolder[64];
00440 static char imapparentfolder[64] = "\0";
00441 static char greetingfolder[64];
00442 static char authuser[32];
00443 static char authpassword[42];
00444 static int imapversion = 1;
00445 
00446 static int expungeonhangup = 1;
00447 static int imapgreetings = 0;
00448 static char delimiter = '\0';
00449 
00450 struct vm_state;
00451 struct ast_vm_user;
00452 
00453 AST_THREADSTORAGE(ts_vmstate);
00454 
00455 /* Forward declarations for IMAP */
00456 static int init_mailstream(struct vm_state *vms, int box);
00457 static void write_file(char *filename, char *buffer, unsigned long len);
00458 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00459 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00460 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00461 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00462 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00463 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00464 static void vmstate_insert(struct vm_state *vms);
00465 static void vmstate_delete(struct vm_state *vms);
00466 static void set_update(MAILSTREAM * stream);
00467 static void init_vm_state(struct vm_state *vms);
00468 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00469 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
00470 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00471 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00472 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);
00473 static void update_messages_by_imapuser(const char *user, unsigned long number);
00474 static int vm_delete(char *file);
00475 
00476 static int imap_remove_file (char *dir, int msgnum);
00477 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00478 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00479 static void check_quota(struct vm_state *vms, char *mailbox);
00480 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00481 struct vmstate {
00482    struct vm_state *vms;
00483    AST_LIST_ENTRY(vmstate) list;
00484 };
00485 
00486 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00487 
00488 #endif
00489 
00490 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00491 
00492 #define COMMAND_TIMEOUT 5000
00493 /* Don't modify these here; set your umask at runtime instead */
00494 #define  VOICEMAIL_DIR_MODE   0777
00495 #define  VOICEMAIL_FILE_MODE  0666
00496 #define  CHUNKSIZE   65536
00497 
00498 #define VOICEMAIL_CONFIG "voicemail.conf"
00499 #define ASTERISK_USERNAME "asterisk"
00500 
00501 /* Define fast-forward, pause, restart, and reverse keys
00502  * while listening to a voicemail message - these are
00503  * strings, not characters */
00504 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00505 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00506 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00507 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00508 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00509 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00510 
00511 /* Default mail command to mail voicemail. Change it with the
00512  * mailcmd= command in voicemail.conf */
00513 #define SENDMAIL "/usr/sbin/sendmail -t"
00514 
00515 #define INTRO "vm-intro"
00516 
00517 #define MAXMSG 100
00518 #define MAXMSGLIMIT 9999
00519 
00520 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00521 
00522 #define BASELINELEN 72
00523 #define BASEMAXINLINE 256
00524 #ifdef IMAP_STORAGE
00525 #define ENDL "\r\n"
00526 #else
00527 #define ENDL "\n"
00528 #endif
00529 
00530 #define MAX_DATETIME_FORMAT   512
00531 #define MAX_NUM_CID_CONTEXTS 10
00532 
00533 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00534 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00535 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00536 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00537 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00538 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00539 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00540 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00541 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00542 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00543 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00544 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00545 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00546 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00547 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00548 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00549 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00550 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00551 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00552 #define ERROR_LOCK_PATH  -100
00553 #define OPERATOR_EXIT     300
00554 
00555 
00556 enum vm_box {
00557    NEW_FOLDER,
00558    OLD_FOLDER,
00559    WORK_FOLDER,
00560    FAMILY_FOLDER,
00561    FRIENDS_FOLDER,
00562    GREETINGS_FOLDER
00563 };
00564 
00565 enum vm_option_flags {
00566    OPT_SILENT =           (1 << 0),
00567    OPT_BUSY_GREETING =    (1 << 1),
00568    OPT_UNAVAIL_GREETING = (1 << 2),
00569    OPT_RECORDGAIN =       (1 << 3),
00570    OPT_PREPEND_MAILBOX =  (1 << 4),
00571    OPT_AUTOPLAY =         (1 << 6),
00572    OPT_DTMFEXIT =         (1 << 7),
00573    OPT_MESSAGE_Urgent =   (1 << 8),
00574    OPT_MESSAGE_PRIORITY = (1 << 9)
00575 };
00576 
00577 enum vm_option_args {
00578    OPT_ARG_RECORDGAIN = 0,
00579    OPT_ARG_PLAYFOLDER = 1,
00580    OPT_ARG_DTMFEXIT   = 2,
00581    /* This *must* be the last value in this enum! */
00582    OPT_ARG_ARRAY_SIZE = 3,
00583 };
00584 
00585 enum vm_passwordlocation {
00586    OPT_PWLOC_VOICEMAILCONF = 0,
00587    OPT_PWLOC_SPOOLDIR      = 1,
00588    OPT_PWLOC_USERSCONF     = 2,
00589 };
00590 
00591 AST_APP_OPTIONS(vm_app_options, {
00592    AST_APP_OPTION('s', OPT_SILENT),
00593    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00594    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00595    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00596    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00597    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00598    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00599    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00600    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00601 });
00602 
00603 static int load_config(int reload);
00604 #ifdef TEST_FRAMEWORK
00605 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00606 #endif
00607 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00608 
00609 /*! \page vmlang Voicemail Language Syntaxes Supported
00610 
00611    \par Syntaxes supported, not really language codes.
00612    \arg \b en    - English
00613    \arg \b de    - German
00614    \arg \b es    - Spanish
00615    \arg \b fr    - French
00616    \arg \b it    - Italian
00617    \arg \b nl    - Dutch
00618    \arg \b pt    - Portuguese
00619    \arg \b pt_BR - Portuguese (Brazil)
00620    \arg \b gr    - Greek
00621    \arg \b no    - Norwegian
00622    \arg \b se    - Swedish
00623    \arg \b tw    - Chinese (Taiwan)
00624    \arg \b ua - Ukrainian
00625 
00626 German requires the following additional soundfile:
00627 \arg \b 1F  einE (feminine)
00628 
00629 Spanish requires the following additional soundfile:
00630 \arg \b 1M      un (masculine)
00631 
00632 Dutch, Portuguese & Spanish require the following additional soundfiles:
00633 \arg \b vm-INBOXs singular of 'new'
00634 \arg \b vm-Olds      singular of 'old/heard/read'
00635 
00636 NB these are plural:
00637 \arg \b vm-INBOX  nieuwe (nl)
00638 \arg \b vm-Old    oude (nl)
00639 
00640 Polish uses:
00641 \arg \b vm-new-a  'new', feminine singular accusative
00642 \arg \b vm-new-e  'new', feminine plural accusative
00643 \arg \b vm-new-ych   'new', feminine plural genitive
00644 \arg \b vm-old-a  'old', feminine singular accusative
00645 \arg \b vm-old-e  'old', feminine plural accusative
00646 \arg \b vm-old-ych   'old', feminine plural genitive
00647 \arg \b digits/1-a   'one', not always same as 'digits/1'
00648 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00649 
00650 Swedish uses:
00651 \arg \b vm-nytt      singular of 'new'
00652 \arg \b vm-nya    plural of 'new'
00653 \arg \b vm-gammalt   singular of 'old'
00654 \arg \b vm-gamla  plural of 'old'
00655 \arg \b digits/ett   'one', not always same as 'digits/1'
00656 
00657 Norwegian uses:
00658 \arg \b vm-ny     singular of 'new'
00659 \arg \b vm-nye    plural of 'new'
00660 \arg \b vm-gammel singular of 'old'
00661 \arg \b vm-gamle  plural of 'old'
00662 
00663 Dutch also uses:
00664 \arg \b nl-om     'at'?
00665 
00666 Spanish also uses:
00667 \arg \b vm-youhaveno
00668 
00669 Italian requires the following additional soundfile:
00670 
00671 For vm_intro_it:
00672 \arg \b vm-nuovo  new
00673 \arg \b vm-nuovi  new plural
00674 \arg \b vm-vecchio   old
00675 \arg \b vm-vecchi old plural
00676 
00677 Chinese (Taiwan) requires the following additional soundfile:
00678 \arg \b vm-tong      A class-word for call (tong1)
00679 \arg \b vm-ri     A class-word for day (ri4)
00680 \arg \b vm-you    You (ni3)
00681 \arg \b vm-haveno   Have no (mei2 you3)
00682 \arg \b vm-have     Have (you3)
00683 \arg \b vm-listen   To listen (yao4 ting1)
00684 
00685 
00686 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00687 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00688 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00689 
00690 */
00691 
00692 struct baseio {
00693    int iocp;
00694    int iolen;
00695    int linelength;
00696    int ateof;
00697    unsigned char iobuf[BASEMAXINLINE];
00698 };
00699 
00700 /*! Structure for linked list of users 
00701  * Use ast_vm_user_destroy() to free one of these structures. */
00702 struct ast_vm_user {
00703    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00704    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00705    char password[80];               /*!< Secret pin code, numbers only */
00706    char fullname[80];               /*!< Full name, for directory app */
00707    char email[80];                  /*!< E-mail address */
00708    char *emailsubject;              /*!< E-mail subject */
00709    char *emailbody;                 /*!< E-mail body */
00710    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00711    char serveremail[80];            /*!< From: Mail address */
00712    char mailcmd[160];               /*!< Configurable mail command */
00713    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00714    char zonetag[80];                /*!< Time zone */
00715    char locale[20];                 /*!< The locale (for presentation of date/time) */
00716    char callback[80];
00717    char dialout[80];
00718    char uniqueid[80];               /*!< Unique integer identifier */
00719    char exit[80];
00720    char attachfmt[20];              /*!< Attachment format */
00721    unsigned int flags;              /*!< VM_ flags */ 
00722    int saydurationm;
00723    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00724    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00725    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00726    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00727    int passwordlocation;            /*!< Storage location of the password */
00728 #ifdef IMAP_STORAGE
00729    char imapserver[48];             /*!< IMAP server address */
00730    char imapport[8];                /*!< IMAP server port */
00731    char imapflags[128];             /*!< IMAP optional flags */
00732    char imapuser[80];               /*!< IMAP server login */
00733    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00734    char imapfolder[64];             /*!< IMAP voicemail folder */
00735    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00736    int imapversion;                 /*!< If configuration changes, use the new values */
00737 #endif
00738    double volgain;                  /*!< Volume gain for voicemails sent via email */
00739    AST_LIST_ENTRY(ast_vm_user) list;
00740 };
00741 
00742 /*! Voicemail time zones */
00743 struct vm_zone {
00744    AST_LIST_ENTRY(vm_zone) list;
00745    char name[80];
00746    char timezone[80];
00747    char msg_format[512];
00748 };
00749 
00750 #define VMSTATE_MAX_MSG_ARRAY 256
00751 
00752 /*! Voicemail mailbox state */
00753 struct vm_state {
00754    char curbox[80];
00755    char username[80];
00756    char context[80];
00757    char curdir[PATH_MAX];
00758    char vmbox[PATH_MAX];
00759    char fn[PATH_MAX];
00760    char intro[PATH_MAX];
00761    int *deleted;
00762    int *heard;
00763    int dh_arraysize; /* used for deleted / heard allocation */
00764    int curmsg;
00765    int lastmsg;
00766    int newmessages;
00767    int oldmessages;
00768    int urgentmessages;
00769    int starting;
00770    int repeats;
00771 #ifdef IMAP_STORAGE
00772    ast_mutex_t lock;
00773    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00774    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00775    MAILSTREAM *mailstream;
00776    int vmArrayIndex;
00777    char imapuser[80];                   /*!< IMAP server login */
00778    char imapfolder[64];                 /*!< IMAP voicemail folder */
00779    char imapserver[48];                 /*!< IMAP server address */
00780    char imapport[8];                    /*!< IMAP server port */
00781    char imapflags[128];                 /*!< IMAP optional flags */
00782    int imapversion;
00783    int interactive;
00784    char introfn[PATH_MAX];              /*!< Name of prepended file */
00785    unsigned int quota_limit;
00786    unsigned int quota_usage;
00787    struct vm_state *persist_vms;
00788 #endif
00789 };
00790 
00791 #ifdef ODBC_STORAGE
00792 static char odbc_database[80];
00793 static char odbc_table[80];
00794 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00795 #define DISPOSE(a,b) remove_file(a,b)
00796 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00797 #define EXISTS(a,b,c,d) (message_exists(a,b))
00798 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00799 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00800 #define DELETE(a,b,c,d) (delete_file(a,b))
00801 #else
00802 #ifdef IMAP_STORAGE
00803 #define DISPOSE(a,b) (imap_remove_file(a,b))
00804 #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))
00805 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00806 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00807 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00808 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00809 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00810 #else
00811 #define RETRIEVE(a,b,c,d)
00812 #define DISPOSE(a,b)
00813 #define STORE(a,b,c,d,e,f,g,h,i,j)
00814 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00815 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00816 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00817 #define DELETE(a,b,c,d) (vm_delete(c))
00818 #endif
00819 #endif
00820 
00821 static char VM_SPOOL_DIR[PATH_MAX];
00822 
00823 static char ext_pass_cmd[128];
00824 static char ext_pass_check_cmd[128];
00825 
00826 static int my_umask;
00827 
00828 #define PWDCHANGE_INTERNAL (1 << 1)
00829 #define PWDCHANGE_EXTERNAL (1 << 2)
00830 static int pwdchange = PWDCHANGE_INTERNAL;
00831 
00832 #ifdef ODBC_STORAGE
00833 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00834 #else
00835 # ifdef IMAP_STORAGE
00836 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00837 # else
00838 # define tdesc "Comedian Mail (Voicemail System)"
00839 # endif
00840 #endif
00841 
00842 static char userscontext[AST_MAX_EXTENSION] = "default";
00843 
00844 static char *addesc = "Comedian Mail";
00845 
00846 /* Leave a message */
00847 static char *app = "VoiceMail";
00848 
00849 /* Check mail, control, etc */
00850 static char *app2 = "VoiceMailMain";
00851 
00852 static char *app3 = "MailboxExists";
00853 static char *app4 = "VMAuthenticate";
00854 
00855 static char *sayname_app = "VMSayName";
00856 
00857 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00858 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00859 static char zonetag[80];
00860 static char locale[20];
00861 static int maxsilence;
00862 static int maxmsg;
00863 static int maxdeletedmsg;
00864 static int silencethreshold = 128;
00865 static char serveremail[80];
00866 static char mailcmd[160];  /* Configurable mail cmd */
00867 static char externnotify[160]; 
00868 static struct ast_smdi_interface *smdi_iface = NULL;
00869 static char vmfmts[80];
00870 static double volgain;
00871 static int vmminsecs;
00872 static int vmmaxsecs;
00873 static int maxgreet;
00874 static int skipms;
00875 static int maxlogins;
00876 static int minpassword;
00877 static int passwordlocation;
00878 
00879 /*! Poll mailboxes for changes since there is something external to
00880  *  app_voicemail that may change them. */
00881 static unsigned int poll_mailboxes;
00882 
00883 /*! Polling frequency */
00884 static unsigned int poll_freq;
00885 /*! By default, poll every 30 seconds */
00886 #define DEFAULT_POLL_FREQ 30
00887 
00888 AST_MUTEX_DEFINE_STATIC(poll_lock);
00889 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00890 static pthread_t poll_thread = AST_PTHREADT_NULL;
00891 static unsigned char poll_thread_run;
00892 
00893 /*! Subscription to ... MWI event subscriptions */
00894 static struct ast_event_sub *mwi_sub_sub;
00895 /*! Subscription to ... MWI event un-subscriptions */
00896 static struct ast_event_sub *mwi_unsub_sub;
00897 
00898 /*!
00899  * \brief An MWI subscription
00900  *
00901  * This is so we can keep track of which mailboxes are subscribed to.
00902  * This way, we know which mailboxes to poll when the pollmailboxes
00903  * option is being used.
00904  */
00905 struct mwi_sub {
00906    AST_RWLIST_ENTRY(mwi_sub) entry;
00907    int old_urgent;
00908    int old_new;
00909    int old_old;
00910    uint32_t uniqueid;
00911    char mailbox[1];
00912 };
00913 
00914 struct mwi_sub_task {
00915    const char *mailbox;
00916    const char *context;
00917    uint32_t uniqueid;
00918 };
00919 
00920 static struct ast_taskprocessor *mwi_subscription_tps;
00921 
00922 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00923 
00924 /* custom audio control prompts for voicemail playback */
00925 static char listen_control_forward_key[12];
00926 static char listen_control_reverse_key[12];
00927 static char listen_control_pause_key[12];
00928 static char listen_control_restart_key[12];
00929 static char listen_control_stop_key[12];
00930 
00931 /* custom password sounds */
00932 static char vm_password[80] = "vm-password";
00933 static char vm_newpassword[80] = "vm-newpassword";
00934 static char vm_passchanged[80] = "vm-passchanged";
00935 static char vm_reenterpassword[80] = "vm-reenterpassword";
00936 static char vm_mismatch[80] = "vm-mismatch";
00937 static char vm_invalid_password[80] = "vm-invalid-password";
00938 static char vm_pls_try_again[80] = "vm-pls-try-again";
00939 
00940 /*
00941  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00942  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00943  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00944  * app.c's __ast_play_and_record function
00945  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00946  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00947  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00948  * more effort than either of the other two.
00949  */
00950 static char vm_prepend_timeout[80] = "vm-then-pound";
00951 
00952 static struct ast_flags globalflags = {0};
00953 
00954 static int saydurationminfo;
00955 
00956 static char dialcontext[AST_MAX_CONTEXT] = "";
00957 static char callcontext[AST_MAX_CONTEXT] = "";
00958 static char exitcontext[AST_MAX_CONTEXT] = "";
00959 
00960 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00961 
00962 
00963 static char *emailbody = NULL;
00964 static char *emailsubject = NULL;
00965 static char *pagerbody = NULL;
00966 static char *pagersubject = NULL;
00967 static char fromstring[100];
00968 static char pagerfromstring[100];
00969 static char charset[32] = "ISO-8859-1";
00970 
00971 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00972 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00973 static int adsiver = 1;
00974 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00975 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00976 
00977 /* Forward declarations - generic */
00978 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00979 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);
00980 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00981 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00982          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00983          signed char record_gain, struct vm_state *vms, char *flag);
00984 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00985 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00986 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);
00987 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);
00988 static void apply_options(struct ast_vm_user *vmu, const char *options);
00989 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);
00990 static int is_valid_dtmf(const char *key);
00991 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00992 static int write_password_to_file(const char *secretfn, const char *password);
00993 static const char *substitute_escapes(const char *value);
00994 
00995 struct ao2_container *inprocess_container;
00996 
00997 struct inprocess {
00998    int count;
00999    char *context;
01000    char mailbox[0];
01001 };
01002 
01003 static int inprocess_hash_fn(const void *obj, const int flags)
01004 {
01005    const struct inprocess *i = obj;
01006    return atoi(i->mailbox);
01007 }
01008 
01009 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
01010 {
01011    struct inprocess *i = obj, *j = arg;
01012    if (strcmp(i->mailbox, j->mailbox)) {
01013       return 0;
01014    }
01015    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
01016 }
01017 
01018 static int inprocess_count(const char *context, const char *mailbox, int delta)
01019 {
01020    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
01021    arg->context = arg->mailbox + strlen(mailbox) + 1;
01022    strcpy(arg->mailbox, mailbox); /* SAFE */
01023    strcpy(arg->context, context); /* SAFE */
01024    ao2_lock(inprocess_container);
01025    if ((i = ao2_find(inprocess_container, arg, 0))) {
01026       int ret = ast_atomic_fetchadd_int(&i->count, delta);
01027       ao2_unlock(inprocess_container);
01028       ao2_ref(i, -1);
01029       return ret;
01030    }
01031    if (delta < 0) {
01032       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
01033    }
01034    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
01035       ao2_unlock(inprocess_container);
01036       return 0;
01037    }
01038    i->context = i->mailbox + strlen(mailbox) + 1;
01039    strcpy(i->mailbox, mailbox); /* SAFE */
01040    strcpy(i->context, context); /* SAFE */
01041    i->count = delta;
01042    ao2_link(inprocess_container, i);
01043    ao2_unlock(inprocess_container);
01044    ao2_ref(i, -1);
01045    return 0;
01046 }
01047 
01048 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
01049 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
01050 #endif
01051 
01052 /*!
01053  * \brief Strips control and non 7-bit clean characters from input string.
01054  *
01055  * \note To map control and none 7-bit characters to a 7-bit clean characters
01056  *  please use ast_str_encode_mine().
01057  */
01058 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
01059 {
01060    char *bufptr = buf;
01061    for (; *input; input++) {
01062       if (*input < 32) {
01063          continue;
01064       }
01065       *bufptr++ = *input;
01066       if (bufptr == buf + buflen - 1) {
01067          break;
01068       }
01069    }
01070    *bufptr = '\0';
01071    return buf;
01072 }
01073 
01074 
01075 /*!
01076  * \brief Sets default voicemail system options to a voicemail user.
01077  *
01078  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01079  * - all the globalflags
01080  * - the saydurationminfo
01081  * - the callcontext
01082  * - the dialcontext
01083  * - the exitcontext
01084  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01085  * - volume gain.
01086  * - emailsubject, emailbody set to NULL
01087  */
01088 static void populate_defaults(struct ast_vm_user *vmu)
01089 {
01090    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01091    vmu->passwordlocation = passwordlocation;
01092    if (saydurationminfo) {
01093       vmu->saydurationm = saydurationminfo;
01094    }
01095    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01096    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01097    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01098    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01099    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01100    if (vmminsecs) {
01101       vmu->minsecs = vmminsecs;
01102    }
01103    if (vmmaxsecs) {
01104       vmu->maxsecs = vmmaxsecs;
01105    }
01106    if (maxmsg) {
01107       vmu->maxmsg = maxmsg;
01108    }
01109    if (maxdeletedmsg) {
01110       vmu->maxdeletedmsg = maxdeletedmsg;
01111    }
01112    vmu->volgain = volgain;
01113    ast_free(vmu->emailsubject);
01114    vmu->emailsubject = NULL;
01115    ast_free(vmu->emailbody);
01116    vmu->emailbody = NULL;
01117 #ifdef IMAP_STORAGE
01118    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01119    ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
01120    ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
01121    ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
01122 #endif
01123 }
01124 
01125 /*!
01126  * \brief Sets a a specific property value.
01127  * \param vmu The voicemail user object to work with.
01128  * \param var The name of the property to be set.
01129  * \param value The value to be set to the property.
01130  * 
01131  * The property name must be one of the understood properties. See the source for details.
01132  */
01133 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01134 {
01135    int x;
01136    if (!strcasecmp(var, "attach")) {
01137       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01138    } else if (!strcasecmp(var, "attachfmt")) {
01139       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01140    } else if (!strcasecmp(var, "serveremail")) {
01141       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01142    } else if (!strcasecmp(var, "emailbody")) {
01143       ast_free(vmu->emailbody);
01144       vmu->emailbody = ast_strdup(substitute_escapes(value));
01145    } else if (!strcasecmp(var, "emailsubject")) {
01146       ast_free(vmu->emailsubject);
01147       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01148    } else if (!strcasecmp(var, "language")) {
01149       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01150    } else if (!strcasecmp(var, "tz")) {
01151       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01152    } else if (!strcasecmp(var, "locale")) {
01153       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01154 #ifdef IMAP_STORAGE
01155    } else if (!strcasecmp(var, "imapuser")) {
01156       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01157       vmu->imapversion = imapversion;
01158    } else if (!strcasecmp(var, "imapserver")) {
01159       ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
01160       vmu->imapversion = imapversion;
01161    } else if (!strcasecmp(var, "imapport")) {
01162       ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
01163       vmu->imapversion = imapversion;
01164    } else if (!strcasecmp(var, "imapflags")) {
01165       ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
01166       vmu->imapversion = imapversion;
01167    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01168       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01169       vmu->imapversion = imapversion;
01170    } else if (!strcasecmp(var, "imapfolder")) {
01171       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01172       vmu->imapversion = imapversion;
01173    } else if (!strcasecmp(var, "imapvmshareid")) {
01174       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01175       vmu->imapversion = imapversion;
01176 #endif
01177    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01178       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01179    } else if (!strcasecmp(var, "saycid")){
01180       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01181    } else if (!strcasecmp(var, "sendvoicemail")){
01182       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01183    } else if (!strcasecmp(var, "review")){
01184       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01185    } else if (!strcasecmp(var, "tempgreetwarn")){
01186       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01187    } else if (!strcasecmp(var, "messagewrap")){
01188       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01189    } else if (!strcasecmp(var, "operator")) {
01190       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01191    } else if (!strcasecmp(var, "envelope")){
01192       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01193    } else if (!strcasecmp(var, "moveheard")){
01194       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01195    } else if (!strcasecmp(var, "sayduration")){
01196       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01197    } else if (!strcasecmp(var, "saydurationm")){
01198       if (sscanf(value, "%30d", &x) == 1) {
01199          vmu->saydurationm = x;
01200       } else {
01201          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01202       }
01203    } else if (!strcasecmp(var, "forcename")){
01204       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01205    } else if (!strcasecmp(var, "forcegreetings")){
01206       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01207    } else if (!strcasecmp(var, "callback")) {
01208       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01209    } else if (!strcasecmp(var, "dialout")) {
01210       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01211    } else if (!strcasecmp(var, "exitcontext")) {
01212       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01213    } else if (!strcasecmp(var, "minsecs")) {
01214       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01215          vmu->minsecs = x;
01216       } else {
01217          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01218          vmu->minsecs = vmminsecs;
01219       }
01220    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01221       vmu->maxsecs = atoi(value);
01222       if (vmu->maxsecs <= 0) {
01223          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01224          vmu->maxsecs = vmmaxsecs;
01225       } else {
01226          vmu->maxsecs = atoi(value);
01227       }
01228       if (!strcasecmp(var, "maxmessage"))
01229          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01230    } else if (!strcasecmp(var, "maxmsg")) {
01231       vmu->maxmsg = atoi(value);
01232       /* Accept maxmsg=0 (Greetings only voicemail) */
01233       if (vmu->maxmsg < 0) {
01234          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01235          vmu->maxmsg = MAXMSG;
01236       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01237          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01238          vmu->maxmsg = MAXMSGLIMIT;
01239       }
01240    } else if (!strcasecmp(var, "nextaftercmd")) {
01241       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01242    } else if (!strcasecmp(var, "backupdeleted")) {
01243       if (sscanf(value, "%30d", &x) == 1)
01244          vmu->maxdeletedmsg = x;
01245       else if (ast_true(value))
01246          vmu->maxdeletedmsg = MAXMSG;
01247       else
01248          vmu->maxdeletedmsg = 0;
01249 
01250       if (vmu->maxdeletedmsg < 0) {
01251          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01252          vmu->maxdeletedmsg = MAXMSG;
01253       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01254          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01255          vmu->maxdeletedmsg = MAXMSGLIMIT;
01256       }
01257    } else if (!strcasecmp(var, "volgain")) {
01258       sscanf(value, "%30lf", &vmu->volgain);
01259    } else if (!strcasecmp(var, "passwordlocation")) {
01260       if (!strcasecmp(value, "spooldir")) {
01261          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01262       } else {
01263          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01264       }
01265    } else if (!strcasecmp(var, "options")) {
01266       apply_options(vmu, value);
01267    }
01268 }
01269 
01270 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01271 {
01272    int fds[2], pid = 0;
01273 
01274    memset(buf, 0, len);
01275 
01276    if (pipe(fds)) {
01277       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01278    } else {
01279       /* good to go*/
01280       pid = ast_safe_fork(0);
01281 
01282       if (pid < 0) {
01283          /* ok maybe not */
01284          close(fds[0]);
01285          close(fds[1]);
01286          snprintf(buf, len, "FAILURE: Fork failed");
01287       } else if (pid) {
01288          /* parent */
01289          close(fds[1]);
01290          if (read(fds[0], buf, len) < 0) {
01291             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01292          }
01293          close(fds[0]);
01294       } else {
01295          /*  child */
01296          AST_DECLARE_APP_ARGS(arg,
01297             AST_APP_ARG(v)[20];
01298          );
01299          char *mycmd = ast_strdupa(command);
01300 
01301          close(fds[0]);
01302          dup2(fds[1], STDOUT_FILENO);
01303          close(fds[1]);
01304          ast_close_fds_above_n(STDOUT_FILENO);
01305 
01306          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01307 
01308          execv(arg.v[0], arg.v); 
01309          printf("FAILURE: %s", strerror(errno));
01310          _exit(0);
01311       }
01312    }
01313    return buf;
01314 }
01315 
01316 /*!
01317  * \brief Check that password meets minimum required length
01318  * \param vmu The voicemail user to change the password for.
01319  * \param password The password string to check
01320  *
01321  * \return zero on ok, 1 on not ok.
01322  */
01323 static int check_password(struct ast_vm_user *vmu, char *password)
01324 {
01325    /* check minimum length */
01326    if (strlen(password) < minpassword)
01327       return 1;
01328    /* check that password does not contain '*' character */
01329    if (!ast_strlen_zero(password) && password[0] == '*')
01330       return 1;
01331    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01332       char cmd[255], buf[255];
01333 
01334       ast_debug(1, "Verify password policies for %s\n", password);
01335 
01336       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01337       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01338          ast_debug(5, "Result: %s\n", buf);
01339          if (!strncasecmp(buf, "VALID", 5)) {
01340             ast_debug(3, "Passed password check: '%s'\n", buf);
01341             return 0;
01342          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01343             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01344             return 0;
01345          } else {
01346             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01347             return 1;
01348          }
01349       }
01350    }
01351    return 0;
01352 }
01353 
01354 /*! 
01355  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01356  * \param vmu The voicemail user to change the password for.
01357  * \param password The new value to be set to the password for this user.
01358  * 
01359  * This only works if there is a realtime engine configured.
01360  * This is called from the (top level) vm_change_password.
01361  *
01362  * \return zero on success, -1 on error.
01363  */
01364 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01365 {
01366    int res = -1;
01367    if (!strcmp(vmu->password, password)) {
01368       /* No change (but an update would return 0 rows updated, so we opt out here) */
01369       return 0;
01370    }
01371 
01372    if (strlen(password) > 10) {
01373       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01374    }
01375    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01376       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01377       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01378       res = 0;
01379    }
01380    return res;
01381 }
01382 
01383 /*!
01384  * \brief Destructively Parse options and apply.
01385  */
01386 static void apply_options(struct ast_vm_user *vmu, const char *options)
01387 {  
01388    char *stringp;
01389    char *s;
01390    char *var, *value;
01391    stringp = ast_strdupa(options);
01392    while ((s = strsep(&stringp, "|"))) {
01393       value = s;
01394       if ((var = strsep(&value, "=")) && value) {
01395          apply_option(vmu, var, value);
01396       }
01397    }  
01398 }
01399 
01400 /*!
01401  * \brief Loads the options specific to a voicemail user.
01402  * 
01403  * This is called when a vm_user structure is being set up, such as from load_options.
01404  */
01405 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01406 {
01407    for (; var; var = var->next) {
01408       if (!strcasecmp(var->name, "vmsecret")) {
01409          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01410       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01411          if (ast_strlen_zero(retval->password)) {
01412             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01413                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01414                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01415             } else {
01416                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01417             }
01418          }
01419       } else if (!strcasecmp(var->name, "uniqueid")) {
01420          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01421       } else if (!strcasecmp(var->name, "pager")) {
01422          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01423       } else if (!strcasecmp(var->name, "email")) {
01424          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01425       } else if (!strcasecmp(var->name, "fullname")) {
01426          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01427       } else if (!strcasecmp(var->name, "context")) {
01428          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01429       } else if (!strcasecmp(var->name, "emailsubject")) {
01430          ast_free(retval->emailsubject);
01431          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01432       } else if (!strcasecmp(var->name, "emailbody")) {
01433          ast_free(retval->emailbody);
01434          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01435 #ifdef IMAP_STORAGE
01436       } else if (!strcasecmp(var->name, "imapuser")) {
01437          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01438          retval->imapversion = imapversion;
01439       } else if (!strcasecmp(var->name, "imapserver")) {
01440          ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
01441          retval->imapversion = imapversion;
01442       } else if (!strcasecmp(var->name, "imapport")) {
01443          ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
01444          retval->imapversion = imapversion;
01445       } else if (!strcasecmp(var->name, "imapflags")) {
01446          ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
01447          retval->imapversion = imapversion;
01448       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01449          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01450          retval->imapversion = imapversion;
01451       } else if (!strcasecmp(var->name, "imapfolder")) {
01452          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01453          retval->imapversion = imapversion;
01454       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01455          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01456          retval->imapversion = imapversion;
01457 #endif
01458       } else
01459          apply_option(retval, var->name, var->value);
01460    }
01461 }
01462 
01463 /*!
01464  * \brief Determines if a DTMF key entered is valid.
01465  * \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.
01466  *
01467  * Tests the character entered against the set of valid DTMF characters. 
01468  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01469  */
01470 static int is_valid_dtmf(const char *key)
01471 {
01472    int i;
01473    char *local_key = ast_strdupa(key);
01474 
01475    for (i = 0; i < strlen(key); ++i) {
01476       if (!strchr(VALID_DTMF, *local_key)) {
01477          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01478          return 0;
01479       }
01480       local_key++;
01481    }
01482    return 1;
01483 }
01484 
01485 /*!
01486  * \brief Finds a voicemail user from the realtime engine.
01487  * \param ivm
01488  * \param context
01489  * \param mailbox
01490  *
01491  * 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.
01492  *
01493  * \return The ast_vm_user structure for the user that was found.
01494  */
01495 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01496 {
01497    struct ast_variable *var;
01498    struct ast_vm_user *retval;
01499 
01500    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01501       if (!ivm)
01502          ast_set_flag(retval, VM_ALLOCED);   
01503       else
01504          memset(retval, 0, sizeof(*retval));
01505       if (mailbox) 
01506          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01507       populate_defaults(retval);
01508       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01509          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01510       else
01511          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01512       if (var) {
01513          apply_options_full(retval, var);
01514          ast_variables_destroy(var);
01515       } else { 
01516          if (!ivm) 
01517             ast_free(retval);
01518          retval = NULL;
01519       }  
01520    } 
01521    return retval;
01522 }
01523 
01524 /*!
01525  * \brief Finds a voicemail user from the users file or the realtime engine.
01526  * \param ivm
01527  * \param context
01528  * \param mailbox
01529  * 
01530  * \return The ast_vm_user structure for the user that was found.
01531  */
01532 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01533 {
01534    /* This function could be made to generate one from a database, too */
01535    struct ast_vm_user *vmu = NULL, *cur;
01536    AST_LIST_LOCK(&users);
01537 
01538    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01539       context = "default";
01540 
01541    AST_LIST_TRAVERSE(&users, cur, list) {
01542 #ifdef IMAP_STORAGE
01543       if (cur->imapversion != imapversion) {
01544          continue;
01545       }
01546 #endif
01547       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01548          break;
01549       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01550          break;
01551    }
01552    if (cur) {
01553       /* Make a copy, so that on a reload, we have no race */
01554       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01555          *vmu = *cur;
01556          if (!ivm) {
01557             vmu->emailbody = ast_strdup(cur->emailbody);
01558             vmu->emailsubject = ast_strdup(cur->emailsubject);
01559          }
01560          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01561          AST_LIST_NEXT(vmu, list) = NULL;
01562       }
01563    } else
01564       vmu = find_user_realtime(ivm, context, mailbox);
01565    AST_LIST_UNLOCK(&users);
01566    return vmu;
01567 }
01568 
01569 /*!
01570  * \brief Resets a user password to a specified password.
01571  * \param context
01572  * \param mailbox
01573  * \param newpass
01574  *
01575  * This does the actual change password work, called by the vm_change_password() function.
01576  *
01577  * \return zero on success, -1 on error.
01578  */
01579 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01580 {
01581    /* This function could be made to generate one from a database, too */
01582    struct ast_vm_user *cur;
01583    int res = -1;
01584    AST_LIST_LOCK(&users);
01585    AST_LIST_TRAVERSE(&users, cur, list) {
01586       if ((!context || !strcasecmp(context, cur->context)) &&
01587          (!strcasecmp(mailbox, cur->mailbox)))
01588             break;
01589    }
01590    if (cur) {
01591       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01592       res = 0;
01593    }
01594    AST_LIST_UNLOCK(&users);
01595    return res;
01596 }
01597 
01598 /*! 
01599  * \brief The handler for the change password option.
01600  * \param vmu The voicemail user to work with.
01601  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01602  * 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.
01603  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01604  */
01605 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01606 {
01607    struct ast_config   *cfg = NULL;
01608    struct ast_variable *var = NULL;
01609    struct ast_category *cat = NULL;
01610    char *category = NULL, *value = NULL, *new = NULL;
01611    const char *tmp = NULL;
01612    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01613    char secretfn[PATH_MAX] = "";
01614    int found = 0;
01615 
01616    if (!change_password_realtime(vmu, newpassword))
01617       return;
01618 
01619    /* check if we should store the secret in the spool directory next to the messages */
01620    switch (vmu->passwordlocation) {
01621    case OPT_PWLOC_SPOOLDIR:
01622       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01623       if (write_password_to_file(secretfn, newpassword) == 0) {
01624          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01625          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01626          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01627          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01628          break;
01629       } else {
01630          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01631       }
01632       /* Fall-through */
01633    case OPT_PWLOC_VOICEMAILCONF:
01634       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01635          while ((category = ast_category_browse(cfg, category))) {
01636             if (!strcasecmp(category, vmu->context)) {
01637                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01638                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01639                   break;
01640                }
01641                value = strstr(tmp, ",");
01642                if (!value) {
01643                   new = alloca(strlen(newpassword)+1);
01644                   sprintf(new, "%s", newpassword);
01645                } else {
01646                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01647                   sprintf(new, "%s%s", newpassword, value);
01648                }
01649                if (!(cat = ast_category_get(cfg, category))) {
01650                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01651                   break;
01652                }
01653                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01654                found = 1;
01655             }
01656          }
01657          /* save the results */
01658          if (found) {
01659             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01660             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01661             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01662             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01663             break;
01664          }
01665       }
01666       /* Fall-through */
01667    case OPT_PWLOC_USERSCONF:
01668       /* check users.conf and update the password stored for the mailbox */
01669       /* if no vmsecret entry exists create one. */
01670       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01671          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01672          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01673             ast_debug(4, "users.conf: %s\n", category);
01674             if (!strcasecmp(category, vmu->mailbox)) {
01675                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01676                   ast_debug(3, "looks like we need to make vmsecret!\n");
01677                   var = ast_variable_new("vmsecret", newpassword, "");
01678                } else {
01679                   var = NULL;
01680                }
01681                new = alloca(strlen(newpassword) + 1);
01682                sprintf(new, "%s", newpassword);
01683                if (!(cat = ast_category_get(cfg, category))) {
01684                   ast_debug(4, "failed to get category!\n");
01685                   ast_free(var);
01686                   break;
01687                }
01688                if (!var) {
01689                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01690                } else {
01691                   ast_variable_append(cat, var);
01692                }
01693                found = 1;
01694                break;
01695             }
01696          }
01697          /* save the results and clean things up */
01698          if (found) {
01699             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01700             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01701             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01702             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01703          }
01704       }
01705    }
01706 }
01707 
01708 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01709 {
01710    char buf[255];
01711    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01712    ast_debug(1, "External password: %s\n",buf);
01713    if (!ast_safe_system(buf)) {
01714       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01715       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01716       /* Reset the password in memory, too */
01717       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01718    }
01719 }
01720 
01721 /*! 
01722  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01723  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01724  * \param len The length of the path string that was written out.
01725  * \param context
01726  * \param ext 
01727  * \param folder 
01728  * 
01729  * The path is constructed as 
01730  *    VM_SPOOL_DIRcontext/ext/folder
01731  *
01732  * \return zero on success, -1 on error.
01733  */
01734 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01735 {
01736    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01737 }
01738 
01739 /*! 
01740  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01741  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01742  * \param len The length of the path string that was written out.
01743  * \param dir 
01744  * \param num 
01745  * 
01746  * The path is constructed as 
01747  *    VM_SPOOL_DIRcontext/ext/folder
01748  *
01749  * \return zero on success, -1 on error.
01750  */
01751 static int make_file(char *dest, const int len, const char *dir, const int num)
01752 {
01753    return snprintf(dest, len, "%s/msg%04d", dir, num);
01754 }
01755 
01756 /* same as mkstemp, but return a FILE * */
01757 static FILE *vm_mkftemp(char *template)
01758 {
01759    FILE *p = NULL;
01760    int pfd = mkstemp(template);
01761    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01762    if (pfd > -1) {
01763       p = fdopen(pfd, "w+");
01764       if (!p) {
01765          close(pfd);
01766          pfd = -1;
01767       }
01768    }
01769    return p;
01770 }
01771 
01772 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01773  * \param dest    String. base directory.
01774  * \param len     Length of dest.
01775  * \param context String. Ignored if is null or empty string.
01776  * \param ext     String. Ignored if is null or empty string.
01777  * \param folder  String. Ignored if is null or empty string. 
01778  * \return -1 on failure, 0 on success.
01779  */
01780 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01781 {
01782    mode_t   mode = VOICEMAIL_DIR_MODE;
01783    int res;
01784 
01785    make_dir(dest, len, context, ext, folder);
01786    if ((res = ast_mkdir(dest, mode))) {
01787       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01788       return -1;
01789    }
01790    return 0;
01791 }
01792 
01793 static const char * const mailbox_folders[] = {
01794 #ifdef IMAP_STORAGE
01795    imapfolder,
01796 #else
01797    "INBOX",
01798 #endif
01799    "Old",
01800    "Work",
01801    "Family",
01802    "Friends",
01803    "Cust1",
01804    "Cust2",
01805    "Cust3",
01806    "Cust4",
01807    "Cust5",
01808    "Deleted",
01809    "Urgent",
01810 };
01811 
01812 static const char *mbox(struct ast_vm_user *vmu, int id)
01813 {
01814 #ifdef IMAP_STORAGE
01815    if (vmu && id == 0) {
01816       return vmu->imapfolder;
01817    }
01818 #endif
01819    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01820 }
01821 
01822 static int get_folder_by_name(const char *name)
01823 {
01824    size_t i;
01825 
01826    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01827       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01828          return i;
01829       }
01830    }
01831 
01832    return -1;
01833 }
01834 
01835 static void free_user(struct ast_vm_user *vmu)
01836 {
01837    if (ast_test_flag(vmu, VM_ALLOCED)) {
01838 
01839       ast_free(vmu->emailbody);
01840       vmu->emailbody = NULL;
01841 
01842       ast_free(vmu->emailsubject);
01843       vmu->emailsubject = NULL;
01844 
01845       ast_free(vmu);
01846    }
01847 }
01848 
01849 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01850 
01851    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01852    if (!vms->dh_arraysize) {
01853       /* initial allocation */
01854       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01855          return -1;
01856       }
01857       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01858          return -1;
01859       }
01860       vms->dh_arraysize = arraysize;
01861    } else if (vms->dh_arraysize < arraysize) {
01862       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01863          return -1;
01864       }
01865       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01866          return -1;
01867       }
01868       memset(vms->deleted, 0, arraysize * sizeof(int));
01869       memset(vms->heard, 0, arraysize * sizeof(int));
01870       vms->dh_arraysize = arraysize;
01871    }
01872 
01873    return 0;
01874 }
01875 
01876 /* All IMAP-specific functions should go in this block. This
01877  * keeps them from being spread out all over the code */
01878 #ifdef IMAP_STORAGE
01879 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01880 {
01881    char arg[10];
01882    struct vm_state *vms;
01883    unsigned long messageNum;
01884 
01885    /* If greetings aren't stored in IMAP, just delete the file */
01886    if (msgnum < 0 && !imapgreetings) {
01887       ast_filedelete(file, NULL);
01888       return;
01889    }
01890 
01891    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01892       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);
01893       return;
01894    }
01895 
01896    /* find real message number based on msgnum */
01897    /* this may be an index into vms->msgArray based on the msgnum. */
01898    messageNum = vms->msgArray[msgnum];
01899    if (messageNum == 0) {
01900       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01901       return;
01902    }
01903    ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01904    /* delete message */
01905    snprintf (arg, sizeof(arg), "%lu", messageNum);
01906    ast_mutex_lock(&vms->lock);
01907    mail_setflag (vms->mailstream, arg, "\\DELETED");
01908    mail_expunge(vms->mailstream);
01909    ast_mutex_unlock(&vms->lock);
01910 }
01911 
01912 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01913 {
01914    struct vm_state *vms_p;
01915    char *file, *filename;
01916    char *attachment;
01917    int i;
01918    BODY *body;
01919 
01920    /* This function is only used for retrieval of IMAP greetings
01921     * regular messages are not retrieved this way, nor are greetings
01922     * if they are stored locally*/
01923    if (msgnum > -1 || !imapgreetings) {
01924       return 0;
01925    } else {
01926       file = strrchr(ast_strdupa(dir), '/');
01927       if (file)
01928          *file++ = '\0';
01929       else {
01930          ast_debug(1, "Failed to procure file name from directory passed.\n");
01931          return -1;
01932       }
01933    }
01934 
01935    /* check if someone is accessing this box right now... */
01936    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01937       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01938       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01939       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01940       * that's all we need to do.
01941       */
01942       if (!(vms_p = create_vm_state_from_user(vmu))) {
01943          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01944          return -1;
01945       }
01946    }
01947 
01948    /* Greetings will never have a prepended message */
01949    *vms_p->introfn = '\0';
01950 
01951    ast_mutex_lock(&vms_p->lock);
01952    if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
01953       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
01954       ast_mutex_unlock(&vms_p->lock);
01955       return -1;
01956    }
01957 
01958    /*XXX Yuck, this could probably be done a lot better */
01959    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01960       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01961       /* We have the body, now we extract the file name of the first attachment. */
01962       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01963          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01964       } else {
01965          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01966          ast_mutex_unlock(&vms_p->lock);
01967          return -1;
01968       }
01969       filename = strsep(&attachment, ".");
01970       if (!strcmp(filename, file)) {
01971          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01972          vms_p->msgArray[vms_p->curmsg] = i + 1;
01973          save_body(body, vms_p, "2", attachment, 0);
01974          ast_mutex_unlock(&vms_p->lock);
01975          return 0;
01976       }
01977    }
01978    ast_mutex_unlock(&vms_p->lock);
01979 
01980    return -1;
01981 }
01982 
01983 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01984 {
01985    BODY *body;
01986    char *header_content;
01987    char *attachedfilefmt;
01988    char buf[80];
01989    struct vm_state *vms;
01990    char text_file[PATH_MAX];
01991    FILE *text_file_ptr;
01992    int res = 0;
01993    struct ast_vm_user *vmu;
01994 
01995    if (!(vmu = find_user(NULL, context, mailbox))) {
01996       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01997       return -1;
01998    }
01999    
02000    if (msgnum < 0) {
02001       if (imapgreetings) {
02002          res = imap_retrieve_greeting(dir, msgnum, vmu);
02003          goto exit;
02004       } else {
02005          res = 0;
02006          goto exit;
02007       }
02008    }
02009 
02010    /* Before anything can happen, we need a vm_state so that we can
02011     * actually access the imap server through the vms->mailstream
02012     */
02013    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
02014       /* This should not happen. If it does, then I guess we'd
02015        * need to create the vm_state, extract which mailbox to
02016        * open, and then set up the msgArray so that the correct
02017        * IMAP message could be accessed. If I have seen correctly
02018        * though, the vms should be obtainable from the vmstates list
02019        * and should have its msgArray properly set up.
02020        */
02021       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
02022       res = -1;
02023       goto exit;
02024    }
02025 
02026    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
02027    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
02028 
02029    /* Don't try to retrieve a message from IMAP if it already is on the file system */
02030    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
02031       res = 0;
02032       goto exit;
02033    }
02034 
02035    ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
02036    if (vms->msgArray[msgnum] == 0) {
02037       ast_log(LOG_WARNING, "Trying to access unknown message\n");
02038       res = -1;
02039       goto exit;
02040    }
02041 
02042    /* This will only work for new messages... */
02043    ast_mutex_lock(&vms->lock);
02044    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
02045    ast_mutex_unlock(&vms->lock);
02046    /* empty string means no valid header */
02047    if (ast_strlen_zero(header_content)) {
02048       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
02049       res = -1;
02050       goto exit;
02051    }
02052 
02053    ast_mutex_lock(&vms->lock);
02054    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
02055    ast_mutex_unlock(&vms->lock);
02056 
02057    /* We have the body, now we extract the file name of the first attachment. */
02058    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
02059       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
02060    } else {
02061       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
02062       res = -1;
02063       goto exit;
02064    }
02065    
02066    /* Find the format of the attached file */
02067 
02068    strsep(&attachedfilefmt, ".");
02069    if (!attachedfilefmt) {
02070       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
02071       res = -1;
02072       goto exit;
02073    }
02074    
02075    save_body(body, vms, "2", attachedfilefmt, 0);
02076    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02077       *vms->introfn = '\0';
02078    }
02079 
02080    /* Get info from headers!! */
02081    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02082 
02083    if (!(text_file_ptr = fopen(text_file, "w"))) {
02084       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02085    }
02086 
02087    fprintf(text_file_ptr, "%s\n", "[message]");
02088 
02089    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02090    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02091    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02092    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02093    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02094    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02095    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02096    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02097    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02098    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02099    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02100    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02101    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02102    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02103    fclose(text_file_ptr);
02104 
02105 exit:
02106    free_user(vmu);
02107    return res;
02108 }
02109 
02110 static int folder_int(const char *folder)
02111 {
02112    /*assume a NULL folder means INBOX*/
02113    if (!folder) {
02114       return 0;
02115    }
02116    if (!strcasecmp(folder, imapfolder)) {
02117       return 0;
02118    } else if (!strcasecmp(folder, "Old")) {
02119       return 1;
02120    } else if (!strcasecmp(folder, "Work")) {
02121       return 2;
02122    } else if (!strcasecmp(folder, "Family")) {
02123       return 3;
02124    } else if (!strcasecmp(folder, "Friends")) {
02125       return 4;
02126    } else if (!strcasecmp(folder, "Cust1")) {
02127       return 5;
02128    } else if (!strcasecmp(folder, "Cust2")) {
02129       return 6;
02130    } else if (!strcasecmp(folder, "Cust3")) {
02131       return 7;
02132    } else if (!strcasecmp(folder, "Cust4")) {
02133       return 8;
02134    } else if (!strcasecmp(folder, "Cust5")) {
02135       return 9;
02136    } else if (!strcasecmp(folder, "Urgent")) {
02137       return 11;
02138    } else { /*assume they meant INBOX if folder is not found otherwise*/
02139       return 0;
02140    }
02141 }
02142 
02143 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02144 {
02145    SEARCHPGM *pgm;
02146    SEARCHHEADER *hdr;
02147 
02148    struct ast_vm_user *vmu, vmus;
02149    struct vm_state *vms_p;
02150    int ret = 0;
02151    int fold = folder_int(folder);
02152    int urgent = 0;
02153    
02154    /* If URGENT, then look at INBOX */
02155    if (fold == 11) {
02156       fold = NEW_FOLDER;
02157       urgent = 1;
02158    }
02159 
02160    if (ast_strlen_zero(mailbox))
02161       return 0;
02162 
02163    /* We have to get the user before we can open the stream! */
02164    vmu = find_user(&vmus, context, mailbox);
02165    if (!vmu) {
02166       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02167       return -1;
02168    } else {
02169       /* No IMAP account available */
02170       if (vmu->imapuser[0] == '\0') {
02171          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02172          return -1;
02173       }
02174    }
02175 
02176    /* No IMAP account available */
02177    if (vmu->imapuser[0] == '\0') {
02178       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02179       free_user(vmu);
02180       return -1;
02181    }
02182 
02183    /* check if someone is accessing this box right now... */
02184    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02185    if (!vms_p) {
02186       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02187    }
02188    if (vms_p) {
02189       ast_debug(3, "Returning before search - user is logged in\n");
02190       if (fold == 0) { /* INBOX */
02191          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02192       }
02193       if (fold == 1) { /* Old messages */
02194          return vms_p->oldmessages;
02195       }
02196    }
02197 
02198    /* add one if not there... */
02199    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02200    if (!vms_p) {
02201       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02202    }
02203 
02204    if (!vms_p) {
02205       vms_p = create_vm_state_from_user(vmu);
02206    }
02207    ret = init_mailstream(vms_p, fold);
02208    if (!vms_p->mailstream) {
02209       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02210       return -1;
02211    }
02212    if (ret == 0) {
02213       ast_mutex_lock(&vms_p->lock);
02214       pgm = mail_newsearchpgm ();
02215       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02216       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02217       pgm->header = hdr;
02218       if (fold != OLD_FOLDER) {
02219          pgm->unseen = 1;
02220          pgm->seen = 0;
02221       }
02222       /* In the special case where fold is 1 (old messages) we have to do things a bit
02223        * differently. Old messages are stored in the INBOX but are marked as "seen"
02224        */
02225       else {
02226          pgm->unseen = 0;
02227          pgm->seen = 1;
02228       }
02229       /* look for urgent messages */
02230       if (fold == NEW_FOLDER) {
02231          if (urgent) {
02232             pgm->flagged = 1;
02233             pgm->unflagged = 0;
02234          } else {
02235             pgm->flagged = 0;
02236             pgm->unflagged = 1;
02237          }
02238       }
02239       pgm->undeleted = 1;
02240       pgm->deleted = 0;
02241 
02242       vms_p->vmArrayIndex = 0;
02243       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02244       if (fold == 0 && urgent == 0)
02245          vms_p->newmessages = vms_p->vmArrayIndex;
02246       if (fold == 1)
02247          vms_p->oldmessages = vms_p->vmArrayIndex;
02248       if (fold == 0 && urgent == 1)
02249          vms_p->urgentmessages = vms_p->vmArrayIndex;
02250       /*Freeing the searchpgm also frees the searchhdr*/
02251       mail_free_searchpgm(&pgm);
02252       ast_mutex_unlock(&vms_p->lock);
02253       vms_p->updated = 0;
02254       return vms_p->vmArrayIndex;
02255    } else {
02256       ast_mutex_lock(&vms_p->lock);
02257       mail_ping(vms_p->mailstream);
02258       ast_mutex_unlock(&vms_p->lock);
02259    }
02260    return 0;
02261 }
02262 
02263 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02264 {
02265    /* Check if mailbox is full */
02266    check_quota(vms, vmu->imapfolder);
02267    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02268       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02269       ast_play_and_wait(chan, "vm-mailboxfull");
02270       return -1;
02271    }
02272 
02273    /* Check if we have exceeded maxmsg */
02274    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));
02275    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02276       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02277       ast_play_and_wait(chan, "vm-mailboxfull");
02278       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02279       return -1;
02280    }
02281 
02282    return 0;
02283 }
02284 
02285 /*!
02286  * \brief Gets the number of messages that exist in a mailbox folder.
02287  * \param context
02288  * \param mailbox
02289  * \param folder
02290  * 
02291  * This method is used when IMAP backend is used.
02292  * \return The number of messages in this mailbox folder (zero or more).
02293  */
02294 static int messagecount(const char *context, const char *mailbox, const char *folder)
02295 {
02296    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02297       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02298    } else {
02299       return __messagecount(context, mailbox, folder);
02300    }
02301 }
02302 
02303 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)
02304 {
02305    char *myserveremail = serveremail;
02306    char fn[PATH_MAX];
02307    char introfn[PATH_MAX];
02308    char mailbox[256];
02309    char *stringp;
02310    FILE *p = NULL;
02311    char tmp[80] = "/tmp/astmail-XXXXXX";
02312    long len;
02313    void *buf;
02314    int tempcopy = 0;
02315    STRING str;
02316    int ret; /* for better error checking */
02317    char *imap_flags = NIL;
02318    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02319    int box = NEW_FOLDER;
02320 
02321    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02322    if (msgnum < 0) {
02323       if(!imapgreetings) {
02324          return 0;
02325       } else {
02326          box = GREETINGS_FOLDER;
02327       }
02328    }
02329 
02330    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02331       return -1;
02332    }
02333 
02334    /* Set urgent flag for IMAP message */
02335    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02336       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02337       imap_flags = "\\FLAGGED";
02338    }
02339 
02340    /* Attach only the first format */
02341    fmt = ast_strdupa(fmt);
02342    stringp = fmt;
02343    strsep(&stringp, "|");
02344 
02345    if (!ast_strlen_zero(vmu->serveremail))
02346       myserveremail = vmu->serveremail;
02347 
02348    if (msgnum > -1)
02349       make_file(fn, sizeof(fn), dir, msgnum);
02350    else
02351       ast_copy_string (fn, dir, sizeof(fn));
02352 
02353    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02354    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02355       *introfn = '\0';
02356    }
02357 
02358    if (ast_strlen_zero(vmu->email)) {
02359       /* We need the vmu->email to be set when we call make_email_file, but
02360        * if we keep it set, a duplicate e-mail will be created. So at the end
02361        * of this function, we will revert back to an empty string if tempcopy
02362        * is 1.
02363        */
02364       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02365       tempcopy = 1;
02366    }
02367 
02368    if (!strcmp(fmt, "wav49"))
02369       fmt = "WAV";
02370    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02371 
02372    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02373       command hangs. */
02374    if (!(p = vm_mkftemp(tmp))) {
02375       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02376       if (tempcopy)
02377          *(vmu->email) = '\0';
02378       return -1;
02379    }
02380 
02381    if (msgnum < 0 && imapgreetings) {
02382       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02383          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02384          return -1;
02385       }
02386       imap_delete_old_greeting(fn, vms);
02387    }
02388 
02389    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02390       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02391       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02392       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02393    /* read mail file to memory */
02394    len = ftell(p);
02395    rewind(p);
02396    if (!(buf = ast_malloc(len + 1))) {
02397       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02398       fclose(p);
02399       if (tempcopy)
02400          *(vmu->email) = '\0';
02401       return -1;
02402    }
02403    if (fread(buf, len, 1, p) < len) {
02404       if (ferror(p)) {
02405          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02406          return -1;
02407       }
02408    }
02409    ((char *) buf)[len] = '\0';
02410    INIT(&str, mail_string, buf, len);
02411    ret = init_mailstream(vms, box);
02412    if (ret == 0) {
02413       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02414       ast_mutex_lock(&vms->lock);
02415       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02416          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02417       ast_mutex_unlock(&vms->lock);
02418       fclose(p);
02419       unlink(tmp);
02420       ast_free(buf);
02421    } else {
02422       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02423       fclose(p);
02424       unlink(tmp);
02425       ast_free(buf);
02426       return -1;
02427    }
02428    ast_debug(3, "%s stored\n", fn);
02429 
02430    if (tempcopy)
02431       *(vmu->email) = '\0';
02432    inprocess_count(vmu->mailbox, vmu->context, -1);
02433    return 0;
02434 
02435 }
02436 
02437 /*!
02438  * \brief Gets the number of messages that exist in the inbox folder.
02439  * \param mailbox_context
02440  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02441  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02442  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02443  * 
02444  * This method is used when IMAP backend is used.
02445  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02446  *
02447  * \return zero on success, -1 on error.
02448  */
02449 
02450 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02451 {
02452    char tmp[PATH_MAX] = "";
02453    char *mailboxnc;
02454    char *context;
02455    char *mb;
02456    char *cur;
02457    if (newmsgs)
02458       *newmsgs = 0;
02459    if (oldmsgs)
02460       *oldmsgs = 0;
02461    if (urgentmsgs)
02462       *urgentmsgs = 0;
02463 
02464    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02465    /* If no mailbox, return immediately */
02466    if (ast_strlen_zero(mailbox_context))
02467       return 0;
02468 
02469    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02470    context = strchr(tmp, '@');
02471    if (strchr(mailbox_context, ',')) {
02472       int tmpnew, tmpold, tmpurgent;
02473       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02474       mb = tmp;
02475       while ((cur = strsep(&mb, ", "))) {
02476          if (!ast_strlen_zero(cur)) {
02477             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02478                return -1;
02479             else {
02480                if (newmsgs)
02481                   *newmsgs += tmpnew; 
02482                if (oldmsgs)
02483                   *oldmsgs += tmpold;
02484                if (urgentmsgs)
02485                   *urgentmsgs += tmpurgent;
02486             }
02487          }
02488       }
02489       return 0;
02490    }
02491    if (context) {
02492       *context = '\0';
02493       mailboxnc = tmp;
02494       context++;
02495    } else {
02496       context = "default";
02497       mailboxnc = (char *) mailbox_context;
02498    }
02499 
02500    if (newmsgs) {
02501       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02502       if (!vmu) {
02503          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02504          return -1;
02505       }
02506       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02507          return -1;
02508       }
02509    }
02510    if (oldmsgs) {
02511       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02512          return -1;
02513       }
02514    }
02515    if (urgentmsgs) {
02516       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02517          return -1;
02518       }
02519    }
02520    return 0;
02521 }
02522 
02523 /** 
02524  * \brief Determines if the given folder has messages.
02525  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02526  * \param folder the folder to look in
02527  *
02528  * This function is used when the mailbox is stored in an IMAP back end.
02529  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02530  * \return 1 if the folder has one or more messages. zero otherwise.
02531  */
02532 
02533 static int has_voicemail(const char *mailbox, const char *folder)
02534 {
02535    char tmp[256], *tmp2, *box, *context;
02536    ast_copy_string(tmp, mailbox, sizeof(tmp));
02537    tmp2 = tmp;
02538    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02539       while ((box = strsep(&tmp2, ",&"))) {
02540          if (!ast_strlen_zero(box)) {
02541             if (has_voicemail(box, folder)) {
02542                return 1;
02543             }
02544          }
02545       }
02546    }
02547    if ((context = strchr(tmp, '@'))) {
02548       *context++ = '\0';
02549    } else {
02550       context = "default";
02551    }
02552    return __messagecount(context, tmp, folder) ? 1 : 0;
02553 }
02554 
02555 /*!
02556  * \brief Copies a message from one mailbox to another.
02557  * \param chan
02558  * \param vmu
02559  * \param imbox
02560  * \param msgnum
02561  * \param duration
02562  * \param recip
02563  * \param fmt
02564  * \param dir
02565  *
02566  * This works with IMAP storage based mailboxes.
02567  *
02568  * \return zero on success, -1 on error.
02569  */
02570 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)
02571 {
02572    struct vm_state *sendvms = NULL;
02573    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02574    if (msgnum >= recip->maxmsg) {
02575       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02576       return -1;
02577    }
02578    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02579       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02580       return -1;
02581    }
02582    if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
02583       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02584       return -1;
02585    }
02586    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02587    ast_mutex_lock(&sendvms->lock);
02588    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02589       ast_mutex_unlock(&sendvms->lock);
02590       return 0;
02591    }
02592    ast_mutex_unlock(&sendvms->lock);
02593    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02594    return -1;
02595 }
02596 
02597 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02598 {
02599    char tmp[256], *t = tmp;
02600    size_t left = sizeof(tmp);
02601    
02602    if (box == OLD_FOLDER) {
02603       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02604    } else {
02605       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02606    }
02607 
02608    if (box == NEW_FOLDER) {
02609       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02610    } else {
02611       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02612    }
02613 
02614    /* Build up server information */
02615    ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
02616 
02617    /* Add authentication user if present */
02618    if (!ast_strlen_zero(authuser))
02619       ast_build_string(&t, &left, "/authuser=%s", authuser);
02620 
02621    /* Add flags if present */
02622    if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
02623       ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
02624    }
02625 
02626    /* End with username */
02627 #if 1
02628    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02629 #else
02630    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02631 #endif
02632    if (box == NEW_FOLDER || box == OLD_FOLDER)
02633       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02634    else if (box == GREETINGS_FOLDER)
02635       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02636    else {   /* Other folders such as Friends, Family, etc... */
02637       if (!ast_strlen_zero(imapparentfolder)) {
02638          /* imapparentfolder would typically be set to INBOX */
02639          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02640       } else {
02641          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02642       }
02643    }
02644 }
02645 
02646 static int init_mailstream(struct vm_state *vms, int box)
02647 {
02648    MAILSTREAM *stream = NIL;
02649    long debug;
02650    char tmp[256];
02651 
02652    if (!vms) {
02653       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02654       return -1;
02655    }
02656    ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
02657    if (vms->mailstream == NIL || !vms->mailstream) {
02658       ast_debug(1, "mailstream not set.\n");
02659    } else {
02660       stream = vms->mailstream;
02661    }
02662    /* debug = T;  user wants protocol telemetry? */
02663    debug = NIL;  /* NO protocol telemetry? */
02664 
02665    if (delimiter == '\0') {      /* did not probe the server yet */
02666       char *cp;
02667 #ifdef USE_SYSTEM_IMAP
02668 #include <imap/linkage.c>
02669 #elif defined(USE_SYSTEM_CCLIENT)
02670 #include <c-client/linkage.c>
02671 #else
02672 #include "linkage.c"
02673 #endif
02674       /* Connect to INBOX first to get folders delimiter */
02675       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02676       ast_mutex_lock(&vms->lock);
02677       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02678       ast_mutex_unlock(&vms->lock);
02679       if (stream == NIL) {
02680          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02681          return -1;
02682       }
02683       get_mailbox_delimiter(vms, stream);
02684       /* update delimiter in imapfolder */
02685       for (cp = vms->imapfolder; *cp; cp++)
02686          if (*cp == '/')
02687             *cp = delimiter;
02688    }
02689    /* Now connect to the target folder */
02690    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02691    ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
02692    ast_mutex_lock(&vms->lock);
02693    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02694    ast_mutex_unlock(&vms->lock);
02695    if (vms->mailstream == NIL) {
02696       return -1;
02697    } else {
02698       return 0;
02699    }
02700 }
02701 
02702 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02703 {
02704    SEARCHPGM *pgm;
02705    SEARCHHEADER *hdr;
02706    int urgent = 0;
02707 
02708    /* If Urgent, then look at INBOX */
02709    if (box == 11) {
02710       box = NEW_FOLDER;
02711       urgent = 1;
02712    }
02713 
02714    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02715    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02716    ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
02717    ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
02718    ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
02719    vms->imapversion = vmu->imapversion;
02720    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02721 
02722    if (init_mailstream(vms, box) || !vms->mailstream) {
02723       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02724       return -1;
02725    }
02726 
02727    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02728 
02729    /* Check Quota */
02730    if  (box == 0)  {
02731       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02732       check_quota(vms, (char *) mbox(vmu, box));
02733    }
02734 
02735    ast_mutex_lock(&vms->lock);
02736    pgm = mail_newsearchpgm();
02737 
02738    /* Check IMAP folder for Asterisk messages only... */
02739    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02740    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02741    pgm->header = hdr;
02742    pgm->deleted = 0;
02743    pgm->undeleted = 1;
02744 
02745    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02746    if (box == NEW_FOLDER && urgent == 1) {
02747       pgm->unseen = 1;
02748       pgm->seen = 0;
02749       pgm->flagged = 1;
02750       pgm->unflagged = 0;
02751    } else if (box == NEW_FOLDER && urgent == 0) {
02752       pgm->unseen = 1;
02753       pgm->seen = 0;
02754       pgm->flagged = 0;
02755       pgm->unflagged = 1;
02756    } else if (box == OLD_FOLDER) {
02757       pgm->seen = 1;
02758       pgm->unseen = 0;
02759    }
02760 
02761    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02762 
02763    vms->vmArrayIndex = 0;
02764    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02765    vms->lastmsg = vms->vmArrayIndex - 1;
02766    mail_free_searchpgm(&pgm);
02767    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02768     * ensure to allocate enough space to account for all of them. Warn if old messages
02769     * have not been checked first as that is required.
02770     */
02771    if (box == 0 && !vms->dh_arraysize) {
02772       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02773    }
02774    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02775       ast_mutex_unlock(&vms->lock);
02776       return -1;
02777    }
02778 
02779    ast_mutex_unlock(&vms->lock);
02780    return 0;
02781 }
02782 
02783 static void write_file(char *filename, char *buffer, unsigned long len)
02784 {
02785    FILE *output;
02786 
02787    output = fopen (filename, "w");
02788    if (fwrite(buffer, len, 1, output) != 1) {
02789       if (ferror(output)) {
02790          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02791       }
02792    }
02793    fclose (output);
02794 }
02795 
02796 static void update_messages_by_imapuser(const char *user, unsigned long number)
02797 {
02798    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02799 
02800    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02801       return;
02802    }
02803 
02804    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02805    vms->msgArray[vms->vmArrayIndex++] = number;
02806 }
02807 
02808 void mm_searched(MAILSTREAM *stream, unsigned long number)
02809 {
02810    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02811 
02812    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02813       return;
02814 
02815    update_messages_by_imapuser(user, number);
02816 }
02817 
02818 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02819 {
02820    struct ast_variable *var;
02821    struct ast_vm_user *vmu;
02822 
02823    vmu = ast_calloc(1, sizeof *vmu);
02824    if (!vmu)
02825       return NULL;
02826    ast_set_flag(vmu, VM_ALLOCED);
02827    populate_defaults(vmu);
02828 
02829    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02830    if (var) {
02831       apply_options_full(vmu, var);
02832       ast_variables_destroy(var);
02833       return vmu;
02834    } else {
02835       ast_free(vmu);
02836       return NULL;
02837    }
02838 }
02839 
02840 /* Interfaces to C-client */
02841 
02842 void mm_exists(MAILSTREAM * stream, unsigned long number)
02843 {
02844    /* mail_ping will callback here if new mail! */
02845    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02846    if (number == 0) return;
02847    set_update(stream);
02848 }
02849 
02850 
02851 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02852 {
02853    /* mail_ping will callback here if expunged mail! */
02854    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02855    if (number == 0) return;
02856    set_update(stream);
02857 }
02858 
02859 
02860 void mm_flags(MAILSTREAM * stream, unsigned long number)
02861 {
02862    /* mail_ping will callback here if read mail! */
02863    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02864    if (number == 0) return;
02865    set_update(stream);
02866 }
02867 
02868 
02869 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02870 {
02871    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02872    mm_log (string, errflg);
02873 }
02874 
02875 
02876 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02877 {
02878    if (delimiter == '\0') {
02879       delimiter = delim;
02880    }
02881 
02882    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02883    if (attributes & LATT_NOINFERIORS)
02884       ast_debug(5, "no inferiors\n");
02885    if (attributes & LATT_NOSELECT)
02886       ast_debug(5, "no select\n");
02887    if (attributes & LATT_MARKED)
02888       ast_debug(5, "marked\n");
02889    if (attributes & LATT_UNMARKED)
02890       ast_debug(5, "unmarked\n");
02891 }
02892 
02893 
02894 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02895 {
02896    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02897    if (attributes & LATT_NOINFERIORS)
02898       ast_debug(5, "no inferiors\n");
02899    if (attributes & LATT_NOSELECT)
02900       ast_debug(5, "no select\n");
02901    if (attributes & LATT_MARKED)
02902       ast_debug(5, "marked\n");
02903    if (attributes & LATT_UNMARKED)
02904       ast_debug(5, "unmarked\n");
02905 }
02906 
02907 
02908 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02909 {
02910    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02911    if (status->flags & SA_MESSAGES)
02912       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02913    if (status->flags & SA_RECENT)
02914       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02915    if (status->flags & SA_UNSEEN)
02916       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02917    if (status->flags & SA_UIDVALIDITY)
02918       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02919    if (status->flags & SA_UIDNEXT)
02920       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02921    ast_log(AST_LOG_NOTICE, "\n");
02922 }
02923 
02924 
02925 void mm_log(char *string, long errflg)
02926 {
02927    switch ((short) errflg) {
02928       case NIL:
02929          ast_debug(1, "IMAP Info: %s\n", string);
02930          break;
02931       case PARSE:
02932       case WARN:
02933          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02934          break;
02935       case ERROR:
02936          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02937          break;
02938    }
02939 }
02940 
02941 
02942 void mm_dlog(char *string)
02943 {
02944    ast_log(AST_LOG_NOTICE, "%s\n", string);
02945 }
02946 
02947 
02948 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02949 {
02950    struct ast_vm_user *vmu;
02951 
02952    ast_debug(4, "Entering callback mm_login\n");
02953 
02954    ast_copy_string(user, mb->user, MAILTMPLEN);
02955 
02956    /* We should only do this when necessary */
02957    if (!ast_strlen_zero(authpassword)) {
02958       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02959    } else {
02960       AST_LIST_TRAVERSE(&users, vmu, list) {
02961          if (!strcasecmp(mb->user, vmu->imapuser)) {
02962             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02963             break;
02964          }
02965       }
02966       if (!vmu) {
02967          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02968             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02969             free_user(vmu);
02970          }
02971       }
02972    }
02973 }
02974 
02975 
02976 void mm_critical(MAILSTREAM * stream)
02977 {
02978 }
02979 
02980 
02981 void mm_nocritical(MAILSTREAM * stream)
02982 {
02983 }
02984 
02985 
02986 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02987 {
02988    kill (getpid (), SIGSTOP);
02989    return NIL;
02990 }
02991 
02992 
02993 void mm_fatal(char *string)
02994 {
02995    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02996 }
02997 
02998 /* C-client callback to handle quota */
02999 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
03000 {
03001    struct vm_state *vms;
03002    char *mailbox = stream->mailbox, *user;
03003    char buf[1024] = "";
03004    unsigned long usage = 0, limit = 0;
03005 
03006    while (pquota) {
03007       usage = pquota->usage;
03008       limit = pquota->limit;
03009       pquota = pquota->next;
03010    }
03011 
03012    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)))) {
03013       ast_log(AST_LOG_ERROR, "No state found.\n");
03014       return;
03015    }
03016 
03017    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
03018 
03019    vms->quota_usage = usage;
03020    vms->quota_limit = limit;
03021 }
03022 
03023 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
03024 {
03025    char *start, *eol_pnt;
03026    int taglen;
03027 
03028    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
03029       return NULL;
03030 
03031    taglen = strlen(tag) + 1;
03032    if (taglen < 1)
03033       return NULL;
03034 
03035    if (!(start = strstr(header, tag)))
03036       return NULL;
03037 
03038    /* Since we can be called multiple times we should clear our buffer */
03039    memset(buf, 0, len);
03040 
03041    ast_copy_string(buf, start+taglen, len);
03042    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
03043       *eol_pnt = '\0';
03044    return buf;
03045 }
03046 
03047 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
03048 {
03049    char *start, *eol_pnt, *quote;
03050 
03051    if (ast_strlen_zero(mailbox))
03052       return NULL;
03053 
03054    if (!(start = strstr(mailbox, "/user=")))
03055       return NULL;
03056 
03057    ast_copy_string(buf, start+6, len);
03058 
03059    if (!(quote = strchr(buf, '"'))) {
03060       if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
03061          *eol_pnt = '\0';
03062       }
03063       return buf;
03064    } else {
03065       if ((eol_pnt = strchr(quote + 1, '"'))) {
03066          *eol_pnt = '\0';
03067       }
03068       return quote + 1;
03069    }
03070 }
03071 
03072 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
03073 {
03074    struct vm_state *vms_p;
03075 
03076    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03077    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03078       return vms_p;
03079    }
03080    ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
03081    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03082       return NULL;
03083    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03084    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03085    ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
03086    ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
03087    ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
03088    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03089    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03090    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03091    vms_p->imapversion = vmu->imapversion;
03092    ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03093    vms_p->updated = 1;
03094    /* set mailbox to INBOX! */
03095    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03096    init_vm_state(vms_p);
03097    vmstate_insert(vms_p);
03098    return vms_p;
03099 }
03100 
03101 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03102 {
03103    struct vmstate *vlist = NULL;
03104 
03105    if (interactive) {
03106       struct vm_state *vms;
03107       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03108       vms = pthread_getspecific(ts_vmstate.key);
03109       return vms;
03110    }
03111 
03112    AST_LIST_LOCK(&vmstates);
03113    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03114       if (!vlist->vms) {
03115          ast_debug(3, "error: vms is NULL for %s\n", user);
03116          continue;
03117       }
03118       if (vlist->vms->imapversion != imapversion) {
03119          continue;
03120       }
03121       if (!vlist->vms->imapuser) {
03122          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03123          continue;
03124       }
03125 
03126       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03127          AST_LIST_UNLOCK(&vmstates);
03128          return vlist->vms;
03129       }
03130    }
03131    AST_LIST_UNLOCK(&vmstates);
03132 
03133    ast_debug(3, "%s not found in vmstates\n", user);
03134 
03135    return NULL;
03136 }
03137 
03138 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03139 {
03140 
03141    struct vmstate *vlist = NULL;
03142    const char *local_context = S_OR(context, "default");
03143 
03144    if (interactive) {
03145       struct vm_state *vms;
03146       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03147       vms = pthread_getspecific(ts_vmstate.key);
03148       return vms;
03149    }
03150 
03151    AST_LIST_LOCK(&vmstates);
03152    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03153       if (!vlist->vms) {
03154          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03155          continue;
03156       }
03157       if (vlist->vms->imapversion != imapversion) {
03158          continue;
03159       }
03160       if (!vlist->vms->username || !vlist->vms->context) {
03161          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03162          continue;
03163       }
03164 
03165       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);
03166 
03167       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03168          ast_debug(3, "Found it!\n");
03169          AST_LIST_UNLOCK(&vmstates);
03170          return vlist->vms;
03171       }
03172    }
03173    AST_LIST_UNLOCK(&vmstates);
03174 
03175    ast_debug(3, "%s not found in vmstates\n", mailbox);
03176 
03177    return NULL;
03178 }
03179 
03180 static void vmstate_insert(struct vm_state *vms)
03181 {
03182    struct vmstate *v;
03183    struct vm_state *altvms;
03184 
03185    /* If interactive, it probably already exists, and we should
03186       use the one we already have since it is more up to date.
03187       We can compare the username to find the duplicate */
03188    if (vms->interactive == 1) {
03189       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03190       if (altvms) {
03191          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03192          vms->newmessages = altvms->newmessages;
03193          vms->oldmessages = altvms->oldmessages;
03194          vms->vmArrayIndex = altvms->vmArrayIndex;
03195          vms->lastmsg = altvms->lastmsg;
03196          vms->curmsg = altvms->curmsg;
03197          /* get a pointer to the persistent store */
03198          vms->persist_vms = altvms;
03199          /* Reuse the mailstream? */
03200 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03201          vms->mailstream = altvms->mailstream;
03202 #else
03203          vms->mailstream = NIL;
03204 #endif
03205       }
03206       return;
03207    }
03208 
03209    if (!(v = ast_calloc(1, sizeof(*v))))
03210       return;
03211 
03212    v->vms = vms;
03213 
03214    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03215 
03216    AST_LIST_LOCK(&vmstates);
03217    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03218    AST_LIST_UNLOCK(&vmstates);
03219 }
03220 
03221 static void vmstate_delete(struct vm_state *vms)
03222 {
03223    struct vmstate *vc = NULL;
03224    struct vm_state *altvms = NULL;
03225 
03226    /* If interactive, we should copy pertinent info
03227       back to the persistent state (to make update immediate) */
03228    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03229       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03230       altvms->newmessages = vms->newmessages;
03231       altvms->oldmessages = vms->oldmessages;
03232       altvms->updated = 1;
03233       vms->mailstream = mail_close(vms->mailstream);
03234 
03235       /* Interactive states are not stored within the persistent list */
03236       return;
03237    }
03238 
03239    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03240 
03241    AST_LIST_LOCK(&vmstates);
03242    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03243       if (vc->vms == vms) {
03244          AST_LIST_REMOVE_CURRENT(list);
03245          break;
03246       }
03247    }
03248    AST_LIST_TRAVERSE_SAFE_END
03249    AST_LIST_UNLOCK(&vmstates);
03250    
03251    if (vc) {
03252       ast_mutex_destroy(&vc->vms->lock);
03253       ast_free(vc);
03254    }
03255    else
03256       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03257 }
03258 
03259 static void set_update(MAILSTREAM * stream)
03260 {
03261    struct vm_state *vms;
03262    char *mailbox = stream->mailbox, *user;
03263    char buf[1024] = "";
03264 
03265    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03266       if (user && option_debug > 2)
03267          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03268       return;
03269    }
03270 
03271    ast_debug(3, "User %s mailbox set for update.\n", user);
03272 
03273    vms->updated = 1; /* Set updated flag since mailbox changed */
03274 }
03275 
03276 static void init_vm_state(struct vm_state *vms)
03277 {
03278    int x;
03279    vms->vmArrayIndex = 0;
03280    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03281       vms->msgArray[x] = 0;
03282    }
03283    ast_mutex_init(&vms->lock);
03284 }
03285 
03286 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
03287 {
03288    char *body_content;
03289    char *body_decoded;
03290    char *fn = is_intro ? vms->introfn : vms->fn;
03291    unsigned long len;
03292    unsigned long newlen;
03293    char filename[256];
03294 
03295    if (!body || body == NIL)
03296       return -1;
03297 
03298    ast_mutex_lock(&vms->lock);
03299    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03300    ast_mutex_unlock(&vms->lock);
03301    if (body_content != NIL) {
03302       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03303       /* ast_debug(1, body_content); */
03304       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03305       /* If the body of the file is empty, return an error */
03306       if (!newlen) {
03307          return -1;
03308       }
03309       write_file(filename, (char *) body_decoded, newlen);
03310    } else {
03311       ast_debug(5, "Body of message is NULL.\n");
03312       return -1;
03313    }
03314    return 0;
03315 }
03316 
03317 /*! 
03318  * \brief Get delimiter via mm_list callback 
03319  * \param vms     The voicemail state object
03320  * \param stream
03321  *
03322  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03323  */
03324 /* MUTEX should already be held */
03325 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
03326    char tmp[50];
03327    snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
03328    mail_list(stream, tmp, "*");
03329 }
03330 
03331 /*! 
03332  * \brief Check Quota for user 
03333  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03334  * \param mailbox the mailbox to check the quota for.
03335  *
03336  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03337  */
03338 static void check_quota(struct vm_state *vms, char *mailbox) {
03339    ast_mutex_lock(&vms->lock);
03340    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03341    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03342    if (vms && vms->mailstream != NULL) {
03343       imap_getquotaroot(vms->mailstream, mailbox);
03344    } else {
03345       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03346    }
03347    ast_mutex_unlock(&vms->lock);
03348 }
03349 
03350 #endif /* IMAP_STORAGE */
03351 
03352 /*! \brief Lock file path
03353  * only return failure if ast_lock_path returns 'timeout',
03354  * not if the path does not exist or any other reason
03355  */
03356 static int vm_lock_path(const char *path)
03357 {
03358    switch (ast_lock_path(path)) {
03359    case AST_LOCK_TIMEOUT:
03360       return -1;
03361    default:
03362       return 0;
03363    }
03364 }
03365 
03366 
03367 #ifdef ODBC_STORAGE
03368 struct generic_prepare_struct {
03369    char *sql;
03370    int argc;
03371    char **argv;
03372 };
03373 
03374 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03375 {
03376    struct generic_prepare_struct *gps = data;
03377    int res, i;
03378    SQLHSTMT stmt;
03379 
03380    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03381    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03382       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03383       return NULL;
03384    }
03385    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03386    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03387       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03388       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03389       return NULL;
03390    }
03391    for (i = 0; i < gps->argc; i++)
03392       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03393 
03394    return stmt;
03395 }
03396 
03397 /*!
03398  * \brief Retrieves a file from an ODBC data store.
03399  * \param dir the path to the file to be retreived.
03400  * \param msgnum the message number, such as within a mailbox folder.
03401  * 
03402  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03403  * 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.
03404  *
03405  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03406  * The output is the message information file with the name msgnum and the extension .txt
03407  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03408  * 
03409  * \return 0 on success, -1 on error.
03410  */
03411 static int retrieve_file(char *dir, int msgnum)
03412 {
03413    int x = 0;
03414    int res;
03415    int fd = -1;
03416    size_t fdlen = 0;
03417    void *fdm = MAP_FAILED;
03418    SQLSMALLINT colcount = 0;
03419    SQLHSTMT stmt;
03420    char sql[PATH_MAX];
03421    char fmt[80]="";
03422    char *c;
03423    char coltitle[256];
03424    SQLSMALLINT collen;
03425    SQLSMALLINT datatype;
03426    SQLSMALLINT decimaldigits;
03427    SQLSMALLINT nullable;
03428    SQLULEN colsize;
03429    SQLLEN colsize2;
03430    FILE *f = NULL;
03431    char rowdata[80];
03432    char fn[PATH_MAX];
03433    char full_fn[PATH_MAX];
03434    char msgnums[80];
03435    char *argv[] = { dir, msgnums };
03436    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03437 
03438    struct odbc_obj *obj;
03439    obj = ast_odbc_request_obj(odbc_database, 0);
03440    if (obj) {
03441       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03442       c = strchr(fmt, '|');
03443       if (c)
03444          *c = '\0';
03445       if (!strcasecmp(fmt, "wav49"))
03446          strcpy(fmt, "WAV");
03447       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03448       if (msgnum > -1)
03449          make_file(fn, sizeof(fn), dir, msgnum);
03450       else
03451          ast_copy_string(fn, dir, sizeof(fn));
03452 
03453       /* Create the information file */
03454       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03455       
03456       if (!(f = fopen(full_fn, "w+"))) {
03457          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03458          goto yuck;
03459       }
03460       
03461       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03462       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03463       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03464       if (!stmt) {
03465          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03466          ast_odbc_release_obj(obj);
03467          goto yuck;
03468       }
03469       res = SQLFetch(stmt);
03470       if (res == SQL_NO_DATA) {
03471          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03472          ast_odbc_release_obj(obj);
03473          goto yuck;
03474       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03475          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03476          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03477          ast_odbc_release_obj(obj);
03478          goto yuck;
03479       }
03480       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03481       if (fd < 0) {
03482          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03483          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03484          ast_odbc_release_obj(obj);
03485          goto yuck;
03486       }
03487       res = SQLNumResultCols(stmt, &colcount);
03488       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03489          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03490          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03491          ast_odbc_release_obj(obj);
03492          goto yuck;
03493       }
03494       if (f) 
03495          fprintf(f, "[message]\n");
03496       for (x = 0; x < colcount; x++) {
03497          rowdata[0] = '\0';
03498          colsize = 0;
03499          collen = sizeof(coltitle);
03500          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03501                   &datatype, &colsize, &decimaldigits, &nullable);
03502          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03503             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03504             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03505             ast_odbc_release_obj(obj);
03506             goto yuck;
03507          }
03508          if (!strcasecmp(coltitle, "recording")) {
03509             off_t offset;
03510             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03511             fdlen = colsize2;
03512             if (fd > -1) {
03513                char tmp[1]="";
03514                lseek(fd, fdlen - 1, SEEK_SET);
03515                if (write(fd, tmp, 1) != 1) {
03516                   close(fd);
03517                   fd = -1;
03518                   continue;
03519                }
03520                /* Read out in small chunks */
03521                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03522                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03523                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03524                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03525                      ast_odbc_release_obj(obj);
03526                      goto yuck;
03527                   } else {
03528                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03529                      munmap(fdm, CHUNKSIZE);
03530                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03531                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03532                         unlink(full_fn);
03533                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03534                         ast_odbc_release_obj(obj);
03535                         goto yuck;
03536                      }
03537                   }
03538                }
03539                if (truncate(full_fn, fdlen) < 0) {
03540                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03541                }
03542             }
03543          } else {
03544             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03545             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03546                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03547                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03548                ast_odbc_release_obj(obj);
03549                goto yuck;
03550             }
03551             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03552                fprintf(f, "%s=%s\n", coltitle, rowdata);
03553          }
03554       }
03555       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03556       ast_odbc_release_obj(obj);
03557    } else
03558       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03559 yuck:
03560    if (f)
03561       fclose(f);
03562    if (fd > -1)
03563       close(fd);
03564    return x - 1;
03565 }
03566 
03567 /*!
03568  * \brief Determines the highest message number in use for a given user and mailbox folder.
03569  * \param vmu 
03570  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03571  *
03572  * This method is used when mailboxes are stored in an ODBC back end.
03573  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03574  *
03575  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03576 
03577  */
03578 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03579 {
03580    int x = 0;
03581    int res;
03582    SQLHSTMT stmt;
03583    char sql[PATH_MAX];
03584    char rowdata[20];
03585    char *argv[] = { dir };
03586    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03587 
03588    struct odbc_obj *obj;
03589    obj = ast_odbc_request_obj(odbc_database, 0);
03590    if (obj) {
03591       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03592 
03593       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03594       if (!stmt) {
03595          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03596          ast_odbc_release_obj(obj);
03597          goto yuck;
03598       }
03599       res = SQLFetch(stmt);
03600       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03601          if (res == SQL_NO_DATA) {
03602             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03603          } else {
03604             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03605          }
03606 
03607          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03608          ast_odbc_release_obj(obj);
03609          goto yuck;
03610       }
03611       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03612       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03613          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03614          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03615          ast_odbc_release_obj(obj);
03616          goto yuck;
03617       }
03618       if (sscanf(rowdata, "%30d", &x) != 1)
03619          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03620       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03621       ast_odbc_release_obj(obj);
03622       return x;
03623    } else
03624       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03625 yuck:
03626    return x - 1;
03627 }
03628 
03629 /*!
03630  * \brief Determines if the specified message exists.
03631  * \param dir the folder the mailbox folder to look for messages. 
03632  * \param msgnum the message index to query for.
03633  *
03634  * This method is used when mailboxes are stored in an ODBC back end.
03635  *
03636  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03637  */
03638 static int message_exists(char *dir, int msgnum)
03639 {
03640    int x = 0;
03641    int res;
03642    SQLHSTMT stmt;
03643    char sql[PATH_MAX];
03644    char rowdata[20];
03645    char msgnums[20];
03646    char *argv[] = { dir, msgnums };
03647    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03648 
03649    struct odbc_obj *obj;
03650    obj = ast_odbc_request_obj(odbc_database, 0);
03651    if (obj) {
03652       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03653       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03654       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03655       if (!stmt) {
03656          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03657          ast_odbc_release_obj(obj);
03658          goto yuck;
03659       }
03660       res = SQLFetch(stmt);
03661       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03662          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03663          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03664          ast_odbc_release_obj(obj);
03665          goto yuck;
03666       }
03667       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03668       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03669          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03670          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03671          ast_odbc_release_obj(obj);
03672          goto yuck;
03673       }
03674       if (sscanf(rowdata, "%30d", &x) != 1)
03675          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03676       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03677       ast_odbc_release_obj(obj);
03678    } else
03679       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03680 yuck:
03681    return x;
03682 }
03683 
03684 /*!
03685  * \brief returns the number of messages found.
03686  * \param vmu
03687  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03688  *
03689  * This method is used when mailboxes are stored in an ODBC back end.
03690  *
03691  * \return The count of messages being zero or more, less than zero on error.
03692  */
03693 static int count_messages(struct ast_vm_user *vmu, char *dir)
03694 {
03695    int x = 0;
03696    int res;
03697    SQLHSTMT stmt;
03698    char sql[PATH_MAX];
03699    char rowdata[20];
03700    char *argv[] = { dir };
03701    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03702 
03703    struct odbc_obj *obj;
03704    obj = ast_odbc_request_obj(odbc_database, 0);
03705    if (obj) {
03706       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03707       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03708       if (!stmt) {
03709          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03710          ast_odbc_release_obj(obj);
03711          goto yuck;
03712       }
03713       res = SQLFetch(stmt);
03714       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03715          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03716          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03717          ast_odbc_release_obj(obj);
03718          goto yuck;
03719       }
03720       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03721       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03722          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03723          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03724          ast_odbc_release_obj(obj);
03725          goto yuck;
03726       }
03727       if (sscanf(rowdata, "%30d", &x) != 1)
03728          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03729       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03730       ast_odbc_release_obj(obj);
03731       return x;
03732    } else
03733       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03734 yuck:
03735    return x - 1;
03736 
03737 }
03738 
03739 /*!
03740  * \brief Deletes a message from the mailbox folder.
03741  * \param sdir The mailbox folder to work in.
03742  * \param smsg The message index to be deleted.
03743  *
03744  * This method is used when mailboxes are stored in an ODBC back end.
03745  * The specified message is directly deleted from the database 'voicemessages' table.
03746  * 
03747  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03748  */
03749 static void delete_file(const char *sdir, int smsg)
03750 {
03751    SQLHSTMT stmt;
03752    char sql[PATH_MAX];
03753    char msgnums[20];
03754    char *argv[] = { NULL, msgnums };
03755    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03756    struct odbc_obj *obj;
03757 
03758    argv[0] = ast_strdupa(sdir);
03759 
03760    obj = ast_odbc_request_obj(odbc_database, 0);
03761    if (obj) {
03762       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03763       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03764       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03765       if (!stmt)
03766          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03767       else
03768          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03769       ast_odbc_release_obj(obj);
03770    } else
03771       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03772    return;  
03773 }
03774 
03775 /*!
03776  * \brief Copies a voicemail from one mailbox to another.
03777  * \param sdir the folder for which to look for the message to be copied.
03778  * \param smsg the index of the message to be copied.
03779  * \param ddir the destination folder to copy the message into.
03780  * \param dmsg the index to be used for the copied message.
03781  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03782  * \param dmailboxcontext The context for the destination user.
03783  *
03784  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03785  */
03786 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03787 {
03788    SQLHSTMT stmt;
03789    char sql[512];
03790    char msgnums[20];
03791    char msgnumd[20];
03792    struct odbc_obj *obj;
03793    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03794    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03795 
03796    delete_file(ddir, dmsg);
03797    obj = ast_odbc_request_obj(odbc_database, 0);
03798    if (obj) {
03799       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03800       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03801       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);
03802       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03803       if (!stmt)
03804          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03805       else
03806          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03807       ast_odbc_release_obj(obj);
03808    } else
03809       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03810    return;  
03811 }
03812 
03813 struct insert_data {
03814    char *sql;
03815    const char *dir;
03816    const char *msgnums;
03817    void *data;
03818    SQLLEN datalen;
03819    SQLLEN indlen;
03820    const char *context;
03821    const char *macrocontext;
03822    const char *callerid;
03823    const char *origtime;
03824    const char *duration;
03825    const char *mailboxuser;
03826    const char *mailboxcontext;
03827    const char *category;
03828    const char *flag;
03829 };
03830 
03831 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03832 {
03833    struct insert_data *data = vdata;
03834    int res;
03835    SQLHSTMT stmt;
03836 
03837    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03838    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03839       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03840       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03841       return NULL;
03842    }
03843 
03844    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03845    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03846    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03847    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03848    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03849    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03850    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03851    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03852    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03853    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03854    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03855    if (!ast_strlen_zero(data->category)) {
03856       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03857    }
03858    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03859    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03860       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03861       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03862       return NULL;
03863    }
03864 
03865    return stmt;
03866 }
03867 
03868 /*!
03869  * \brief Stores a voicemail into the database.
03870  * \param dir the folder the mailbox folder to store the message.
03871  * \param mailboxuser the user owning the mailbox folder.
03872  * \param mailboxcontext
03873  * \param msgnum the message index for the message to be stored.
03874  *
03875  * This method is used when mailboxes are stored in an ODBC back end.
03876  * The message sound file and information file is looked up on the file system. 
03877  * A SQL query is invoked to store the message into the (MySQL) database.
03878  *
03879  * \return the zero on success -1 on error.
03880  */
03881 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03882 {
03883    int res = 0;
03884    int fd = -1;
03885    void *fdm = MAP_FAILED;
03886    size_t fdlen = -1;
03887    SQLHSTMT stmt;
03888    char sql[PATH_MAX];
03889    char msgnums[20];
03890    char fn[PATH_MAX];
03891    char full_fn[PATH_MAX];
03892    char fmt[80]="";
03893    char *c;
03894    struct ast_config *cfg = NULL;
03895    struct odbc_obj *obj;
03896    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03897       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03898    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03899 
03900    delete_file(dir, msgnum);
03901    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03902       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03903       return -1;
03904    }
03905 
03906    do {
03907       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03908       c = strchr(fmt, '|');
03909       if (c)
03910          *c = '\0';
03911       if (!strcasecmp(fmt, "wav49"))
03912          strcpy(fmt, "WAV");
03913       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03914       if (msgnum > -1)
03915          make_file(fn, sizeof(fn), dir, msgnum);
03916       else
03917          ast_copy_string(fn, dir, sizeof(fn));
03918       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03919       cfg = ast_config_load(full_fn, config_flags);
03920       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03921       fd = open(full_fn, O_RDWR);
03922       if (fd < 0) {
03923          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03924          res = -1;
03925          break;
03926       }
03927       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03928          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03929             idata.context = "";
03930          }
03931          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03932             idata.macrocontext = "";
03933          }
03934          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03935             idata.callerid = "";
03936          }
03937          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03938             idata.origtime = "";
03939          }
03940          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03941             idata.duration = "";
03942          }
03943          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03944             idata.category = "";
03945          }
03946          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03947             idata.flag = "";
03948          }
03949       }
03950       fdlen = lseek(fd, 0, SEEK_END);
03951       lseek(fd, 0, SEEK_SET);
03952       printf("Length is %zd\n", fdlen);
03953       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03954       if (fdm == MAP_FAILED) {
03955          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03956          res = -1;
03957          break;
03958       } 
03959       idata.data = fdm;
03960       idata.datalen = idata.indlen = fdlen;
03961 
03962       if (!ast_strlen_zero(idata.category)) 
03963          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03964       else
03965          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03966 
03967       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03968          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03969       } else {
03970          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03971          res = -1;
03972       }
03973    } while (0);
03974    if (obj) {
03975       ast_odbc_release_obj(obj);
03976    }
03977    if (cfg)
03978       ast_config_destroy(cfg);
03979    if (fdm != MAP_FAILED)
03980       munmap(fdm, fdlen);
03981    if (fd > -1)
03982       close(fd);
03983    return res;
03984 }
03985 
03986 /*!
03987  * \brief Renames a message in a mailbox folder.
03988  * \param sdir The folder of the message to be renamed.
03989  * \param smsg The index of the message to be renamed.
03990  * \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.
03991  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03992  * \param ddir The destination folder for the message to be renamed into
03993  * \param dmsg The destination message for the message to be renamed.
03994  *
03995  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03996  * 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.
03997  * 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.
03998  */
03999 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
04000 {
04001    SQLHSTMT stmt;
04002    char sql[PATH_MAX];
04003    char msgnums[20];
04004    char msgnumd[20];
04005    struct odbc_obj *obj;
04006    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
04007    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
04008 
04009    delete_file(ddir, dmsg);
04010    obj = ast_odbc_request_obj(odbc_database, 0);
04011    if (obj) {
04012       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
04013       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
04014       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
04015       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04016       if (!stmt)
04017          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04018       else
04019          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04020       ast_odbc_release_obj(obj);
04021    } else
04022       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04023    return;  
04024 }
04025 
04026 /*!
04027  * \brief Removes a voicemail message file.
04028  * \param dir the path to the message file.
04029  * \param msgnum the unique number for the message within the mailbox.
04030  *
04031  * Removes the message content file and the information file.
04032  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
04033  * Typical use is to clean up after a RETRIEVE operation. 
04034  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
04035  * \return zero on success, -1 on error.
04036  */
04037 static int remove_file(char *dir, int msgnum)
04038 {
04039    char fn[PATH_MAX];
04040    char full_fn[PATH_MAX];
04041    char msgnums[80];
04042    
04043    if (msgnum > -1) {
04044       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
04045       make_file(fn, sizeof(fn), dir, msgnum);
04046    } else
04047       ast_copy_string(fn, dir, sizeof(fn));
04048    ast_filedelete(fn, NULL);  
04049    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
04050    unlink(full_fn);
04051    return 0;
04052 }
04053 #else
04054 #ifndef IMAP_STORAGE
04055 /*!
04056  * \brief Find all .txt files - even if they are not in sequence from 0000.
04057  * \param vmu
04058  * \param dir
04059  *
04060  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04061  *
04062  * \return the count of messages, zero or more.
04063  */
04064 static int count_messages(struct ast_vm_user *vmu, char *dir)
04065 {
04066 
04067    int vmcount = 0;
04068    DIR *vmdir = NULL;
04069    struct dirent *vment = NULL;
04070 
04071    if (vm_lock_path(dir))
04072       return ERROR_LOCK_PATH;
04073 
04074    if ((vmdir = opendir(dir))) {
04075       while ((vment = readdir(vmdir))) {
04076          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04077             vmcount++;
04078          }
04079       }
04080       closedir(vmdir);
04081    }
04082    ast_unlock_path(dir);
04083    
04084    return vmcount;
04085 }
04086 
04087 /*!
04088  * \brief Renames a message in a mailbox folder.
04089  * \param sfn The path to the mailbox information and data file to be renamed.
04090  * \param dfn The path for where the message data and information files will be renamed to.
04091  *
04092  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04093  */
04094 static void rename_file(char *sfn, char *dfn)
04095 {
04096    char stxt[PATH_MAX];
04097    char dtxt[PATH_MAX];
04098    ast_filerename(sfn, dfn, NULL);
04099    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04100    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04101    if (ast_check_realtime("voicemail_data")) {
04102       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04103    }
04104    rename(stxt, dtxt);
04105 }
04106 
04107 /*! 
04108  * \brief Determines the highest message number in use for a given user and mailbox folder.
04109  * \param vmu 
04110  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04111  *
04112  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04113  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04114  *
04115  * \note Should always be called with a lock already set on dir.
04116  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04117  */
04118 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04119 {
04120    int x;
04121    unsigned char map[MAXMSGLIMIT] = "";
04122    DIR *msgdir;
04123    struct dirent *msgdirent;
04124    int msgdirint;
04125    char extension[4];
04126    int stopcount = 0;
04127 
04128    /* Reading the entire directory into a file map scales better than
04129     * doing a stat repeatedly on a predicted sequence.  I suspect this
04130     * is partially due to stat(2) internally doing a readdir(2) itself to
04131     * find each file. */
04132    if (!(msgdir = opendir(dir))) {
04133       return -1;
04134    }
04135 
04136    while ((msgdirent = readdir(msgdir))) {
04137       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04138          map[msgdirint] = 1;
04139          stopcount++;
04140          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04141       }
04142    }
04143    closedir(msgdir);
04144 
04145    for (x = 0; x < vmu->maxmsg; x++) {
04146       if (map[x] == 1) {
04147          stopcount--;
04148       } else if (map[x] == 0 && !stopcount) {
04149          break;
04150       }
04151    }
04152 
04153    return x - 1;
04154 }
04155 
04156 #endif /* #ifndef IMAP_STORAGE */
04157 #endif /* #else of #ifdef ODBC_STORAGE */
04158 #ifndef IMAP_STORAGE
04159 /*!
04160  * \brief Utility function to copy a file.
04161  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04162  * \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.
04163  *
04164  * 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.
04165  * The copy operation copies up to 4096 bytes at once.
04166  *
04167  * \return zero on success, -1 on error.
04168  */
04169 static int copy(char *infile, char *outfile)
04170 {
04171    int ifd;
04172    int ofd;
04173    int res;
04174    int len;
04175    char buf[4096];
04176 
04177 #ifdef HARDLINK_WHEN_POSSIBLE
04178    /* Hard link if possible; saves disk space & is faster */
04179    if (link(infile, outfile)) {
04180 #endif
04181       if ((ifd = open(infile, O_RDONLY)) < 0) {
04182          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04183          return -1;
04184       }
04185       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04186          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04187          close(ifd);
04188          return -1;
04189       }
04190       do {
04191          len = read(ifd, buf, sizeof(buf));
04192          if (len < 0) {
04193             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04194             close(ifd);
04195             close(ofd);
04196             unlink(outfile);
04197          }
04198          if (len) {
04199             res = write(ofd, buf, len);
04200             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04201                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04202                close(ifd);
04203                close(ofd);
04204                unlink(outfile);
04205             }
04206          }
04207       } while (len);
04208       close(ifd);
04209       close(ofd);
04210       return 0;
04211 #ifdef HARDLINK_WHEN_POSSIBLE
04212    } else {
04213       /* Hard link succeeded */
04214       return 0;
04215    }
04216 #endif
04217 }
04218 
04219 /*!
04220  * \brief Copies a voicemail information (envelope) file.
04221  * \param frompath
04222  * \param topath 
04223  *
04224  * Every voicemail has the data (.wav) file, and the information file.
04225  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04226  * This is used by the COPY macro when not using IMAP storage.
04227  */
04228 static void copy_plain_file(char *frompath, char *topath)
04229 {
04230    char frompath2[PATH_MAX], topath2[PATH_MAX];
04231    struct ast_variable *tmp,*var = NULL;
04232    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04233    ast_filecopy(frompath, topath, NULL);
04234    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04235    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04236    if (ast_check_realtime("voicemail_data")) {
04237       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04238       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04239       for (tmp = var; tmp; tmp = tmp->next) {
04240          if (!strcasecmp(tmp->name, "origmailbox")) {
04241             origmailbox = tmp->value;
04242          } else if (!strcasecmp(tmp->name, "context")) {
04243             context = tmp->value;
04244          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04245             macrocontext = tmp->value;
04246          } else if (!strcasecmp(tmp->name, "exten")) {
04247             exten = tmp->value;
04248          } else if (!strcasecmp(tmp->name, "priority")) {
04249             priority = tmp->value;
04250          } else if (!strcasecmp(tmp->name, "callerchan")) {
04251             callerchan = tmp->value;
04252          } else if (!strcasecmp(tmp->name, "callerid")) {
04253             callerid = tmp->value;
04254          } else if (!strcasecmp(tmp->name, "origdate")) {
04255             origdate = tmp->value;
04256          } else if (!strcasecmp(tmp->name, "origtime")) {
04257             origtime = tmp->value;
04258          } else if (!strcasecmp(tmp->name, "category")) {
04259             category = tmp->value;
04260          } else if (!strcasecmp(tmp->name, "duration")) {
04261             duration = tmp->value;
04262          }
04263       }
04264       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);
04265    }
04266    copy(frompath2, topath2);
04267    ast_variables_destroy(var);
04268 }
04269 #endif
04270 
04271 /*! 
04272  * \brief Removes the voicemail sound and information file.
04273  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04274  *
04275  * This is used by the DELETE macro when voicemails are stored on the file system.
04276  *
04277  * \return zero on success, -1 on error.
04278  */
04279 static int vm_delete(char *file)
04280 {
04281    char *txt;
04282    int txtsize = 0;
04283 
04284    txtsize = (strlen(file) + 5)*sizeof(char);
04285    txt = alloca(txtsize);
04286    /* Sprintf here would safe because we alloca'd exactly the right length,
04287     * but trying to eliminate all sprintf's anyhow
04288     */
04289    if (ast_check_realtime("voicemail_data")) {
04290       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04291    }
04292    snprintf(txt, txtsize, "%s.txt", file);
04293    unlink(txt);
04294    return ast_filedelete(file, NULL);
04295 }
04296 
04297 /*!
04298  * \brief utility used by inchar(), for base_encode()
04299  */
04300 static int inbuf(struct baseio *bio, FILE *fi)
04301 {
04302    int l;
04303 
04304    if (bio->ateof)
04305       return 0;
04306 
04307    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04308       if (ferror(fi))
04309          return -1;
04310 
04311       bio->ateof = 1;
04312       return 0;
04313    }
04314 
04315    bio->iolen = l;
04316    bio->iocp = 0;
04317 
04318    return 1;
04319 }
04320 
04321 /*!
04322  * \brief utility used by base_encode()
04323  */
04324 static int inchar(struct baseio *bio, FILE *fi)
04325 {
04326    if (bio->iocp>=bio->iolen) {
04327       if (!inbuf(bio, fi))
04328          return EOF;
04329    }
04330 
04331    return bio->iobuf[bio->iocp++];
04332 }
04333 
04334 /*!
04335  * \brief utility used by base_encode()
04336  */
04337 static int ochar(struct baseio *bio, int c, FILE *so)
04338 {
04339    if (bio->linelength >= BASELINELEN) {
04340       if (fputs(ENDL, so) == EOF) {
04341          return -1;
04342       }
04343 
04344       bio->linelength = 0;
04345    }
04346 
04347    if (putc(((unsigned char) c), so) == EOF) {
04348       return -1;
04349    }
04350 
04351    bio->linelength++;
04352 
04353    return 1;
04354 }
04355 
04356 /*!
04357  * \brief Performs a base 64 encode algorithm on the contents of a File
04358  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04359  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04360  *
04361  * 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 ?
04362  *
04363  * \return zero on success, -1 on error.
04364  */
04365 static int base_encode(char *filename, FILE *so)
04366 {
04367    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04368       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04369       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04370       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04371    int i, hiteof = 0;
04372    FILE *fi;
04373    struct baseio bio;
04374 
04375    memset(&bio, 0, sizeof(bio));
04376    bio.iocp = BASEMAXINLINE;
04377 
04378    if (!(fi = fopen(filename, "rb"))) {
04379       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04380       return -1;
04381    }
04382 
04383    while (!hiteof){
04384       unsigned char igroup[3], ogroup[4];
04385       int c, n;
04386 
04387       memset(igroup, 0, sizeof(igroup));
04388 
04389       for (n = 0; n < 3; n++) {
04390          if ((c = inchar(&bio, fi)) == EOF) {
04391             hiteof = 1;
04392             break;
04393          }
04394 
04395          igroup[n] = (unsigned char) c;
04396       }
04397 
04398       if (n > 0) {
04399          ogroup[0]= dtable[igroup[0] >> 2];
04400          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04401          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04402          ogroup[3]= dtable[igroup[2] & 0x3F];
04403 
04404          if (n < 3) {
04405             ogroup[3] = '=';
04406 
04407             if (n < 2)
04408                ogroup[2] = '=';
04409          }
04410 
04411          for (i = 0; i < 4; i++)
04412             ochar(&bio, ogroup[i], so);
04413       }
04414    }
04415 
04416    fclose(fi);
04417    
04418    if (fputs(ENDL, so) == EOF) {
04419       return 0;
04420    }
04421 
04422    return 1;
04423 }
04424 
04425 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)
04426 {
04427    char callerid[256];
04428    char num[12];
04429    char fromdir[256], fromfile[256];
04430    struct ast_config *msg_cfg;
04431    const char *origcallerid, *origtime;
04432    char origcidname[80], origcidnum[80], origdate[80];
04433    int inttime;
04434    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04435 
04436    /* Prepare variables for substitution in email body and subject */
04437    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04438    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04439    snprintf(num, sizeof(num), "%d", msgnum);
04440    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04441    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04442    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04443    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04444       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04445    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04446    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04447    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04448    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04449    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04450 
04451    /* Retrieve info from VM attribute file */
04452    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04453    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04454    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04455       strcat(fromfile, ".txt");
04456    }
04457    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04458       ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
04459       return;
04460    }
04461 
04462    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04463       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04464       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04465       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04466       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04467    }
04468 
04469    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04470       struct timeval tv = { inttime, };
04471       struct ast_tm tm;
04472       ast_localtime(&tv, &tm, NULL);
04473       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04474       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04475    }
04476    ast_config_destroy(msg_cfg);
04477 }
04478 
04479 /*!
04480  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04481  * \param from The string to work with.
04482  * \param buf The buffer into which to write the modified quoted string.
04483  * \param maxlen Always zero, but see \see ast_str
04484  * 
04485  * \return The destination string with quotes wrapped on it (the to field).
04486  */
04487 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04488 {
04489    const char *ptr;
04490 
04491    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04492    ast_str_set(buf, maxlen, "\"");
04493    for (ptr = from; *ptr; ptr++) {
04494       if (*ptr == '"' || *ptr == '\\') {
04495          ast_str_append(buf, maxlen, "\\%c", *ptr);
04496       } else {
04497          ast_str_append(buf, maxlen, "%c", *ptr);
04498       }
04499    }
04500    ast_str_append(buf, maxlen, "\"");
04501 
04502    return ast_str_buffer(*buf);
04503 }
04504 
04505 /*! \brief
04506  * fill in *tm for current time according to the proper timezone, if any.
04507  * \return tm so it can be used as a function argument.
04508  */
04509 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04510 {
04511    const struct vm_zone *z = NULL;
04512    struct timeval t = ast_tvnow();
04513 
04514    /* Does this user have a timezone specified? */
04515    if (!ast_strlen_zero(vmu->zonetag)) {
04516       /* Find the zone in the list */
04517       AST_LIST_LOCK(&zones);
04518       AST_LIST_TRAVERSE(&zones, z, list) {
04519          if (!strcmp(z->name, vmu->zonetag))
04520             break;
04521       }
04522       AST_LIST_UNLOCK(&zones);
04523    }
04524    ast_localtime(&t, tm, z ? z->timezone : NULL);
04525    return tm;
04526 }
04527 
04528 /*!\brief Check if the string would need encoding within the MIME standard, to
04529  * avoid confusing certain mail software that expects messages to be 7-bit
04530  * clean.
04531  */
04532 static int check_mime(const char *str)
04533 {
04534    for (; *str; str++) {
04535       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04536          return 1;
04537       }
04538    }
04539    return 0;
04540 }
04541 
04542 /*!\brief Encode a string according to the MIME rules for encoding strings
04543  * that are not 7-bit clean or contain control characters.
04544  *
04545  * Additionally, if the encoded string would exceed the MIME limit of 76
04546  * characters per line, then the encoding will be broken up into multiple
04547  * sections, separated by a space character, in order to facilitate
04548  * breaking up the associated header across multiple lines.
04549  *
04550  * \param end An expandable buffer for holding the result
04551  * \param maxlen Always zero, but see \see ast_str
04552  * \param start A string to be encoded
04553  * \param preamble The length of the first line already used for this string,
04554  * to ensure that each line maintains a maximum length of 76 chars.
04555  * \param postamble the length of any additional characters appended to the
04556  * line, used to ensure proper field wrapping.
04557  * \retval The encoded string.
04558  */
04559 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04560 {
04561    struct ast_str *tmp = ast_str_alloca(80);
04562    int first_section = 1;
04563 
04564    ast_str_reset(*end);
04565    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04566    for (; *start; start++) {
04567       int need_encoding = 0;
04568       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04569          need_encoding = 1;
04570       }
04571       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04572          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04573          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04574          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04575          /* Start new line */
04576          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04577          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04578          first_section = 0;
04579       }
04580       if (need_encoding && *start == ' ') {
04581          ast_str_append(&tmp, -1, "_");
04582       } else if (need_encoding) {
04583          ast_str_append(&tmp, -1, "=%hhX", *start);
04584       } else {
04585          ast_str_append(&tmp, -1, "%c", *start);
04586       }
04587    }
04588    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04589    return ast_str_buffer(*end);
04590 }
04591 
04592 /*!
04593  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04594  * \param p The output file to generate the email contents into.
04595  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04596  * \param vmu The voicemail user who is sending the voicemail.
04597  * \param msgnum The message index in the mailbox folder.
04598  * \param context 
04599  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04600  * \param fromfolder
04601  * \param cidnum The caller ID number.
04602  * \param cidname The caller ID name.
04603  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04604  * \param attach2 
04605  * \param format The message sound file format. i.e. .wav
04606  * \param duration The time of the message content, in seconds.
04607  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04608  * \param chan
04609  * \param category
04610  * \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.
04611  * \param flag
04612  *
04613  * 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.
04614  */
04615 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)
04616 {
04617    char date[256];
04618    char host[MAXHOSTNAMELEN] = "";
04619    char who[256];
04620    char bound[256];
04621    char dur[256];
04622    struct ast_tm tm;
04623    char enc_cidnum[256] = "", enc_cidname[256] = "";
04624    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04625    char *greeting_attachment; 
04626    char filename[256];
04627 
04628    if (!str1 || !str2) {
04629       ast_free(str1);
04630       ast_free(str2);
04631       return;
04632    }
04633 
04634    if (cidnum) {
04635       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04636    }
04637    if (cidname) {
04638       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04639    }
04640    gethostname(host, sizeof(host) - 1);
04641 
04642    if (strchr(srcemail, '@')) {
04643       ast_copy_string(who, srcemail, sizeof(who));
04644    } else {
04645       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04646    }
04647 
04648    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04649    if (greeting_attachment) {
04650       *greeting_attachment++ = '\0';
04651    }
04652 
04653    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04654    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04655    fprintf(p, "Date: %s" ENDL, date);
04656 
04657    /* Set date format for voicemail mail */
04658    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04659 
04660    if (!ast_strlen_zero(fromstring)) {
04661       struct ast_channel *ast;
04662       if ((ast = ast_dummy_channel_alloc())) {
04663          char *ptr;
04664          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04665          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04666 
04667          if (check_mime(ast_str_buffer(str1))) {
04668             int first_line = 1;
04669             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04670             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04671                *ptr = '\0';
04672                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04673                first_line = 0;
04674                /* Substring is smaller, so this will never grow */
04675                ast_str_set(&str2, 0, "%s", ptr + 1);
04676             }
04677             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04678          } else {
04679             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04680          }
04681          ast = ast_channel_unref(ast);
04682       } else {
04683          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04684       }
04685    } else {
04686       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04687    }
04688 
04689    if (check_mime(vmu->fullname)) {
04690       int first_line = 1;
04691       char *ptr;
04692       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04693       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04694          *ptr = '\0';
04695          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04696          first_line = 0;
04697          /* Substring is smaller, so this will never grow */
04698          ast_str_set(&str2, 0, "%s", ptr + 1);
04699       }
04700       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04701    } else {
04702       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04703    }
04704 
04705    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04706       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04707       struct ast_channel *ast;
04708       if ((ast = ast_dummy_channel_alloc())) {
04709          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04710          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04711          if (check_mime(ast_str_buffer(str1))) {
04712             int first_line = 1;
04713             char *ptr;
04714             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04715             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04716                *ptr = '\0';
04717                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04718                first_line = 0;
04719                /* Substring is smaller, so this will never grow */
04720                ast_str_set(&str2, 0, "%s", ptr + 1);
04721             }
04722             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04723          } else {
04724             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04725          }
04726          ast = ast_channel_unref(ast);
04727       } else {
04728          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04729       }
04730    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04731       if (ast_strlen_zero(flag)) {
04732          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04733       } else {
04734          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04735       }
04736    } else {
04737       if (ast_strlen_zero(flag)) {
04738          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04739       } else {
04740          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04741       }
04742    }
04743 
04744    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04745       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04746    if (imap) {
04747       /* additional information needed for IMAP searching */
04748       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04749       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04750       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04751       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04752 #ifdef IMAP_STORAGE
04753       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04754 #else
04755       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04756 #endif
04757       /* flag added for Urgent */
04758       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04759       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04760       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, ast_channel_name(chan));
04761       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04762       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04763       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04764       if (!ast_strlen_zero(category)) {
04765          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04766       } else {
04767          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04768       }
04769       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04770       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04771       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04772    }
04773    if (!ast_strlen_zero(cidnum)) {
04774       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04775    }
04776    if (!ast_strlen_zero(cidname)) {
04777       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04778    }
04779    fprintf(p, "MIME-Version: 1.0" ENDL);
04780    if (attach_user_voicemail) {
04781       /* Something unique. */
04782       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04783          (int) getpid(), (unsigned int) ast_random());
04784 
04785       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04786       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04787       fprintf(p, "--%s" ENDL, bound);
04788    }
04789    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04790    if (emailbody || vmu->emailbody) {
04791       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04792       struct ast_channel *ast;
04793       if ((ast = ast_dummy_channel_alloc())) {
04794          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04795          ast_str_substitute_variables(&str1, 0, ast, e_body);
04796 #ifdef IMAP_STORAGE
04797             {
04798                /* Convert body to native line terminators for IMAP backend */
04799                char *line = ast_str_buffer(str1), *next;
04800                do {
04801                   /* Terminate line before outputting it to the file */
04802                   if ((next = strchr(line, '\n'))) {
04803                      *next++ = '\0';
04804                   }
04805                   fprintf(p, "%s" ENDL, line);
04806                   line = next;
04807                } while (!ast_strlen_zero(line));
04808             }
04809 #else
04810          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04811 #endif
04812          ast = ast_channel_unref(ast);
04813       } else {
04814          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04815       }
04816    } else if (msgnum > -1) {
04817       if (strcmp(vmu->mailbox, mailbox)) {
04818          /* Forwarded type */
04819          struct ast_config *msg_cfg;
04820          const char *v;
04821          int inttime;
04822          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04823          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04824          /* Retrieve info from VM attribute file */
04825          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04826          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04827          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04828             strcat(fromfile, ".txt");
04829          }
04830          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04831             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04832                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04833             }
04834 
04835             /* You might be tempted to do origdate, except that a) it's in the wrong
04836              * format, and b) it's missing for IMAP recordings. */
04837             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04838                struct timeval tv = { inttime, };
04839                struct ast_tm tm;
04840                ast_localtime(&tv, &tm, NULL);
04841                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04842             }
04843             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04844                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04845                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04846                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04847                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04848                date, origcallerid, origdate);
04849             ast_config_destroy(msg_cfg);
04850          } else {
04851             goto plain_message;
04852          }
04853       } else {
04854 plain_message:
04855          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04856             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04857             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04858             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04859             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04860       }
04861    } else {
04862       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04863             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04864    }
04865 
04866    if (imap || attach_user_voicemail) {
04867       if (!ast_strlen_zero(attach2)) {
04868          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04869          ast_debug(5, "creating second attachment filename %s\n", filename);
04870          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04871          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04872          ast_debug(5, "creating attachment filename %s\n", filename);
04873          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04874       } else {
04875          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04876          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04877          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04878       }
04879    }
04880    ast_free(str1);
04881    ast_free(str2);
04882 }
04883 
04884 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)
04885 {
04886    char tmpdir[256], newtmp[256];
04887    char fname[256];
04888    char tmpcmd[256];
04889    int tmpfd = -1;
04890    int soxstatus = 0;
04891 
04892    /* Eww. We want formats to tell us their own MIME type */
04893    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04894 
04895    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04896       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04897       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04898       tmpfd = mkstemp(newtmp);
04899       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04900       ast_debug(3, "newtmp: %s\n", newtmp);
04901       if (tmpfd > -1) {
04902          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04903          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04904             attach = newtmp;
04905             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04906          } else {
04907             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04908                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04909             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04910          }
04911       }
04912    }
04913    fprintf(p, "--%s" ENDL, bound);
04914    if (msgnum > -1)
04915       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04916    else
04917       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04918    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04919    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04920    if (msgnum > -1)
04921       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04922    else
04923       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04924    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04925    base_encode(fname, p);
04926    if (last)
04927       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04928    if (tmpfd > -1) {
04929       if (soxstatus == 0) {
04930          unlink(fname);
04931       }
04932       close(tmpfd);
04933       unlink(newtmp);
04934    }
04935    return 0;
04936 }
04937 
04938 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)
04939 {
04940    FILE *p = NULL;
04941    char tmp[80] = "/tmp/astmail-XXXXXX";
04942    char tmp2[256];
04943    char *stringp;
04944 
04945    if (vmu && ast_strlen_zero(vmu->email)) {
04946       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04947       return(0);
04948    }
04949 
04950    /* Mail only the first format */
04951    format = ast_strdupa(format);
04952    stringp = format;
04953    strsep(&stringp, "|");
04954 
04955    if (!strcmp(format, "wav49"))
04956       format = "WAV";
04957    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));
04958    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04959       command hangs */
04960    if ((p = vm_mkftemp(tmp)) == NULL) {
04961       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04962       return -1;
04963    } else {
04964       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04965       fclose(p);
04966       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04967       ast_safe_system(tmp2);
04968       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04969    }
04970    return 0;
04971 }
04972 
04973 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)
04974 {
04975    char enc_cidnum[256], enc_cidname[256];
04976    char date[256];
04977    char host[MAXHOSTNAMELEN] = "";
04978    char who[256];
04979    char dur[PATH_MAX];
04980    char tmp[80] = "/tmp/astmail-XXXXXX";
04981    char tmp2[PATH_MAX];
04982    struct ast_tm tm;
04983    FILE *p;
04984    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04985 
04986    if (!str1 || !str2) {
04987       ast_free(str1);
04988       ast_free(str2);
04989       return -1;
04990    }
04991 
04992    if (cidnum) {
04993       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04994    }
04995    if (cidname) {
04996       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04997    }
04998 
04999    if ((p = vm_mkftemp(tmp)) == NULL) {
05000       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
05001       ast_free(str1);
05002       ast_free(str2);
05003       return -1;
05004    }
05005    gethostname(host, sizeof(host)-1);
05006    if (strchr(srcemail, '@')) {
05007       ast_copy_string(who, srcemail, sizeof(who));
05008    } else {
05009       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
05010    }
05011    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
05012    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
05013    fprintf(p, "Date: %s\n", date);
05014 
05015    /* Reformat for custom pager format */
05016    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
05017 
05018    if (!ast_strlen_zero(pagerfromstring)) {
05019       struct ast_channel *ast;
05020       if ((ast = ast_dummy_channel_alloc())) {
05021          char *ptr;
05022          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
05023          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
05024 
05025          if (check_mime(ast_str_buffer(str1))) {
05026             int first_line = 1;
05027             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
05028             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05029                *ptr = '\0';
05030                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
05031                first_line = 0;
05032                /* Substring is smaller, so this will never grow */
05033                ast_str_set(&str2, 0, "%s", ptr + 1);
05034             }
05035             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
05036          } else {
05037             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
05038          }
05039          ast = ast_channel_unref(ast);
05040       } else {
05041          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05042       }
05043    } else {
05044       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
05045    }
05046 
05047    if (check_mime(vmu->fullname)) {
05048       int first_line = 1;
05049       char *ptr;
05050       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
05051       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05052          *ptr = '\0';
05053          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
05054          first_line = 0;
05055          /* Substring is smaller, so this will never grow */
05056          ast_str_set(&str2, 0, "%s", ptr + 1);
05057       }
05058       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
05059    } else {
05060       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
05061    }
05062 
05063    if (!ast_strlen_zero(pagersubject)) {
05064       struct ast_channel *ast;
05065       if ((ast = ast_dummy_channel_alloc())) {
05066          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05067          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05068          if (check_mime(ast_str_buffer(str1))) {
05069             int first_line = 1;
05070             char *ptr;
05071             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05072             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05073                *ptr = '\0';
05074                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05075                first_line = 0;
05076                /* Substring is smaller, so this will never grow */
05077                ast_str_set(&str2, 0, "%s", ptr + 1);
05078             }
05079             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05080          } else {
05081             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05082          }
05083          ast = ast_channel_unref(ast);
05084       } else {
05085          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05086       }
05087    } else {
05088       if (ast_strlen_zero(flag)) {
05089          fprintf(p, "Subject: New VM\n\n");
05090       } else {
05091          fprintf(p, "Subject: New %s VM\n\n", flag);
05092       }
05093    }
05094 
05095    if (pagerbody) {
05096       struct ast_channel *ast;
05097       if ((ast = ast_dummy_channel_alloc())) {
05098          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05099          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05100          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05101          ast = ast_channel_unref(ast);
05102       } else {
05103          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05104       }
05105    } else {
05106       fprintf(p, "New %s long %s msg in box %s\n"
05107             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05108    }
05109 
05110    fclose(p);
05111    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05112    ast_safe_system(tmp2);
05113    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05114    ast_free(str1);
05115    ast_free(str2);
05116    return 0;
05117 }
05118 
05119 /*!
05120  * \brief Gets the current date and time, as formatted string.
05121  * \param s The buffer to hold the output formatted date.
05122  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05123  * 
05124  * The date format string used is "%a %b %e %r UTC %Y".
05125  * 
05126  * \return zero on success, -1 on error.
05127  */
05128 static int get_date(char *s, int len)
05129 {
05130    struct ast_tm tm;
05131    struct timeval t = ast_tvnow();
05132 
05133    ast_localtime(&t, &tm, "UTC");
05134 
05135    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05136 }
05137 
05138 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05139 {
05140    int res;
05141    char fn[PATH_MAX];
05142    char dest[PATH_MAX];
05143 
05144    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05145 
05146    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05147       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05148       return -1;
05149    }
05150 
05151    RETRIEVE(fn, -1, ext, context);
05152    if (ast_fileexists(fn, NULL, NULL) > 0) {
05153       res = ast_stream_and_wait(chan, fn, ecodes);
05154       if (res) {
05155          DISPOSE(fn, -1);
05156          return res;
05157       }
05158    } else {
05159       /* Dispose just in case */
05160       DISPOSE(fn, -1);
05161       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05162       if (res)
05163          return res;
05164       res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
05165       if (res)
05166          return res;
05167    }
05168    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05169    return res;
05170 }
05171 
05172 static void free_zone(struct vm_zone *z)
05173 {
05174    ast_free(z);
05175 }
05176 
05177 #ifdef ODBC_STORAGE
05178 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05179 {
05180    int x = -1;
05181    int res;
05182    SQLHSTMT stmt = NULL;
05183    char sql[PATH_MAX];
05184    char rowdata[20];
05185    char tmp[PATH_MAX] = "";
05186    struct odbc_obj *obj = NULL;
05187    char *context;
05188    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05189 
05190    if (newmsgs)
05191       *newmsgs = 0;
05192    if (oldmsgs)
05193       *oldmsgs = 0;
05194    if (urgentmsgs)
05195       *urgentmsgs = 0;
05196 
05197    /* If no mailbox, return immediately */
05198    if (ast_strlen_zero(mailbox))
05199       return 0;
05200 
05201    ast_copy_string(tmp, mailbox, sizeof(tmp));
05202 
05203    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05204       int u, n, o;
05205       char *next, *remaining = tmp;
05206       while ((next = strsep(&remaining, " ,"))) {
05207          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05208             return -1;
05209          }
05210          if (urgentmsgs) {
05211             *urgentmsgs += u;
05212          }
05213          if (newmsgs) {
05214             *newmsgs += n;
05215          }
05216          if (oldmsgs) {
05217             *oldmsgs += o;
05218          }
05219       }
05220       return 0;
05221    }
05222 
05223    context = strchr(tmp, '@');
05224    if (context) {
05225       *context = '\0';
05226       context++;
05227    } else
05228       context = "default";
05229 
05230    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05231       do {
05232          if (newmsgs) {
05233             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05234             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05235                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05236                break;
05237             }
05238             res = SQLFetch(stmt);
05239             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05240                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05241                break;
05242             }
05243             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05244             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05245                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05246                break;
05247             }
05248             *newmsgs = atoi(rowdata);
05249             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05250          }
05251 
05252          if (oldmsgs) {
05253             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05254             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05255                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05256                break;
05257             }
05258             res = SQLFetch(stmt);
05259             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05260                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05261                break;
05262             }
05263             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05264             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05265                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05266                break;
05267             }
05268             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05269             *oldmsgs = atoi(rowdata);
05270          }
05271 
05272          if (urgentmsgs) {
05273             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05274             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05275                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05276                break;
05277             }
05278             res = SQLFetch(stmt);
05279             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05280                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05281                break;
05282             }
05283             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05284             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05285                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05286                break;
05287             }
05288             *urgentmsgs = atoi(rowdata);
05289          }
05290 
05291          x = 0;
05292       } while (0);
05293    } else {
05294       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05295    }
05296 
05297    if (stmt) {
05298       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05299    }
05300    if (obj) {
05301       ast_odbc_release_obj(obj);
05302    }
05303    return x;
05304 }
05305 
05306 /*!
05307  * \brief Gets the number of messages that exist in a mailbox folder.
05308  * \param context
05309  * \param mailbox
05310  * \param folder
05311  * 
05312  * This method is used when ODBC backend is used.
05313  * \return The number of messages in this mailbox folder (zero or more).
05314  */
05315 static int messagecount(const char *context, const char *mailbox, const char *folder)
05316 {
05317    struct odbc_obj *obj = NULL;
05318    int nummsgs = 0;
05319    int res;
05320    SQLHSTMT stmt = NULL;
05321    char sql[PATH_MAX];
05322    char rowdata[20];
05323    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05324 
05325    /* If no mailbox, return immediately */
05326    if (ast_strlen_zero(mailbox)) {
05327       return 0;
05328    }
05329 
05330    if (ast_strlen_zero(folder)) {
05331       folder = "INBOX";
05332    }
05333 
05334    obj = ast_odbc_request_obj(odbc_database, 0);
05335    if (obj) {
05336       if (!strcmp(folder, "INBOX")) {
05337          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);
05338       } else {
05339          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05340       }
05341       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05342       if (!stmt) {
05343          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05344          goto yuck;
05345       }
05346       res = SQLFetch(stmt);
05347       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05348          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05349          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05350          goto yuck;
05351       }
05352       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05353       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05354          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05355          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05356          goto yuck;
05357       }
05358       nummsgs = atoi(rowdata);
05359       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05360    } else
05361       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05362 
05363 yuck:
05364    if (obj)
05365       ast_odbc_release_obj(obj);
05366    return nummsgs;
05367 }
05368 
05369 /** 
05370  * \brief Determines if the given folder has messages.
05371  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05372  * 
05373  * This function is used when the mailbox is stored in an ODBC back end.
05374  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05375  * \return 1 if the folder has one or more messages. zero otherwise.
05376  */
05377 static int has_voicemail(const char *mailbox, const char *folder)
05378 {
05379    char tmp[256], *tmp2 = tmp, *box, *context;
05380    ast_copy_string(tmp, mailbox, sizeof(tmp));
05381    while ((context = box = strsep(&tmp2, ",&"))) {
05382       strsep(&context, "@");
05383       if (ast_strlen_zero(context))
05384          context = "default";
05385       if (messagecount(context, box, folder))
05386          return 1;
05387    }
05388    return 0;
05389 }
05390 #endif
05391 #ifndef IMAP_STORAGE
05392 /*! 
05393  * \brief Copies a message from one mailbox to another.
05394  * \param chan
05395  * \param vmu
05396  * \param imbox
05397  * \param msgnum
05398  * \param duration
05399  * \param recip
05400  * \param fmt
05401  * \param dir
05402  * \param flag
05403  *
05404  * This is only used by file storage based mailboxes.
05405  *
05406  * \return zero on success, -1 on error.
05407  */
05408 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)
05409 {
05410    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05411    const char *frombox = mbox(vmu, imbox);
05412    const char *userfolder;
05413    int recipmsgnum;
05414    int res = 0;
05415 
05416    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05417 
05418    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05419       userfolder = "Urgent";
05420    } else {
05421       userfolder = "INBOX";
05422    }
05423 
05424    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05425 
05426    if (!dir)
05427       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05428    else
05429       ast_copy_string(fromdir, dir, sizeof(fromdir));
05430 
05431    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05432    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05433 
05434    if (vm_lock_path(todir))
05435       return ERROR_LOCK_PATH;
05436 
05437    recipmsgnum = last_message_index(recip, todir) + 1;
05438    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05439       make_file(topath, sizeof(topath), todir, recipmsgnum);
05440 #ifndef ODBC_STORAGE
05441       if (EXISTS(fromdir, msgnum, frompath, ast_channel_language(chan))) { 
05442          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05443       } else {
05444 #endif
05445          /* If we are prepending a message for ODBC, then the message already
05446           * exists in the database, but we want to force copying from the
05447           * filesystem (since only the FS contains the prepend). */
05448          copy_plain_file(frompath, topath);
05449          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05450          vm_delete(topath);
05451 #ifndef ODBC_STORAGE
05452       }
05453 #endif
05454    } else {
05455       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05456       res = -1;
05457    }
05458    ast_unlock_path(todir);
05459    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05460       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05461       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05462       flag);
05463    
05464    return res;
05465 }
05466 #endif
05467 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05468 
05469 static int messagecount(const char *context, const char *mailbox, const char *folder)
05470 {
05471    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05472 }
05473 
05474 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05475 {
05476    DIR *dir;
05477    struct dirent *de;
05478    char fn[256];
05479    int ret = 0;
05480 
05481    /* If no mailbox, return immediately */
05482    if (ast_strlen_zero(mailbox))
05483       return 0;
05484 
05485    if (ast_strlen_zero(folder))
05486       folder = "INBOX";
05487    if (ast_strlen_zero(context))
05488       context = "default";
05489 
05490    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05491 
05492    if (!(dir = opendir(fn)))
05493       return 0;
05494 
05495    while ((de = readdir(dir))) {
05496       if (!strncasecmp(de->d_name, "msg", 3)) {
05497          if (shortcircuit) {
05498             ret = 1;
05499             break;
05500          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05501             ret++;
05502          }
05503       }
05504    }
05505 
05506    closedir(dir);
05507 
05508    return ret;
05509 }
05510 
05511 /** 
05512  * \brief Determines if the given folder has messages.
05513  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05514  * \param folder the folder to look in
05515  *
05516  * This function is used when the mailbox is stored in a filesystem back end.
05517  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05518  * \return 1 if the folder has one or more messages. zero otherwise.
05519  */
05520 static int has_voicemail(const char *mailbox, const char *folder)
05521 {
05522    char tmp[256], *tmp2 = tmp, *box, *context;
05523    ast_copy_string(tmp, mailbox, sizeof(tmp));
05524    if (ast_strlen_zero(folder)) {
05525       folder = "INBOX";
05526    }
05527    while ((box = strsep(&tmp2, ",&"))) {
05528       if ((context = strchr(box, '@')))
05529          *context++ = '\0';
05530       else
05531          context = "default";
05532       if (__has_voicemail(context, box, folder, 1))
05533          return 1;
05534       /* If we are checking INBOX, we should check Urgent as well */
05535       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05536          return 1;
05537       }
05538    }
05539    return 0;
05540 }
05541 
05542 
05543 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05544 {
05545    char tmp[256];
05546    char *context;
05547 
05548    /* If no mailbox, return immediately */
05549    if (ast_strlen_zero(mailbox))
05550       return 0;
05551 
05552    if (newmsgs)
05553       *newmsgs = 0;
05554    if (oldmsgs)
05555       *oldmsgs = 0;
05556    if (urgentmsgs)
05557       *urgentmsgs = 0;
05558 
05559    if (strchr(mailbox, ',')) {
05560       int tmpnew, tmpold, tmpurgent;
05561       char *mb, *cur;
05562 
05563       ast_copy_string(tmp, mailbox, sizeof(tmp));
05564       mb = tmp;
05565       while ((cur = strsep(&mb, ", "))) {
05566          if (!ast_strlen_zero(cur)) {
05567             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05568                return -1;
05569             else {
05570                if (newmsgs)
05571                   *newmsgs += tmpnew; 
05572                if (oldmsgs)
05573                   *oldmsgs += tmpold;
05574                if (urgentmsgs)
05575                   *urgentmsgs += tmpurgent;
05576             }
05577          }
05578       }
05579       return 0;
05580    }
05581 
05582    ast_copy_string(tmp, mailbox, sizeof(tmp));
05583    
05584    if ((context = strchr(tmp, '@')))
05585       *context++ = '\0';
05586    else
05587       context = "default";
05588 
05589    if (newmsgs)
05590       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05591    if (oldmsgs)
05592       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05593    if (urgentmsgs)
05594       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05595 
05596    return 0;
05597 }
05598 
05599 #endif
05600 
05601 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05602 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05603 {
05604    int urgentmsgs = 0;
05605    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05606    if (newmsgs) {
05607       *newmsgs += urgentmsgs;
05608    }
05609    return res;
05610 }
05611 
05612 static void run_externnotify(char *context, char *extension, const char *flag)
05613 {
05614    char arguments[255];
05615    char ext_context[256] = "";
05616    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05617    struct ast_smdi_mwi_message *mwi_msg;
05618 
05619    if (!ast_strlen_zero(context))
05620       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05621    else
05622       ast_copy_string(ext_context, extension, sizeof(ext_context));
05623 
05624    if (smdi_iface) {
05625       if (ast_app_has_voicemail(ext_context, NULL)) 
05626          ast_smdi_mwi_set(smdi_iface, extension);
05627       else
05628          ast_smdi_mwi_unset(smdi_iface, extension);
05629 
05630       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05631          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05632          if (!strncmp(mwi_msg->cause, "INV", 3))
05633             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05634          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05635             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05636          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05637          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05638       } else {
05639          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05640       }
05641    }
05642 
05643    if (!ast_strlen_zero(externnotify)) {
05644       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05645          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05646       } else {
05647          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05648          ast_debug(1, "Executing %s\n", arguments);
05649          ast_safe_system(arguments);
05650       }
05651    }
05652 }
05653 
05654 /*!
05655  * \brief Variables used for saving a voicemail.
05656  *
05657  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05658  */
05659 struct leave_vm_options {
05660    unsigned int flags;
05661    signed char record_gain;
05662    char *exitcontext;
05663 };
05664 
05665 /*!
05666  * \brief Prompts the user and records a voicemail to a mailbox.
05667  * \param chan
05668  * \param ext
05669  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05670  * 
05671  * 
05672  * 
05673  * \return zero on success, -1 on error.
05674  */
05675 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05676 {
05677 #ifdef IMAP_STORAGE
05678    int newmsgs, oldmsgs;
05679 #else
05680    char urgdir[PATH_MAX];
05681 #endif
05682    char txtfile[PATH_MAX];
05683    char tmptxtfile[PATH_MAX];
05684    struct vm_state *vms = NULL;
05685    char callerid[256];
05686    FILE *txt;
05687    char date[256];
05688    int txtdes;
05689    int res = 0;
05690    int msgnum;
05691    int duration = 0;
05692    int sound_duration = 0;
05693    int ausemacro = 0;
05694    int ousemacro = 0;
05695    int ouseexten = 0;
05696    char tmpdur[16];
05697    char priority[16];
05698    char origtime[16];
05699    char dir[PATH_MAX];
05700    char tmpdir[PATH_MAX];
05701    char fn[PATH_MAX];
05702    char prefile[PATH_MAX] = "";
05703    char tempfile[PATH_MAX] = "";
05704    char ext_context[256] = "";
05705    char fmt[80];
05706    char *context;
05707    char ecodes[17] = "#";
05708    struct ast_str *tmp = ast_str_create(16);
05709    char *tmpptr;
05710    struct ast_vm_user *vmu;
05711    struct ast_vm_user svm;
05712    const char *category = NULL;
05713    const char *code;
05714    const char *alldtmf = "0123456789ABCD*#";
05715    char flag[80];
05716 
05717    if (!tmp) {
05718       return -1;
05719    }
05720 
05721    ast_str_set(&tmp, 0, "%s", ext);
05722    ext = ast_str_buffer(tmp);
05723    if ((context = strchr(ext, '@'))) {
05724       *context++ = '\0';
05725       tmpptr = strchr(context, '&');
05726    } else {
05727       tmpptr = strchr(ext, '&');
05728    }
05729 
05730    if (tmpptr)
05731       *tmpptr++ = '\0';
05732 
05733    ast_channel_lock(chan);
05734    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05735       category = ast_strdupa(category);
05736    }
05737    ast_channel_unlock(chan);
05738 
05739    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05740       ast_copy_string(flag, "Urgent", sizeof(flag));
05741    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05742       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05743    } else {
05744       flag[0] = '\0';
05745    }
05746 
05747    ast_debug(3, "Before find_user\n");
05748    if (!(vmu = find_user(&svm, context, ext))) {
05749       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05750       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05751       ast_free(tmp);
05752       return res;
05753    }
05754    /* Setup pre-file if appropriate */
05755    if (strcmp(vmu->context, "default"))
05756       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05757    else
05758       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05759 
05760    /* Set the path to the prefile. Will be one of 
05761       VM_SPOOL_DIRcontext/ext/busy
05762       VM_SPOOL_DIRcontext/ext/unavail
05763       Depending on the flag set in options.
05764    */
05765    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05766       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05767    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05768       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05769    }
05770    /* Set the path to the tmpfile as
05771       VM_SPOOL_DIR/context/ext/temp
05772       and attempt to create the folder structure.
05773    */
05774    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05775    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05776       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05777       ast_free(tmp);
05778       return -1;
05779    }
05780    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05781    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05782       ast_copy_string(prefile, tempfile, sizeof(prefile));
05783 
05784    DISPOSE(tempfile, -1);
05785    /* It's easier just to try to make it than to check for its existence */
05786 #ifndef IMAP_STORAGE
05787    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05788 #else
05789    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05790    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05791       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05792    }
05793 #endif
05794 
05795    /* Check current or macro-calling context for special extensions */
05796    if (ast_test_flag(vmu, VM_OPERATOR)) {
05797       if (!ast_strlen_zero(vmu->exit)) {
05798          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05799             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05800             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05801             ouseexten = 1;
05802          }
05803       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05804          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05805          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05806          ouseexten = 1;
05807       } else if (!ast_strlen_zero(chan->macrocontext)
05808          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05809             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05810          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05811          ousemacro = 1;
05812       }
05813    }
05814 
05815    if (!ast_strlen_zero(vmu->exit)) {
05816       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05817          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05818          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05819       }
05820    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05821       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05822       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05823    } else if (!ast_strlen_zero(chan->macrocontext)
05824       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05825          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05826       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05827       ausemacro = 1;
05828    }
05829 
05830    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05831       for (code = alldtmf; *code; code++) {
05832          char e[2] = "";
05833          e[0] = *code;
05834          if (strchr(ecodes, e[0]) == NULL
05835             && ast_canmatch_extension(chan, chan->context, e, 1,
05836                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05837             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05838          }
05839       }
05840    }
05841 
05842    /* Play the beginning intro if desired */
05843    if (!ast_strlen_zero(prefile)) {
05844 #ifdef ODBC_STORAGE
05845       int success = 
05846 #endif
05847          RETRIEVE(prefile, -1, ext, context);
05848       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05849          if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) 
05850             res = ast_waitstream(chan, ecodes);
05851 #ifdef ODBC_STORAGE
05852          if (success == -1) {
05853             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05854             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05855             store_file(prefile, vmu->mailbox, vmu->context, -1);
05856          }
05857 #endif
05858       } else {
05859          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05860          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05861       }
05862       DISPOSE(prefile, -1);
05863       if (res < 0) {
05864          ast_debug(1, "Hang up during prefile playback\n");
05865          free_user(vmu);
05866          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05867          ast_free(tmp);
05868          return -1;
05869       }
05870    }
05871    if (res == '#') {
05872       /* On a '#' we skip the instructions */
05873       ast_set_flag(options, OPT_SILENT);
05874       res = 0;
05875    }
05876    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05877    if (vmu->maxmsg == 0) {
05878       ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05879       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05880       goto leave_vm_out;
05881    }
05882    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05883       res = ast_stream_and_wait(chan, INTRO, ecodes);
05884       if (res == '#') {
05885          ast_set_flag(options, OPT_SILENT);
05886          res = 0;
05887       }
05888    }
05889    if (res > 0)
05890       ast_stopstream(chan);
05891    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05892     other than the operator -- an automated attendant or mailbox login for example */
05893    if (res == '*') {
05894       chan->exten[0] = 'a';
05895       chan->exten[1] = '\0';
05896       if (!ast_strlen_zero(vmu->exit)) {
05897          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05898       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05899          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05900       }
05901       chan->priority = 0;
05902       free_user(vmu);
05903       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05904       ast_free(tmp);
05905       return 0;
05906    }
05907 
05908    /* Check for a '0' here */
05909    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05910    transfer:
05911       if (ouseexten || ousemacro) {
05912          chan->exten[0] = 'o';
05913          chan->exten[1] = '\0';
05914          if (!ast_strlen_zero(vmu->exit)) {
05915             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05916          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05917             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05918          }
05919          ast_play_and_wait(chan, "transfer");
05920          chan->priority = 0;
05921          free_user(vmu);
05922          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05923       }
05924       ast_free(tmp);
05925       return OPERATOR_EXIT;
05926    }
05927 
05928    /* Allow all other digits to exit Voicemail and return to the dialplan */
05929    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05930       if (!ast_strlen_zero(options->exitcontext))
05931          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05932       free_user(vmu);
05933       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05934       ast_free(tmp);
05935       return res;
05936    }
05937 
05938    if (res < 0) {
05939       free_user(vmu);
05940       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05941       ast_free(tmp);
05942       return -1;
05943    }
05944    /* The meat of recording the message...  All the announcements and beeps have been played*/
05945    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05946    if (!ast_strlen_zero(fmt)) {
05947       msgnum = 0;
05948 
05949 #ifdef IMAP_STORAGE
05950       /* Is ext a mailbox? */
05951       /* must open stream for this user to get info! */
05952       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05953       if (res < 0) {
05954          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05955          ast_free(tmp);
05956          return -1;
05957       }
05958       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05959       /* It is possible under certain circumstances that inboxcount did not
05960        * create a vm_state when it was needed. This is a catchall which will
05961        * rarely be used.
05962        */
05963          if (!(vms = create_vm_state_from_user(vmu))) {
05964             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05965             ast_free(tmp);
05966             return -1;
05967          }
05968       }
05969       vms->newmessages++;
05970 
05971       /* here is a big difference! We add one to it later */
05972       msgnum = newmsgs + oldmsgs;
05973       ast_debug(3, "Messagecount set to %d\n", msgnum);
05974       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05975       /* set variable for compatibility */
05976       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05977 
05978       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05979          goto leave_vm_out;
05980       }
05981 #else
05982       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05983          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
05984          if (!res)
05985             res = ast_waitstream(chan, "");
05986          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05987          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05988          inprocess_count(vmu->mailbox, vmu->context, -1);
05989          goto leave_vm_out;
05990       }
05991 
05992 #endif
05993       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05994       txtdes = mkstemp(tmptxtfile);
05995       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05996       if (txtdes < 0) {
05997          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
05998          if (!res)
05999             res = ast_waitstream(chan, "");
06000          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
06001          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06002          inprocess_count(vmu->mailbox, vmu->context, -1);
06003          goto leave_vm_out;
06004       }
06005 
06006       /* Now play the beep once we have the message number for our next message. */
06007       if (res >= 0) {
06008          /* Unless we're *really* silent, try to send the beep */
06009          res = ast_stream_and_wait(chan, "beep", "");
06010       }
06011             
06012       /* Store information in real-time storage */
06013       if (ast_check_realtime("voicemail_data")) {
06014          snprintf(priority, sizeof(priority), "%d", chan->priority);
06015          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
06016          get_date(date, sizeof(date));
06017          ast_callerid_merge(callerid, sizeof(callerid),
06018             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06019             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06020             "Unknown");
06021          ast_store_realtime("voicemail_data",
06022             "origmailbox", ext,
06023             "context", chan->context,
06024             "macrocontext", chan->macrocontext,
06025             "exten", chan->exten,
06026             "priority", priority,
06027             "callerchan", ast_channel_name(chan),
06028             "callerid", callerid,
06029             "origdate", date,
06030             "origtime", origtime,
06031             "category", S_OR(category, ""),
06032             "filename", tmptxtfile,
06033             SENTINEL);
06034       }
06035 
06036       /* Store information */
06037       txt = fdopen(txtdes, "w+");
06038       if (txt) {
06039          get_date(date, sizeof(date));
06040          ast_callerid_merge(callerid, sizeof(callerid),
06041             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06042             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06043             "Unknown");
06044          fprintf(txt, 
06045             ";\n"
06046             "; Message Information file\n"
06047             ";\n"
06048             "[message]\n"
06049             "origmailbox=%s\n"
06050             "context=%s\n"
06051             "macrocontext=%s\n"
06052             "exten=%s\n"
06053             "rdnis=%s\n"
06054             "priority=%d\n"
06055             "callerchan=%s\n"
06056             "callerid=%s\n"
06057             "origdate=%s\n"
06058             "origtime=%ld\n"
06059             "category=%s\n",
06060             ext,
06061             chan->context,
06062             chan->macrocontext, 
06063             chan->exten,
06064             S_COR(chan->redirecting.from.number.valid,
06065                chan->redirecting.from.number.str, "unknown"),
06066             chan->priority,
06067             ast_channel_name(chan),
06068             callerid,
06069             date, (long) time(NULL),
06070             category ? category : "");
06071       } else {
06072          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06073          inprocess_count(vmu->mailbox, vmu->context, -1);
06074          if (ast_check_realtime("voicemail_data")) {
06075             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06076          }
06077          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
06078          goto leave_vm_out;
06079       }
06080       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06081 
06082       if (txt) {
06083          fprintf(txt, "flag=%s\n", flag);
06084          if (sound_duration < vmu->minsecs) {
06085             fclose(txt);
06086             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06087             ast_filedelete(tmptxtfile, NULL);
06088             unlink(tmptxtfile);
06089             if (ast_check_realtime("voicemail_data")) {
06090                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06091             }
06092             inprocess_count(vmu->mailbox, vmu->context, -1);
06093          } else {
06094             fprintf(txt, "duration=%d\n", duration);
06095             fclose(txt);
06096             if (vm_lock_path(dir)) {
06097                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06098                /* Delete files */
06099                ast_filedelete(tmptxtfile, NULL);
06100                unlink(tmptxtfile);
06101                inprocess_count(vmu->mailbox, vmu->context, -1);
06102             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06103                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06104                unlink(tmptxtfile);
06105                ast_unlock_path(dir);
06106                inprocess_count(vmu->mailbox, vmu->context, -1);
06107                if (ast_check_realtime("voicemail_data")) {
06108                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06109                }
06110             } else {
06111 #ifndef IMAP_STORAGE
06112                msgnum = last_message_index(vmu, dir) + 1;
06113 #endif
06114                make_file(fn, sizeof(fn), dir, msgnum);
06115 
06116                /* assign a variable with the name of the voicemail file */ 
06117 #ifndef IMAP_STORAGE
06118                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06119 #else
06120                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06121 #endif
06122 
06123                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06124                ast_filerename(tmptxtfile, fn, NULL);
06125                rename(tmptxtfile, txtfile);
06126                inprocess_count(vmu->mailbox, vmu->context, -1);
06127 
06128                /* Properly set permissions on voicemail text descriptor file.
06129                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06130                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06131                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06132 
06133                ast_unlock_path(dir);
06134                if (ast_check_realtime("voicemail_data")) {
06135                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06136                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06137                }
06138                /* We must store the file first, before copying the message, because
06139                 * ODBC storage does the entire copy with SQL.
06140                 */
06141                if (ast_fileexists(fn, NULL, NULL) > 0) {
06142                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06143                }
06144 
06145                /* Are there to be more recipients of this message? */
06146                while (tmpptr) {
06147                   struct ast_vm_user recipu, *recip;
06148                   char *exten, *cntx;
06149 
06150                   exten = strsep(&tmpptr, "&");
06151                   cntx = strchr(exten, '@');
06152                   if (cntx) {
06153                      *cntx = '\0';
06154                      cntx++;
06155                   }
06156                   if ((recip = find_user(&recipu, cntx, exten))) {
06157                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06158                      free_user(recip);
06159                   }
06160                }
06161 #ifndef IMAP_STORAGE
06162                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06163                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06164                   char sfn[PATH_MAX];
06165                   char dfn[PATH_MAX];
06166                   int x;
06167                   /* It's easier just to try to make it than to check for its existence */
06168                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06169                   x = last_message_index(vmu, urgdir) + 1;
06170                   make_file(sfn, sizeof(sfn), dir, msgnum);
06171                   make_file(dfn, sizeof(dfn), urgdir, x);
06172                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06173                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06174                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06175                   ast_copy_string(fn, dfn, sizeof(fn));
06176                   msgnum = x;
06177                }
06178 #endif
06179                /* Notification needs to happen after the copy, though. */
06180                if (ast_fileexists(fn, NULL, NULL)) {
06181 #ifdef IMAP_STORAGE
06182                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06183                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06184                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06185                      flag);
06186 #else
06187                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06188                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06189                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06190                      flag);
06191 #endif
06192                }
06193 
06194                /* Disposal needs to happen after the optional move and copy */
06195                if (ast_fileexists(fn, NULL, NULL)) {
06196                   DISPOSE(dir, msgnum);
06197                }
06198             }
06199          }
06200       } else {
06201          inprocess_count(vmu->mailbox, vmu->context, -1);
06202       }
06203       if (res == '0') {
06204          goto transfer;
06205       } else if (res > 0 && res != 't')
06206          res = 0;
06207 
06208       if (sound_duration < vmu->minsecs)
06209          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06210          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06211       else
06212          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06213    } else
06214       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06215 leave_vm_out:
06216    free_user(vmu);
06217 
06218 #ifdef IMAP_STORAGE
06219    /* expunge message - use UID Expunge if supported on IMAP server*/
06220    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06221    if (expungeonhangup == 1) {
06222       ast_mutex_lock(&vms->lock);
06223 #ifdef HAVE_IMAP_TK2006
06224       if (LEVELUIDPLUS (vms->mailstream)) {
06225          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06226       } else 
06227 #endif
06228          mail_expunge(vms->mailstream);
06229       ast_mutex_unlock(&vms->lock);
06230    }
06231 #endif
06232 
06233    ast_free(tmp);
06234    return res;
06235 }
06236 
06237 #if !defined(IMAP_STORAGE)
06238 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06239 {
06240    /* we know the actual number of messages, so stop process when number is hit */
06241 
06242    int x, dest;
06243    char sfn[PATH_MAX];
06244    char dfn[PATH_MAX];
06245 
06246    if (vm_lock_path(dir)) {
06247       return ERROR_LOCK_PATH;
06248    }
06249 
06250    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06251       make_file(sfn, sizeof(sfn), dir, x);
06252       if (EXISTS(dir, x, sfn, NULL)) {
06253 
06254          if (x != dest) {
06255             make_file(dfn, sizeof(dfn), dir, dest);
06256             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06257          }
06258 
06259          dest++;
06260       }
06261    }
06262    ast_unlock_path(dir);
06263 
06264    return dest;
06265 }
06266 #endif
06267 
06268 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06269 {
06270    int d;
06271    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06272    return d;
06273 }
06274 
06275 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06276 {
06277 #ifdef IMAP_STORAGE
06278    /* we must use mbox(x) folder names, and copy the message there */
06279    /* simple. huh? */
06280    char sequence[10];
06281    char mailbox[256];
06282    int res;
06283 
06284    /* get the real IMAP message number for this message */
06285    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06286 
06287    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06288    ast_mutex_lock(&vms->lock);
06289    /* if save to Old folder, put in INBOX as read */
06290    if (box == OLD_FOLDER) {
06291       mail_setflag(vms->mailstream, sequence, "\\Seen");
06292       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06293    } else if (box == NEW_FOLDER) {
06294       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06295       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06296    }
06297    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06298       ast_mutex_unlock(&vms->lock);
06299       return 0;
06300    }
06301    /* Create the folder if it don't exist */
06302    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06303    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06304    if (mail_create(vms->mailstream, mailbox) == NIL)
06305       ast_debug(5, "Folder exists.\n");
06306    else
06307       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06308    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06309    ast_mutex_unlock(&vms->lock);
06310    return res;
06311 #else
06312    char *dir = vms->curdir;
06313    char *username = vms->username;
06314    char *context = vmu->context;
06315    char sfn[PATH_MAX];
06316    char dfn[PATH_MAX];
06317    char ddir[PATH_MAX];
06318    const char *dbox = mbox(vmu, box);
06319    int x, i;
06320    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06321 
06322    if (vm_lock_path(ddir))
06323       return ERROR_LOCK_PATH;
06324 
06325    x = last_message_index(vmu, ddir) + 1;
06326 
06327    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06328       x--;
06329       for (i = 1; i <= x; i++) {
06330          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06331          make_file(sfn, sizeof(sfn), ddir, i);
06332          make_file(dfn, sizeof(dfn), ddir, i - 1);
06333          if (EXISTS(ddir, i, sfn, NULL)) {
06334             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06335          } else
06336             break;
06337       }
06338    } else {
06339       if (x >= vmu->maxmsg) {
06340          ast_unlock_path(ddir);
06341          return -1;
06342       }
06343    }
06344    make_file(sfn, sizeof(sfn), dir, msg);
06345    make_file(dfn, sizeof(dfn), ddir, x);
06346    if (strcmp(sfn, dfn)) {
06347       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06348    }
06349    ast_unlock_path(ddir);
06350    return 0;
06351 #endif
06352 }
06353 
06354 static int adsi_logo(unsigned char *buf)
06355 {
06356    int bytes = 0;
06357    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06358    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06359    return bytes;
06360 }
06361 
06362 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06363 {
06364    unsigned char buf[256];
06365    int bytes = 0;
06366    int x;
06367    char num[5];
06368 
06369    *useadsi = 0;
06370    bytes += ast_adsi_data_mode(buf + bytes);
06371    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06372 
06373    bytes = 0;
06374    bytes += adsi_logo(buf);
06375    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06376 #ifdef DISPLAY
06377    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06378 #endif
06379    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06380    bytes += ast_adsi_data_mode(buf + bytes);
06381    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06382 
06383    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06384       bytes = 0;
06385       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06386       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06387       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06388       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06389       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06390       return 0;
06391    }
06392 
06393 #ifdef DISPLAY
06394    /* Add a dot */
06395    bytes = 0;
06396    bytes += ast_adsi_logo(buf);
06397    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06398    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06399    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06400    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06401 #endif
06402    bytes = 0;
06403    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06404    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06405    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06406    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06407    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06408    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06409    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06410 
06411 #ifdef DISPLAY
06412    /* Add another dot */
06413    bytes = 0;
06414    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06415    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06416 
06417    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06418    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06419 #endif
06420 
06421    bytes = 0;
06422    /* These buttons we load but don't use yet */
06423    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06424    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06425    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06426    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06427    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06428    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06429    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06430 
06431 #ifdef DISPLAY
06432    /* Add another dot */
06433    bytes = 0;
06434    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06435    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06436    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06437 #endif
06438 
06439    bytes = 0;
06440    for (x = 0; x < 5; x++) {
06441       snprintf(num, sizeof(num), "%d", x);
06442       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06443    }
06444    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06445    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06446 
06447 #ifdef DISPLAY
06448    /* Add another dot */
06449    bytes = 0;
06450    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06451    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06452    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06453 #endif
06454 
06455    if (ast_adsi_end_download(chan)) {
06456       bytes = 0;
06457       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06458       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06459       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06460       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06461       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06462       return 0;
06463    }
06464    bytes = 0;
06465    bytes += ast_adsi_download_disconnect(buf + bytes);
06466    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06467    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06468 
06469    ast_debug(1, "Done downloading scripts...\n");
06470 
06471 #ifdef DISPLAY
06472    /* Add last dot */
06473    bytes = 0;
06474    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06475    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06476 #endif
06477    ast_debug(1, "Restarting session...\n");
06478 
06479    bytes = 0;
06480    /* Load the session now */
06481    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06482       *useadsi = 1;
06483       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06484    } else
06485       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06486 
06487    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06488    return 0;
06489 }
06490 
06491 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06492 {
06493    int x;
06494    if (!ast_adsi_available(chan))
06495       return;
06496    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06497    if (x < 0)
06498       return;
06499    if (!x) {
06500       if (adsi_load_vmail(chan, useadsi)) {
06501          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06502          return;
06503       }
06504    } else
06505       *useadsi = 1;
06506 }
06507 
06508 static void adsi_login(struct ast_channel *chan)
06509 {
06510    unsigned char buf[256];
06511    int bytes = 0;
06512    unsigned char keys[8];
06513    int x;
06514    if (!ast_adsi_available(chan))
06515       return;
06516 
06517    for (x = 0; x < 8; x++)
06518       keys[x] = 0;
06519    /* Set one key for next */
06520    keys[3] = ADSI_KEY_APPS + 3;
06521 
06522    bytes += adsi_logo(buf + bytes);
06523    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06524    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06525    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06526    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06527    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06528    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06529    bytes += ast_adsi_set_keys(buf + bytes, keys);
06530    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06531    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06532 }
06533 
06534 static void adsi_password(struct ast_channel *chan)
06535 {
06536    unsigned char buf[256];
06537    int bytes = 0;
06538    unsigned char keys[8];
06539    int x;
06540    if (!ast_adsi_available(chan))
06541       return;
06542 
06543    for (x = 0; x < 8; x++)
06544       keys[x] = 0;
06545    /* Set one key for next */
06546    keys[3] = ADSI_KEY_APPS + 3;
06547 
06548    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06549    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06550    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06551    bytes += ast_adsi_set_keys(buf + bytes, keys);
06552    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06553    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06554 }
06555 
06556 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06557 {
06558    unsigned char buf[256];
06559    int bytes = 0;
06560    unsigned char keys[8];
06561    int x, y;
06562 
06563    if (!ast_adsi_available(chan))
06564       return;
06565 
06566    for (x = 0; x < 5; x++) {
06567       y = ADSI_KEY_APPS + 12 + start + x;
06568       if (y > ADSI_KEY_APPS + 12 + 4)
06569          y = 0;
06570       keys[x] = ADSI_KEY_SKT | y;
06571    }
06572    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06573    keys[6] = 0;
06574    keys[7] = 0;
06575 
06576    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06577    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06578    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06579    bytes += ast_adsi_set_keys(buf + bytes, keys);
06580    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06581 
06582    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06583 }
06584 
06585 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06586 {
06587    int bytes = 0;
06588    unsigned char buf[256]; 
06589    char buf1[256], buf2[256];
06590    char fn2[PATH_MAX];
06591 
06592    char cid[256] = "";
06593    char *val;
06594    char *name, *num;
06595    char datetime[21] = "";
06596    FILE *f;
06597 
06598    unsigned char keys[8];
06599 
06600    int x;
06601 
06602    if (!ast_adsi_available(chan))
06603       return;
06604 
06605    /* Retrieve important info */
06606    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06607    f = fopen(fn2, "r");
06608    if (f) {
06609       while (!feof(f)) {   
06610          if (!fgets((char *) buf, sizeof(buf), f)) {
06611             continue;
06612          }
06613          if (!feof(f)) {
06614             char *stringp = NULL;
06615             stringp = (char *) buf;
06616             strsep(&stringp, "=");
06617             val = strsep(&stringp, "=");
06618             if (!ast_strlen_zero(val)) {
06619                if (!strcmp((char *) buf, "callerid"))
06620                   ast_copy_string(cid, val, sizeof(cid));
06621                if (!strcmp((char *) buf, "origdate"))
06622                   ast_copy_string(datetime, val, sizeof(datetime));
06623             }
06624          }
06625       }
06626       fclose(f);
06627    }
06628    /* New meaning for keys */
06629    for (x = 0; x < 5; x++)
06630       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06631    keys[6] = 0x0;
06632    keys[7] = 0x0;
06633 
06634    if (!vms->curmsg) {
06635       /* No prev key, provide "Folder" instead */
06636       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06637    }
06638    if (vms->curmsg >= vms->lastmsg) {
06639       /* If last message ... */
06640       if (vms->curmsg) {
06641          /* but not only message, provide "Folder" instead */
06642          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06643          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06644 
06645       } else {
06646          /* Otherwise if only message, leave blank */
06647          keys[3] = 1;
06648       }
06649    }
06650 
06651    if (!ast_strlen_zero(cid)) {
06652       ast_callerid_parse(cid, &name, &num);
06653       if (!name)
06654          name = num;
06655    } else
06656       name = "Unknown Caller";
06657 
06658    /* If deleted, show "undeleted" */
06659 
06660    if (vms->deleted[vms->curmsg])
06661       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06662 
06663    /* Except "Exit" */
06664    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06665    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06666       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06667    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06668 
06669    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06670    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06671    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06672    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06673    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06674    bytes += ast_adsi_set_keys(buf + bytes, keys);
06675    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06676 
06677    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06678 }
06679 
06680 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06681 {
06682    int bytes = 0;
06683    unsigned char buf[256];
06684    unsigned char keys[8];
06685 
06686    int x;
06687 
06688    if (!ast_adsi_available(chan))
06689       return;
06690 
06691    /* New meaning for keys */
06692    for (x = 0; x < 5; x++)
06693       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06694 
06695    keys[6] = 0x0;
06696    keys[7] = 0x0;
06697 
06698    if (!vms->curmsg) {
06699       /* No prev key, provide "Folder" instead */
06700       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06701    }
06702    if (vms->curmsg >= vms->lastmsg) {
06703       /* If last message ... */
06704       if (vms->curmsg) {
06705          /* but not only message, provide "Folder" instead */
06706          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06707       } else {
06708          /* Otherwise if only message, leave blank */
06709          keys[3] = 1;
06710       }
06711    }
06712 
06713    /* If deleted, show "undeleted" */
06714    if (vms->deleted[vms->curmsg]) 
06715       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06716 
06717    /* Except "Exit" */
06718    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06719    bytes += ast_adsi_set_keys(buf + bytes, keys);
06720    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06721 
06722    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06723 }
06724 
06725 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06726 {
06727    unsigned char buf[256] = "";
06728    char buf1[256] = "", buf2[256] = "";
06729    int bytes = 0;
06730    unsigned char keys[8];
06731    int x;
06732 
06733    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06734    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06735    if (!ast_adsi_available(chan))
06736       return;
06737    if (vms->newmessages) {
06738       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06739       if (vms->oldmessages) {
06740          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06741          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06742       } else {
06743          snprintf(buf2, sizeof(buf2), "%s.", newm);
06744       }
06745    } else if (vms->oldmessages) {
06746       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06747       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06748    } else {
06749       strcpy(buf1, "You have no messages.");
06750       buf2[0] = ' ';
06751       buf2[1] = '\0';
06752    }
06753    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06754    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06755    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06756 
06757    for (x = 0; x < 6; x++)
06758       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06759    keys[6] = 0;
06760    keys[7] = 0;
06761 
06762    /* Don't let them listen if there are none */
06763    if (vms->lastmsg < 0)
06764       keys[0] = 1;
06765    bytes += ast_adsi_set_keys(buf + bytes, keys);
06766 
06767    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06768 
06769    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06770 }
06771 
06772 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06773 {
06774    unsigned char buf[256] = "";
06775    char buf1[256] = "", buf2[256] = "";
06776    int bytes = 0;
06777    unsigned char keys[8];
06778    int x;
06779 
06780    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06781 
06782    if (!ast_adsi_available(chan))
06783       return;
06784 
06785    /* Original command keys */
06786    for (x = 0; x < 6; x++)
06787       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06788 
06789    keys[6] = 0;
06790    keys[7] = 0;
06791 
06792    if ((vms->lastmsg + 1) < 1)
06793       keys[0] = 0;
06794 
06795    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06796       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06797 
06798    if (vms->lastmsg + 1)
06799       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06800    else
06801       strcpy(buf2, "no messages.");
06802    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06803    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06804    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06805    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06806    bytes += ast_adsi_set_keys(buf + bytes, keys);
06807 
06808    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06809 
06810    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06811    
06812 }
06813 
06814 /*
06815 static void adsi_clear(struct ast_channel *chan)
06816 {
06817    char buf[256];
06818    int bytes=0;
06819    if (!ast_adsi_available(chan))
06820       return;
06821    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06822    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06823 
06824    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06825 }
06826 */
06827 
06828 static void adsi_goodbye(struct ast_channel *chan)
06829 {
06830    unsigned char buf[256];
06831    int bytes = 0;
06832 
06833    if (!ast_adsi_available(chan))
06834       return;
06835    bytes += adsi_logo(buf + bytes);
06836    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06837    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06838    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06839    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06840 
06841    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06842 }
06843 
06844 /*!\brief get_folder: Folder menu
06845  * Plays "press 1 for INBOX messages" etc.
06846  * Should possibly be internationalized
06847  */
06848 static int get_folder(struct ast_channel *chan, int start)
06849 {
06850    int x;
06851    int d;
06852    char fn[PATH_MAX];
06853    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06854    if (d)
06855       return d;
06856    for (x = start; x < 5; x++) { /* For all folders */
06857       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
06858          return d;
06859       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06860       if (d)
06861          return d;
06862       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06863 
06864       /* The inbox folder can have its name changed under certain conditions
06865        * so this checks if the sound file exists for the inbox folder name and
06866        * if it doesn't, plays the default name instead. */
06867       if (x == 0) {
06868          if (ast_fileexists(fn, NULL, NULL)) {
06869             d = vm_play_folder_name(chan, fn);
06870          } else {
06871             ast_verb(1, "failed to find %s\n", fn);
06872             d = vm_play_folder_name(chan, "vm-INBOX");
06873          }
06874       } else {
06875          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06876          d = vm_play_folder_name(chan, fn);
06877       }
06878 
06879       if (d)
06880          return d;
06881       d = ast_waitfordigit(chan, 500);
06882       if (d)
06883          return d;
06884    }
06885 
06886    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06887    if (d)
06888       return d;
06889    d = ast_waitfordigit(chan, 4000);
06890    return d;
06891 }
06892 
06893 /*!
06894  * \brief plays a prompt and waits for a keypress.
06895  * \param chan
06896  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06897  * \param start Does not appear to be used at this time.
06898  *
06899  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06900  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06901  * prompting for the number inputs that correspond to the available folders.
06902  * 
06903  * \return zero on success, or -1 on error.
06904  */
06905 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06906 {
06907    int res = 0;
06908    int loops = 0;
06909 
06910    res = ast_play_and_wait(chan, fn);  /* Folder name */
06911    while (((res < '0') || (res > '9')) &&
06912          (res != '#') && (res >= 0) &&
06913          loops < 4) {
06914       res = get_folder(chan, 0);
06915       loops++;
06916    }
06917    if (loops == 4) { /* give up */
06918       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06919       return '#';
06920    }
06921    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06922    return res;
06923 }
06924 
06925 /*!
06926  * \brief presents the option to prepend to an existing message when forwarding it.
06927  * \param chan
06928  * \param vmu
06929  * \param curdir
06930  * \param curmsg
06931  * \param vm_fmts
06932  * \param context
06933  * \param record_gain
06934  * \param duration
06935  * \param vms
06936  * \param flag 
06937  *
06938  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06939  *
06940  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06941  * \return zero on success, -1 on error.
06942  */
06943 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06944          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06945 {
06946    int cmd = 0;
06947    int retries = 0, prepend_duration = 0, already_recorded = 0;
06948    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06949    char textfile[PATH_MAX];
06950    struct ast_config *msg_cfg;
06951    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06952 #ifndef IMAP_STORAGE
06953    signed char zero_gain = 0;
06954 #endif
06955    const char *duration_str;
06956 
06957    /* Must always populate duration correctly */
06958    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06959    strcpy(textfile, msgfile);
06960    strcpy(backup, msgfile);
06961    strcpy(backup_textfile, msgfile);
06962    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06963    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06964    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06965 
06966    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06967       *duration = atoi(duration_str);
06968    } else {
06969       *duration = 0;
06970    }
06971 
06972    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06973       if (cmd)
06974          retries = 0;
06975       switch (cmd) {
06976       case '1': 
06977 
06978 #ifdef IMAP_STORAGE
06979          /* Record new intro file */
06980          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06981          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06982          ast_play_and_wait(chan, INTRO);
06983          ast_play_and_wait(chan, "beep");
06984          play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06985          cmd = 't';
06986 #else
06987 
06988          /* prepend a message to the current message, update the metadata and return */
06989 
06990          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06991          strcpy(textfile, msgfile);
06992          strncat(textfile, ".txt", sizeof(textfile) - 1);
06993          *duration = 0;
06994 
06995          /* if we can't read the message metadata, stop now */
06996          if (!msg_cfg) {
06997             cmd = 0;
06998             break;
06999          }
07000 
07001          /* Back up the original file, so we can retry the prepend and restore it after forward. */
07002 #ifndef IMAP_STORAGE
07003          if (already_recorded) {
07004             ast_filecopy(backup, msgfile, NULL);
07005             copy(backup_textfile, textfile);
07006          }
07007          else {
07008             ast_filecopy(msgfile, backup, NULL);
07009             copy(textfile, backup_textfile);
07010          }
07011 #endif
07012          already_recorded = 1;
07013 
07014          if (record_gain)
07015             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
07016 
07017          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
07018 
07019          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
07020             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
07021             ast_stream_and_wait(chan, vm_prepend_timeout, "");
07022             ast_filerename(backup, msgfile, NULL);
07023          }
07024 
07025          if (record_gain)
07026             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
07027 
07028          
07029          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
07030             *duration = atoi(duration_str);
07031 
07032          if (prepend_duration) {
07033             struct ast_category *msg_cat;
07034             /* need enough space for a maximum-length message duration */
07035             char duration_buf[12];
07036 
07037             *duration += prepend_duration;
07038             msg_cat = ast_category_get(msg_cfg, "message");
07039             snprintf(duration_buf, 11, "%ld", *duration);
07040             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
07041                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
07042             }
07043          }
07044 
07045 #endif
07046          break;
07047       case '2': 
07048          /* NULL out introfile so we know there is no intro! */
07049 #ifdef IMAP_STORAGE
07050          *vms->introfn = '\0';
07051 #endif
07052          cmd = 't';
07053          break;
07054       case '*':
07055          cmd = '*';
07056          break;
07057       default: 
07058          /* If time_out and return to menu, reset already_recorded */
07059          already_recorded = 0;
07060 
07061          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07062             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07063          if (!cmd) {
07064             cmd = ast_play_and_wait(chan, "vm-starmain");
07065             /* "press star to return to the main menu" */
07066          }
07067          if (!cmd) {
07068             cmd = ast_waitfordigit(chan, 6000);
07069          }
07070          if (!cmd) {
07071             retries++;
07072          }
07073          if (retries > 3) {
07074             cmd = '*'; /* Let's cancel this beast */
07075          }
07076          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07077       }
07078    }
07079 
07080    if (msg_cfg)
07081       ast_config_destroy(msg_cfg);
07082    if (prepend_duration)
07083       *duration = prepend_duration;
07084 
07085    if (already_recorded && cmd == -1) {
07086       /* restore original message if prepention cancelled */
07087       ast_filerename(backup, msgfile, NULL);
07088       rename(backup_textfile, textfile);
07089    }
07090 
07091    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07092       cmd = 0;
07093    return cmd;
07094 }
07095 
07096 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07097 {
07098    struct ast_event *event;
07099    char *mailbox, *context;
07100 
07101    /* Strip off @default */
07102    context = mailbox = ast_strdupa(box);
07103    strsep(&context, "@");
07104    if (ast_strlen_zero(context))
07105       context = "default";
07106 
07107    if (!(event = ast_event_new(AST_EVENT_MWI,
07108          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07109          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07110          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07111          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07112          AST_EVENT_IE_END))) {
07113       return;
07114    }
07115 
07116    ast_event_queue_and_cache(event);
07117 }
07118 
07119 /*!
07120  * \brief Sends email notification that a user has a new voicemail waiting for them.
07121  * \param chan
07122  * \param vmu
07123  * \param vms
07124  * \param msgnum
07125  * \param duration
07126  * \param fmt
07127  * \param cidnum The Caller ID phone number value.
07128  * \param cidname The Caller ID name value.
07129  * \param flag
07130  *
07131  * \return zero on success, -1 on error.
07132  */
07133 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)
07134 {
07135    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07136    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07137    const char *category;
07138    char *myserveremail = serveremail;
07139 
07140    ast_channel_lock(chan);
07141    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07142       category = ast_strdupa(category);
07143    }
07144    ast_channel_unlock(chan);
07145 
07146 #ifndef IMAP_STORAGE
07147    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07148 #else
07149    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07150 #endif
07151    make_file(fn, sizeof(fn), todir, msgnum);
07152    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07153 
07154    if (!ast_strlen_zero(vmu->attachfmt)) {
07155       if (strstr(fmt, vmu->attachfmt))
07156          fmt = vmu->attachfmt;
07157       else
07158          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);
07159    }
07160 
07161    /* Attach only the first format */
07162    fmt = ast_strdupa(fmt);
07163    stringp = fmt;
07164    strsep(&stringp, "|");
07165 
07166    if (!ast_strlen_zero(vmu->serveremail))
07167       myserveremail = vmu->serveremail;
07168 
07169    if (!ast_strlen_zero(vmu->email)) {
07170       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07171 
07172       if (attach_user_voicemail)
07173          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07174 
07175       /* XXX possible imap issue, should category be NULL XXX */
07176       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07177 
07178       if (attach_user_voicemail)
07179          DISPOSE(todir, msgnum);
07180    }
07181 
07182    if (!ast_strlen_zero(vmu->pager)) {
07183       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07184    }
07185 
07186    if (ast_test_flag(vmu, VM_DELETE))
07187       DELETE(todir, msgnum, fn, vmu);
07188 
07189    /* Leave voicemail for someone */
07190    if (ast_app_has_voicemail(ext_context, NULL)) 
07191       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07192 
07193    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07194 
07195    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);
07196    run_externnotify(vmu->context, vmu->mailbox, flag);
07197 
07198 #ifdef IMAP_STORAGE
07199    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07200    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07201       vm_imap_delete(NULL, vms->curmsg, vmu);
07202       vms->newmessages--;  /* Fix new message count */
07203    }
07204 #endif
07205 
07206    return 0;
07207 }
07208 
07209 /*!
07210  * \brief Sends a voicemail message to a mailbox recipient.
07211  * \param chan
07212  * \param context
07213  * \param vms
07214  * \param sender
07215  * \param fmt
07216  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07217  *             Will be 0 when called to forward an existing message (option 8)
07218  *             Will be 1 when called to leave a message (option 3->5)
07219  * \param record_gain 
07220  * \param urgent
07221  *
07222  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07223  * 
07224  * When in the leave message mode (is_new_message == 1):
07225  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07226  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07227  *
07228  * When in the forward message mode (is_new_message == 0):
07229  *   - retreives the current message to be forwarded
07230  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07231  *   - determines the target mailbox and folders
07232  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07233  *
07234  * \return zero on success, -1 on error.
07235  */
07236 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)
07237 {
07238 #ifdef IMAP_STORAGE
07239    int todircount = 0;
07240    struct vm_state *dstvms;
07241 #endif
07242    char username[70]="";
07243    char fn[PATH_MAX]; /* for playback of name greeting */
07244    char ecodes[16] = "#";
07245    int res = 0, cmd = 0;
07246    struct ast_vm_user *receiver = NULL, *vmtmp;
07247    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07248    char *stringp;
07249    const char *s;
07250    int saved_messages = 0;
07251    int valid_extensions = 0;
07252    char *dir;
07253    int curmsg;
07254    char urgent_str[7] = "";
07255    int prompt_played = 0;
07256 #ifndef IMAP_STORAGE
07257    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07258 #endif
07259    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07260       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07261    }
07262 
07263    if (vms == NULL) return -1;
07264    dir = vms->curdir;
07265    curmsg = vms->curmsg;
07266 
07267    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07268    while (!res && !valid_extensions) {
07269       int use_directory = 0;
07270       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07271          int done = 0;
07272          int retries = 0;
07273          cmd = 0;
07274          while ((cmd >= 0) && !done ){
07275             if (cmd)
07276                retries = 0;
07277             switch (cmd) {
07278             case '1': 
07279                use_directory = 0;
07280                done = 1;
07281                break;
07282             case '2': 
07283                use_directory = 1;
07284                done = 1;
07285                break;
07286             case '*': 
07287                cmd = 't';
07288                done = 1;
07289                break;
07290             default: 
07291                /* Press 1 to enter an extension press 2 to use the directory */
07292                cmd = ast_play_and_wait(chan, "vm-forward");
07293                if (!cmd) {
07294                   cmd = ast_waitfordigit(chan, 3000);
07295                }
07296                if (!cmd) {
07297                   retries++;
07298                }
07299                if (retries > 3) {
07300                   cmd = 't';
07301                   done = 1;
07302                }
07303                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07304             }
07305          }
07306          if (cmd < 0 || cmd == 't')
07307             break;
07308       }
07309       
07310       if (use_directory) {
07311          /* use app_directory */
07312          
07313          char old_context[sizeof(chan->context)];
07314          char old_exten[sizeof(chan->exten)];
07315          int old_priority;
07316          struct ast_app* directory_app;
07317 
07318          directory_app = pbx_findapp("Directory");
07319          if (directory_app) {
07320             char vmcontext[256];
07321             /* make backup copies */
07322             memcpy(old_context, chan->context, sizeof(chan->context));
07323             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07324             old_priority = chan->priority;
07325             
07326             /* call the the Directory, changes the channel */
07327             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07328             res = pbx_exec(chan, directory_app, vmcontext);
07329             
07330             ast_copy_string(username, chan->exten, sizeof(username));
07331             
07332             /* restore the old context, exten, and priority */
07333             memcpy(chan->context, old_context, sizeof(chan->context));
07334             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07335             chan->priority = old_priority;
07336          } else {
07337             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07338             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07339          }
07340       } else {
07341          /* Ask for an extension */
07342          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07343          res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
07344          prompt_played++;
07345          if (res || prompt_played > 4)
07346             break;
07347          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
07348             break;
07349       }
07350 
07351       /* start all over if no username */
07352       if (ast_strlen_zero(username))
07353          continue;
07354       stringp = username;
07355       s = strsep(&stringp, "*");
07356       /* start optimistic */
07357       valid_extensions = 1;
07358       while (s) {
07359          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07360             int oldmsgs;
07361             int newmsgs;
07362             int capacity;
07363             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07364                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07365                /* Shouldn't happen, but allow trying another extension if it does */
07366                res = ast_play_and_wait(chan, "pbx-invalid");
07367                valid_extensions = 0;
07368                break;
07369             }
07370             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07371             if ((newmsgs + oldmsgs) >= capacity) {
07372                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07373                res = ast_play_and_wait(chan, "vm-mailboxfull");
07374                valid_extensions = 0;
07375                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07376                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07377                   free_user(vmtmp);
07378                }
07379                inprocess_count(receiver->mailbox, receiver->context, -1);
07380                break;
07381             }
07382             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07383          } else {
07384             /* XXX Optimization for the future.  When we encounter a single bad extension,
07385              * bailing out on all of the extensions may not be the way to go.  We should
07386              * probably just bail on that single extension, then allow the user to enter
07387              * several more. XXX
07388              */
07389             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07390                free_user(receiver);
07391             }
07392             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07393             /* "I am sorry, that's not a valid extension.  Please try again." */
07394             res = ast_play_and_wait(chan, "pbx-invalid");
07395             valid_extensions = 0;
07396             break;
07397          }
07398 
07399          /* play name if available, else play extension number */
07400          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07401          RETRIEVE(fn, -1, s, receiver->context);
07402          if (ast_fileexists(fn, NULL, NULL) > 0) {
07403             res = ast_stream_and_wait(chan, fn, ecodes);
07404             if (res) {
07405                DISPOSE(fn, -1);
07406                return res;
07407             }
07408          } else {
07409             res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
07410          }
07411          DISPOSE(fn, -1);
07412 
07413          s = strsep(&stringp, "*");
07414       }
07415       /* break from the loop of reading the extensions */
07416       if (valid_extensions)
07417          break;
07418    }
07419    /* check if we're clear to proceed */
07420    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07421       return res;
07422    if (is_new_message == 1) {
07423       struct leave_vm_options leave_options;
07424       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07425       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07426 
07427       /* Send VoiceMail */
07428       memset(&leave_options, 0, sizeof(leave_options));
07429       leave_options.record_gain = record_gain;
07430       cmd = leave_voicemail(chan, mailbox, &leave_options);
07431    } else {
07432       /* Forward VoiceMail */
07433       long duration = 0;
07434       struct vm_state vmstmp;
07435       int copy_msg_result = 0;
07436       memcpy(&vmstmp, vms, sizeof(vmstmp));
07437 
07438       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07439 
07440       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07441       if (!cmd) {
07442          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07443 #ifdef IMAP_STORAGE
07444             int attach_user_voicemail;
07445             char *myserveremail = serveremail;
07446             
07447             /* get destination mailbox */
07448             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07449             if (!dstvms) {
07450                dstvms = create_vm_state_from_user(vmtmp);
07451             }
07452             if (dstvms) {
07453                init_mailstream(dstvms, 0);
07454                if (!dstvms->mailstream) {
07455                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07456                } else {
07457                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07458                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07459                }
07460             } else {
07461                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07462             }
07463             if (!ast_strlen_zero(vmtmp->serveremail))
07464                myserveremail = vmtmp->serveremail;
07465             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07466             /* NULL category for IMAP storage */
07467             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07468                dstvms->curbox,
07469                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07470                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07471                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07472                NULL, urgent_str);
07473 #else
07474             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07475 #endif
07476             saved_messages++;
07477             AST_LIST_REMOVE_CURRENT(list);
07478             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07479             free_user(vmtmp);
07480             if (res)
07481                break;
07482          }
07483          AST_LIST_TRAVERSE_SAFE_END;
07484          if (saved_messages > 0 && !copy_msg_result) {
07485             /* give confirmation that the message was saved */
07486             /* commented out since we can't forward batches yet
07487             if (saved_messages == 1)
07488                res = ast_play_and_wait(chan, "vm-message");
07489             else
07490                res = ast_play_and_wait(chan, "vm-messages");
07491             if (!res)
07492                res = ast_play_and_wait(chan, "vm-saved"); */
07493 #ifdef IMAP_STORAGE
07494             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07495             if (ast_strlen_zero(vmstmp.introfn))
07496 #endif
07497             res = ast_play_and_wait(chan, "vm-msgsaved");
07498          }
07499 #ifndef IMAP_STORAGE
07500          else {
07501             /* with IMAP, mailbox full warning played by imap_check_limits */
07502             res = ast_play_and_wait(chan, "vm-mailboxfull");
07503          }
07504          /* Restore original message without prepended message if backup exists */
07505          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07506          strcpy(textfile, msgfile);
07507          strcpy(backup, msgfile);
07508          strcpy(backup_textfile, msgfile);
07509          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07510          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07511          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07512          if (ast_fileexists(backup, NULL, NULL) > 0) {
07513             ast_filerename(backup, msgfile, NULL);
07514             rename(backup_textfile, textfile);
07515          }
07516 #endif
07517       }
07518       DISPOSE(dir, curmsg);
07519 #ifndef IMAP_STORAGE
07520       if (cmd) { /* assuming hangup, cleanup backup file */
07521          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07522          strcpy(textfile, msgfile);
07523          strcpy(backup_textfile, msgfile);
07524          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07525          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07526          rename(backup_textfile, textfile);
07527       }
07528 #endif
07529    }
07530 
07531    /* If anything failed above, we still have this list to free */
07532    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07533       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07534       free_user(vmtmp);
07535    }
07536    return res ? res : cmd;
07537 }
07538 
07539 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07540 {
07541    int res;
07542    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07543       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07544    return res;
07545 }
07546 
07547 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07548 {
07549    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07550    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);
07551 }
07552 
07553 static int play_message_category(struct ast_channel *chan, const char *category)
07554 {
07555    int res = 0;
07556 
07557    if (!ast_strlen_zero(category))
07558       res = ast_play_and_wait(chan, category);
07559 
07560    if (res) {
07561       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07562       res = 0;
07563    }
07564 
07565    return res;
07566 }
07567 
07568 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07569 {
07570    int res = 0;
07571    struct vm_zone *the_zone = NULL;
07572    time_t t;
07573 
07574    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07575       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07576       return 0;
07577    }
07578 
07579    /* Does this user have a timezone specified? */
07580    if (!ast_strlen_zero(vmu->zonetag)) {
07581       /* Find the zone in the list */
07582       struct vm_zone *z;
07583       AST_LIST_LOCK(&zones);
07584       AST_LIST_TRAVERSE(&zones, z, list) {
07585          if (!strcmp(z->name, vmu->zonetag)) {
07586             the_zone = z;
07587             break;
07588          }
07589       }
07590       AST_LIST_UNLOCK(&zones);
07591    }
07592 
07593 /* No internal variable parsing for now, so we'll comment it out for the time being */
07594 #if 0
07595    /* Set the DIFF_* variables */
07596    ast_localtime(&t, &time_now, NULL);
07597    tv_now = ast_tvnow();
07598    ast_localtime(&tv_now, &time_then, NULL);
07599 
07600    /* Day difference */
07601    if (time_now.tm_year == time_then.tm_year)
07602       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07603    else
07604       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07605    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07606 
07607    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07608 #endif
07609    if (the_zone) {
07610       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
07611    } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) {     /* GERMAN syntax */
07612       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
07613    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {     /* GREEK syntax */
07614       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q  H 'digits/kai' M ", NULL);
07615    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {     /* ITALIAN syntax */
07616       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
07617    } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {     /* DUTCH syntax */
07618       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
07619    } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {     /* NORWEGIAN syntax */
07620       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
07621    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {     /* POLISH syntax */
07622       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
07623    } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07624       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
07625    } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) {     /* SWEDISH syntax */
07626       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
07627    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07628       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
07629    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {     /* VIETNAMESE syntax */
07630       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
07631    } else {
07632       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
07633    }
07634 #if 0
07635    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07636 #endif
07637    return res;
07638 }
07639 
07640 
07641 
07642 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
07643 {
07644    int res = 0;
07645    int i;
07646    char *callerid, *name;
07647    char prefile[PATH_MAX] = "";
07648 
07649    /* If voicemail cid is not enabled, or we didn't get cid or context from
07650     * the attribute file, leave now.
07651     *
07652     * TODO Still need to change this so that if this function is called by the
07653     * message envelope (and someone is explicitly requesting to hear the CID),
07654     * it does not check to see if CID is enabled in the config file.
07655     */
07656    if ((cid == NULL)||(context == NULL))
07657       return res;
07658 
07659    /* Strip off caller ID number from name */
07660    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07661    ast_callerid_parse(cid, &name, &callerid);
07662    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07663       /* Check for internal contexts and only */
07664       /* say extension when the call didn't come from an internal context in the list */
07665       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07666          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07667          if ((strcmp(cidinternalcontexts[i], context) == 0))
07668             break;
07669       }
07670       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07671          if (!res) {
07672             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07673             if (!ast_strlen_zero(prefile)) {
07674                /* See if we can find a recorded name for this callerid
07675                 * and if found, use that instead of saying number. */
07676                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07677                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07678                   if (!callback)
07679                      res = wait_file2(chan, vms, "vm-from");
07680                   res = ast_stream_and_wait(chan, prefile, "");
07681                } else {
07682                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07683                   /* Say "from extension" as one saying to sound smoother */
07684                   if (!callback)
07685                      res = wait_file2(chan, vms, "vm-from-extension");
07686                   res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
07687                }
07688             }
07689          }
07690       } else if (!res) {
07691          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07692          /* If there is a recording for this numeric callerid then play that */
07693          if (!callback) {
07694             /* See if we can find a recorded name for this person instead of their extension number */
07695             snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
07696             if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
07697                ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
07698                wait_file2(chan, vms, "vm-from");
07699                res = ast_stream_and_wait(chan, prefile, "");
07700                ast_verb(3, "Played recorded name result '%d'\n", res);
07701             } else {
07702                /* Since this is all nicely figured out, why not say "from phone number" in this case" */
07703                wait_file2(chan, vms, "vm-from-phonenumber");
07704                res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
07705             }
07706          } else {
07707             res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
07708          }
07709       }
07710    } else {
07711       /* Number unknown */
07712       ast_debug(1, "VM-CID: From an unknown number\n");
07713       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07714       res = wait_file2(chan, vms, "vm-unknown-caller");
07715    }
07716    return res;
07717 }
07718 
07719 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07720 {
07721    int res = 0;
07722    int durationm;
07723    int durations;
07724    /* Verify that we have a duration for the message */
07725    if (duration == NULL)
07726       return res;
07727 
07728    /* Convert from seconds to minutes */
07729    durations = atoi(duration);
07730    durationm = (durations / 60);
07731 
07732    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07733 
07734    if ((!res) && (durationm >= minduration)) {
07735       res = wait_file2(chan, vms, "vm-duration");
07736 
07737       /* POLISH syntax */
07738       if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
07739          div_t num = div(durationm, 10);
07740 
07741          if (durationm == 1) {
07742             res = ast_play_and_wait(chan, "digits/1z");
07743             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07744          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07745             if (num.rem == 2) {
07746                if (!num.quot) {
07747                   res = ast_play_and_wait(chan, "digits/2-ie");
07748                } else {
07749                   res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
07750                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07751                }
07752             } else {
07753                res = say_and_wait(chan, durationm, ast_channel_language(chan));
07754             }
07755             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07756          } else {
07757             res = say_and_wait(chan, durationm, ast_channel_language(chan));
07758             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07759          }
07760       /* DEFAULT syntax */
07761       } else {
07762          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
07763          res = wait_file2(chan, vms, "vm-minutes");
07764       }
07765    }
07766    return res;
07767 }
07768 
07769 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07770 {
07771    int res = 0;
07772    char filename[256], *cid;
07773    const char *origtime, *context, *category, *duration, *flag;
07774    struct ast_config *msg_cfg;
07775    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07776 
07777    vms->starting = 0;
07778    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07779    adsi_message(chan, vms);
07780    if (!vms->curmsg) {
07781       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07782    } else if (vms->curmsg == vms->lastmsg) {
07783       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07784    }
07785 
07786    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07787    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07788    msg_cfg = ast_config_load(filename, config_flags);
07789    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07790       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07791       return 0;
07792    }
07793    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07794 
07795    /* Play the word urgent if we are listening to urgent messages */
07796    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07797       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07798    }
07799 
07800    if (!res) {
07801       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07802       /* POLISH syntax */
07803       if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
07804          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07805             int ten, one;
07806             char nextmsg[256];
07807             ten = (vms->curmsg + 1) / 10;
07808             one = (vms->curmsg + 1) % 10;
07809 
07810             if (vms->curmsg < 20) {
07811                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07812                res = wait_file2(chan, vms, nextmsg);
07813             } else {
07814                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07815                res = wait_file2(chan, vms, nextmsg);
07816                if (one > 0) {
07817                   if (!res) {
07818                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07819                      res = wait_file2(chan, vms, nextmsg);
07820                   }
07821                }
07822             }
07823          }
07824          if (!res)
07825             res = wait_file2(chan, vms, "vm-message");
07826       /* HEBREW syntax */
07827       } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
07828          if (!vms->curmsg) {
07829             res = wait_file2(chan, vms, "vm-message");
07830             res = wait_file2(chan, vms, "vm-first");
07831          } else if (vms->curmsg == vms->lastmsg) {
07832             res = wait_file2(chan, vms, "vm-message");
07833             res = wait_file2(chan, vms, "vm-last");
07834          } else {
07835             res = wait_file2(chan, vms, "vm-message");
07836             res = wait_file2(chan, vms, "vm-number");
07837             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
07838          }
07839       /* VIETNAMESE syntax */
07840       } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
07841          if (!vms->curmsg) {
07842             res = wait_file2(chan, vms, "vm-message");
07843             res = wait_file2(chan, vms, "vm-first");
07844          } else if (vms->curmsg == vms->lastmsg) {
07845             res = wait_file2(chan, vms, "vm-message");
07846             res = wait_file2(chan, vms, "vm-last");
07847          } else {
07848             res = wait_file2(chan, vms, "vm-message");
07849             res = wait_file2(chan, vms, "vm-number");
07850             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
07851          }
07852       } else {
07853          if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
07854             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07855          } else { /* DEFAULT syntax */
07856             res = wait_file2(chan, vms, "vm-message");
07857          }
07858          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07859             if (!res) {
07860                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07861                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
07862             }
07863          }
07864       }
07865    }
07866 
07867    if (!msg_cfg) {
07868       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07869       return 0;
07870    }
07871 
07872    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07873       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07874       DISPOSE(vms->curdir, vms->curmsg);
07875       ast_config_destroy(msg_cfg);
07876       return 0;
07877    }
07878 
07879    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07880    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07881    category = ast_variable_retrieve(msg_cfg, "message", "category");
07882 
07883    context = ast_variable_retrieve(msg_cfg, "message", "context");
07884    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07885       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07886    if (!res) {
07887       res = play_message_category(chan, category);
07888    }
07889    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07890       res = play_message_datetime(chan, vmu, origtime, filename);
07891    }
07892    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07893       res = play_message_callerid(chan, vms, cid, context, 0, 0);
07894    }
07895    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07896       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07897    }
07898    /* Allow pressing '1' to skip envelope / callerid */
07899    if (res == '1') {
07900       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07901       res = 0;
07902    }
07903    ast_config_destroy(msg_cfg);
07904 
07905    if (!res) {
07906       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07907       vms->heard[vms->curmsg] = 1;
07908 #ifdef IMAP_STORAGE
07909       /*IMAP storage stores any prepended message from a forward
07910        * as a separate file from the rest of the message
07911        */
07912       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07913          wait_file(chan, vms, vms->introfn);
07914       }
07915 #endif
07916       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07917          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07918          res = 0;
07919       }
07920       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07921    }
07922    DISPOSE(vms->curdir, vms->curmsg);
07923    return res;
07924 }
07925 
07926 #ifdef IMAP_STORAGE
07927 static int imap_remove_file(char *dir, int msgnum)
07928 {
07929    char fn[PATH_MAX];
07930    char full_fn[PATH_MAX];
07931    char intro[PATH_MAX] = {0,};
07932    
07933    if (msgnum > -1) {
07934       make_file(fn, sizeof(fn), dir, msgnum);
07935       snprintf(intro, sizeof(intro), "%sintro", fn);
07936    } else
07937       ast_copy_string(fn, dir, sizeof(fn));
07938    
07939    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07940       ast_filedelete(fn, NULL);
07941       if (!ast_strlen_zero(intro)) {
07942          ast_filedelete(intro, NULL);
07943       }
07944       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07945       unlink(full_fn);
07946    }
07947    return 0;
07948 }
07949 
07950 
07951 
07952 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07953 {
07954    char *file, *filename;
07955    char *attachment;
07956    char arg[10];
07957    int i;
07958    BODY* body;
07959 
07960    file = strrchr(ast_strdupa(dir), '/');
07961    if (file) {
07962       *file++ = '\0';
07963    } else {
07964       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07965       return -1;
07966    }
07967 
07968    ast_mutex_lock(&vms->lock);
07969    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07970       mail_fetchstructure(vms->mailstream, i + 1, &body);
07971       /* We have the body, now we extract the file name of the first attachment. */
07972       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07973          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07974       } else {
07975          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07976          ast_mutex_unlock(&vms->lock);
07977          return -1;
07978       }
07979       filename = strsep(&attachment, ".");
07980       if (!strcmp(filename, file)) {
07981          sprintf(arg, "%d", i + 1);
07982          mail_setflag(vms->mailstream, arg, "\\DELETED");
07983       }
07984    }
07985    mail_expunge(vms->mailstream);
07986    ast_mutex_unlock(&vms->lock);
07987    return 0;
07988 }
07989 
07990 #elif !defined(IMAP_STORAGE)
07991 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07992 {
07993    int count_msg, last_msg;
07994 
07995    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07996 
07997    /* Rename the member vmbox HERE so that we don't try to return before
07998     * we know what's going on.
07999     */
08000    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
08001 
08002    /* Faster to make the directory than to check if it exists. */
08003    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
08004 
08005    /* traverses directory using readdir (or select query for ODBC) */
08006    count_msg = count_messages(vmu, vms->curdir);
08007    if (count_msg < 0) {
08008       return count_msg;
08009    } else {
08010       vms->lastmsg = count_msg - 1;
08011    }
08012 
08013    if (vm_allocate_dh(vms, vmu, count_msg)) {
08014       return -1;
08015    }
08016 
08017    /*
08018    The following test is needed in case sequencing gets messed up.
08019    There appears to be more than one way to mess up sequence, so
08020    we will not try to find all of the root causes--just fix it when
08021    detected.
08022    */
08023 
08024    if (vm_lock_path(vms->curdir)) {
08025       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
08026       return ERROR_LOCK_PATH;
08027    }
08028 
08029    /* for local storage, checks directory for messages up to maxmsg limit */
08030    last_msg = last_message_index(vmu, vms->curdir);
08031    ast_unlock_path(vms->curdir);
08032 
08033    if (last_msg < -1) {
08034       return last_msg;
08035    } else if (vms->lastmsg != last_msg) {
08036       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
08037       resequence_mailbox(vmu, vms->curdir, count_msg);
08038    }
08039 
08040    return 0;
08041 }
08042 #endif
08043 
08044 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
08045 {
08046    int x = 0;
08047 
08048 #ifndef IMAP_STORAGE
08049    int last_msg_idx;
08050    int res = 0, nummsg;
08051    char fn2[PATH_MAX];
08052 #endif
08053 
08054    if (vms->lastmsg <= -1) {
08055       goto done;
08056    }
08057 
08058    vms->curmsg = -1;
08059 #ifndef IMAP_STORAGE
08060    /* Get the deleted messages fixed */
08061    if (vm_lock_path(vms->curdir)) {
08062       return ERROR_LOCK_PATH;
08063    }
08064 
08065    /* update count as message may have arrived while we've got mailbox open */
08066    last_msg_idx = last_message_index(vmu, vms->curdir);
08067    if (last_msg_idx != vms->lastmsg) {
08068       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08069    }
08070 
08071    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08072    for (x = 0; x < last_msg_idx + 1; x++) {
08073       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08074          /* Save this message.  It's not in INBOX or hasn't been heard */
08075          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08076          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08077             break;
08078          }
08079          vms->curmsg++;
08080          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08081          if (strcmp(vms->fn, fn2)) {
08082             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08083          }
08084       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08085          /* Move to old folder before deleting */
08086          res = save_to_folder(vmu, vms, x, 1);
08087          if (res == ERROR_LOCK_PATH) {
08088             /* If save failed do not delete the message */
08089             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08090             vms->deleted[x] = 0;
08091             vms->heard[x] = 0;
08092             --x;
08093          }
08094       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08095          /* Move to deleted folder */
08096          res = save_to_folder(vmu, vms, x, 10);
08097          if (res == ERROR_LOCK_PATH) {
08098             /* If save failed do not delete the message */
08099             vms->deleted[x] = 0;
08100             vms->heard[x] = 0;
08101             --x;
08102          }
08103       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08104          /* If realtime storage enabled - we should explicitly delete this message,
08105          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08106          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08107          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08108             DELETE(vms->curdir, x, vms->fn, vmu);
08109          }
08110       }
08111    }
08112 
08113    /* Delete ALL remaining messages */
08114    nummsg = x - 1;
08115    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08116       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08117       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08118          DELETE(vms->curdir, x, vms->fn, vmu);
08119       }
08120    }
08121    ast_unlock_path(vms->curdir);
08122 #else /* defined(IMAP_STORAGE) */
08123    if (vms->deleted) {
08124       /* Since we now expunge after each delete, deleting in reverse order
08125        * ensures that no reordering occurs between each step. */
08126       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
08127          if (vms->deleted[x]) {
08128             ast_debug(3, "IMAP delete of %d\n", x);
08129             DELETE(vms->curdir, x, vms->fn, vmu);
08130          }
08131       }
08132    }
08133 #endif
08134 
08135 done:
08136    if (vms->deleted) {
08137       ast_free(vms->deleted);
08138    }
08139    if (vms->heard) {
08140       ast_free(vms->heard);
08141    }
08142 
08143    return 0;
08144 }
08145 
08146 /* In Greek even though we CAN use a syntax like "friends messages"
08147  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08148  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08149  * syntax for the above three categories which is more elegant.
08150  */
08151 
08152 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08153 {
08154    int cmd;
08155    char *buf;
08156 
08157    buf = alloca(strlen(box) + 2);
08158    strcpy(buf, box);
08159    strcat(buf, "s");
08160 
08161    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08162       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08163       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08164    } else {
08165       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08166       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08167    }
08168 }
08169 
08170 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08171 {
08172    int cmd;
08173 
08174    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08175       if (!strcasecmp(box, "vm-INBOX"))
08176          cmd = ast_play_and_wait(chan, "vm-new-e");
08177       else
08178          cmd = ast_play_and_wait(chan, "vm-old-e");
08179       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08180    } else {
08181       cmd = ast_play_and_wait(chan, "vm-messages");
08182       return cmd ? cmd : ast_play_and_wait(chan, box);
08183    }
08184 }
08185 
08186 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08187 {
08188    int cmd;
08189 
08190    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08191       cmd = ast_play_and_wait(chan, "vm-messages");
08192       return cmd ? cmd : ast_play_and_wait(chan, box);
08193    } else {
08194       cmd = ast_play_and_wait(chan, box);
08195       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08196    }
08197 }
08198 
08199 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08200 {
08201    int cmd;
08202 
08203    if (  !strncasecmp(ast_channel_language(chan), "it", 2) ||
08204         !strncasecmp(ast_channel_language(chan), "es", 2) ||
08205         !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08206       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08207       return cmd ? cmd : ast_play_and_wait(chan, box);
08208    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
08209       return vm_play_folder_name_gr(chan, box);
08210    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* Hebrew syntax */
08211       return ast_play_and_wait(chan, box);
08212    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
08213       return vm_play_folder_name_pl(chan, box);
08214    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {  /* Ukrainian syntax */
08215       return vm_play_folder_name_ua(chan, box);
08216    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
08217       return ast_play_and_wait(chan, box);
08218    } else {  /* Default English */
08219       cmd = ast_play_and_wait(chan, box);
08220       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08221    }
08222 }
08223 
08224 /* GREEK SYNTAX
08225    In greek the plural for old/new is
08226    different so we need the following files
08227    We also need vm-denExeteMynhmata because
08228    this syntax is different.
08229 
08230    -> vm-Olds.wav : "Palia"
08231    -> vm-INBOXs.wav : "Nea"
08232    -> vm-denExeteMynhmata : "den exete mynhmata"
08233 */
08234 
08235 
08236 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08237 {
08238    int res = 0;
08239 
08240    if (vms->newmessages) {
08241       res = ast_play_and_wait(chan, "vm-youhave");
08242       if (!res) 
08243          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08244       if (!res) {
08245          if ((vms->newmessages == 1)) {
08246             res = ast_play_and_wait(chan, "vm-INBOX");
08247             if (!res)
08248                res = ast_play_and_wait(chan, "vm-message");
08249          } else {
08250             res = ast_play_and_wait(chan, "vm-INBOXs");
08251             if (!res)
08252                res = ast_play_and_wait(chan, "vm-messages");
08253          }
08254       }
08255    } else if (vms->oldmessages){
08256       res = ast_play_and_wait(chan, "vm-youhave");
08257       if (!res)
08258          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08259       if ((vms->oldmessages == 1)){
08260          res = ast_play_and_wait(chan, "vm-Old");
08261          if (!res)
08262             res = ast_play_and_wait(chan, "vm-message");
08263       } else {
08264          res = ast_play_and_wait(chan, "vm-Olds");
08265          if (!res)
08266             res = ast_play_and_wait(chan, "vm-messages");
08267       }
08268    } else if (!vms->oldmessages && !vms->newmessages) 
08269       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08270    return res;
08271 }
08272 
08273 /* Version of vm_intro() designed to work for many languages.
08274  *
08275  * It is hoped that this function can prevent the proliferation of 
08276  * language-specific vm_intro() functions and in time replace the language-
08277  * specific functions which already exist.  An examination of the language-
08278  * specific functions revealed that they all corrected the same deficiencies
08279  * in vm_intro_en() (which was the default function). Namely:
08280  *
08281  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08282  *     wording of the voicemail greeting hides this problem.  For example,
08283  *     vm-INBOX contains only the word "new".  This means that both of these
08284  *     sequences produce valid utterances:
08285  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08286  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08287  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08288  *     in many languages) the first utterance becomes "you have 1 the new message".
08289  *  2) The function contains hardcoded rules for pluralizing the word "message".
08290  *     These rules are correct for English, but not for many other languages.
08291  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08292  *     required in many languages.
08293  *  4) The gender of the word for "message" is not specified. This is a problem
08294  *     because in many languages the gender of the number in phrases such
08295  *     as "you have one new message" must match the gender of the word
08296  *     meaning "message".
08297  *
08298  * Fixing these problems for each new language has meant duplication of effort.
08299  * This new function solves the problems in the following general ways:
08300  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08301  *     and vm-Old respectively for those languages where it makes sense.
08302  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08303  *     on vm-message.
08304  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08305  *     prefix on vm-new and vm-old (none for English).
08306  *  4) Pass the gender of the language's word for "message" as an agument to
08307  *     this function which is can in turn pass on to the functions which 
08308  *     say numbers and put endings on nounds and adjectives.
08309  *
08310  * All languages require these messages:
08311  *  vm-youhave    "You have..."
08312  *  vm-and     "and"
08313  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08314  *
08315  * To use it for English, you will need these additional sound files:
08316  *  vm-new     "new"
08317  *  vm-message    "message", singular
08318  *  vm-messages      "messages", plural
08319  *
08320  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08321  *
08322  *  vm-newn    "novoye" (singular, neuter)
08323  *  vm-newx    "novikh" (counting plural form, genative plural)
08324  *  vm-message    "sobsheniye" (singular form)
08325  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08326  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08327  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08328  *  digits/2n     "dva" (neuter singular)
08329  */
08330 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08331 {
08332    int res;
08333    int lastnum = 0;
08334 
08335    res = ast_play_and_wait(chan, "vm-youhave");
08336 
08337    if (!res && vms->newmessages) {
08338       lastnum = vms->newmessages;
08339 
08340       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
08341          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08342       }
08343 
08344       if (!res && vms->oldmessages) {
08345          res = ast_play_and_wait(chan, "vm-and");
08346       }
08347    }
08348 
08349    if (!res && vms->oldmessages) {
08350       lastnum = vms->oldmessages;
08351 
08352       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
08353          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08354       }
08355    }
08356 
08357    if (!res) {
08358       if (lastnum == 0) {
08359          res = ast_play_and_wait(chan, "vm-no");
08360       }
08361       if (!res) {
08362          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08363       }
08364    }
08365 
08366    return res;
08367 }
08368 
08369 /* Default Hebrew syntax */
08370 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08371 {
08372    int res = 0;
08373 
08374    /* Introduce messages they have */
08375    if (!res) {
08376       if ((vms->newmessages) || (vms->oldmessages)) {
08377          res = ast_play_and_wait(chan, "vm-youhave");
08378       }
08379       /*
08380        * The word "shtei" refers to the number 2 in hebrew when performing a count
08381        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08382        * an element, this is one of them.
08383        */
08384       if (vms->newmessages) {
08385          if (!res) {
08386             if (vms->newmessages == 1) {
08387                res = ast_play_and_wait(chan, "vm-INBOX1");
08388             } else {
08389                if (vms->newmessages == 2) {
08390                   res = ast_play_and_wait(chan, "vm-shtei");
08391                } else {
08392                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08393                }
08394                res = ast_play_and_wait(chan, "vm-INBOX");
08395             }
08396          }
08397          if (vms->oldmessages && !res) {
08398             res = ast_play_and_wait(chan, "vm-and");
08399             if (vms->oldmessages == 1) {
08400                res = ast_play_and_wait(chan, "vm-Old1");
08401             } else {
08402                if (vms->oldmessages == 2) {
08403                   res = ast_play_and_wait(chan, "vm-shtei");
08404                } else {
08405                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08406                }
08407                res = ast_play_and_wait(chan, "vm-Old");
08408             }
08409          }
08410       }
08411       if (!res && vms->oldmessages && !vms->newmessages) {
08412          if (!res) {
08413             if (vms->oldmessages == 1) {
08414                res = ast_play_and_wait(chan, "vm-Old1");
08415             } else {
08416                if (vms->oldmessages == 2) {
08417                   res = ast_play_and_wait(chan, "vm-shtei");
08418                } else {
08419                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");            
08420                }
08421                res = ast_play_and_wait(chan, "vm-Old");
08422             }
08423          }
08424       }
08425       if (!res) {
08426          if (!vms->oldmessages && !vms->newmessages) {
08427             if (!res) {
08428                res = ast_play_and_wait(chan, "vm-nomessages");
08429             }
08430          }
08431       }
08432    }
08433    return res;
08434 }
08435    
08436 /* Default English syntax */
08437 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08438 {
08439    int res;
08440 
08441    /* Introduce messages they have */
08442    res = ast_play_and_wait(chan, "vm-youhave");
08443    if (!res) {
08444       if (vms->urgentmessages) {
08445          res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
08446          if (!res)
08447             res = ast_play_and_wait(chan, "vm-Urgent");
08448          if ((vms->oldmessages || vms->newmessages) && !res) {
08449             res = ast_play_and_wait(chan, "vm-and");
08450          } else if (!res) {
08451             if ((vms->urgentmessages == 1))
08452                res = ast_play_and_wait(chan, "vm-message");
08453             else
08454                res = ast_play_and_wait(chan, "vm-messages");
08455          }
08456       }
08457       if (vms->newmessages) {
08458          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08459          if (!res)
08460             res = ast_play_and_wait(chan, "vm-INBOX");
08461          if (vms->oldmessages && !res)
08462             res = ast_play_and_wait(chan, "vm-and");
08463          else if (!res) {
08464             if ((vms->newmessages == 1))
08465                res = ast_play_and_wait(chan, "vm-message");
08466             else
08467                res = ast_play_and_wait(chan, "vm-messages");
08468          }
08469             
08470       }
08471       if (!res && vms->oldmessages) {
08472          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08473          if (!res)
08474             res = ast_play_and_wait(chan, "vm-Old");
08475          if (!res) {
08476             if (vms->oldmessages == 1)
08477                res = ast_play_and_wait(chan, "vm-message");
08478             else
08479                res = ast_play_and_wait(chan, "vm-messages");
08480          }
08481       }
08482       if (!res) {
08483          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08484             res = ast_play_and_wait(chan, "vm-no");
08485             if (!res)
08486                res = ast_play_and_wait(chan, "vm-messages");
08487          }
08488       }
08489    }
08490    return res;
08491 }
08492 
08493 /* ITALIAN syntax */
08494 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08495 {
08496    /* Introduce messages they have */
08497    int res;
08498    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08499       res = ast_play_and_wait(chan, "vm-no") ||
08500          ast_play_and_wait(chan, "vm-message");
08501    else
08502       res = ast_play_and_wait(chan, "vm-youhave");
08503    if (!res && vms->newmessages) {
08504       res = (vms->newmessages == 1) ?
08505          ast_play_and_wait(chan, "digits/un") ||
08506          ast_play_and_wait(chan, "vm-nuovo") ||
08507          ast_play_and_wait(chan, "vm-message") :
08508          /* 2 or more new messages */
08509          say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
08510          ast_play_and_wait(chan, "vm-nuovi") ||
08511          ast_play_and_wait(chan, "vm-messages");
08512       if (!res && vms->oldmessages)
08513          res = ast_play_and_wait(chan, "vm-and");
08514    }
08515    if (!res && vms->oldmessages) {
08516       res = (vms->oldmessages == 1) ?
08517          ast_play_and_wait(chan, "digits/un") ||
08518          ast_play_and_wait(chan, "vm-vecchio") ||
08519          ast_play_and_wait(chan, "vm-message") :
08520          /* 2 or more old messages */
08521          say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
08522          ast_play_and_wait(chan, "vm-vecchi") ||
08523          ast_play_and_wait(chan, "vm-messages");
08524    }
08525    return res;
08526 }
08527 
08528 /* POLISH syntax */
08529 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08530 {
08531    /* Introduce messages they have */
08532    int res;
08533    div_t num;
08534 
08535    if (!vms->oldmessages && !vms->newmessages) {
08536       res = ast_play_and_wait(chan, "vm-no");
08537       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08538       return res;
08539    } else {
08540       res = ast_play_and_wait(chan, "vm-youhave");
08541    }
08542 
08543    if (vms->newmessages) {
08544       num = div(vms->newmessages, 10);
08545       if (vms->newmessages == 1) {
08546          res = ast_play_and_wait(chan, "digits/1-a");
08547          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08548          res = res ? res : ast_play_and_wait(chan, "vm-message");
08549       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08550          if (num.rem == 2) {
08551             if (!num.quot) {
08552                res = ast_play_and_wait(chan, "digits/2-ie");
08553             } else {
08554                res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
08555                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08556             }
08557          } else {
08558             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08559          }
08560          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08561          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08562       } else {
08563          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08564          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08565          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08566       }
08567       if (!res && vms->oldmessages)
08568          res = ast_play_and_wait(chan, "vm-and");
08569    }
08570    if (!res && vms->oldmessages) {
08571       num = div(vms->oldmessages, 10);
08572       if (vms->oldmessages == 1) {
08573          res = ast_play_and_wait(chan, "digits/1-a");
08574          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08575          res = res ? res : ast_play_and_wait(chan, "vm-message");
08576       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08577          if (num.rem == 2) {
08578             if (!num.quot) {
08579                res = ast_play_and_wait(chan, "digits/2-ie");
08580             } else {
08581                res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
08582                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08583             }
08584          } else {
08585             res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08586          }
08587          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08588          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08589       } else {
08590          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08591          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08592          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08593       }
08594    }
08595 
08596    return res;
08597 }
08598 
08599 /* SWEDISH syntax */
08600 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08601 {
08602    /* Introduce messages they have */
08603    int res;
08604 
08605    res = ast_play_and_wait(chan, "vm-youhave");
08606    if (res)
08607       return res;
08608 
08609    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08610       res = ast_play_and_wait(chan, "vm-no");
08611       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08612       return res;
08613    }
08614 
08615    if (vms->newmessages) {
08616       if ((vms->newmessages == 1)) {
08617          res = ast_play_and_wait(chan, "digits/ett");
08618          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08619          res = res ? res : ast_play_and_wait(chan, "vm-message");
08620       } else {
08621          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08622          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08623          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08624       }
08625       if (!res && vms->oldmessages)
08626          res = ast_play_and_wait(chan, "vm-and");
08627    }
08628    if (!res && vms->oldmessages) {
08629       if (vms->oldmessages == 1) {
08630          res = ast_play_and_wait(chan, "digits/ett");
08631          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08632          res = res ? res : ast_play_and_wait(chan, "vm-message");
08633       } else {
08634          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08635          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08636          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08637       }
08638    }
08639 
08640    return res;
08641 }
08642 
08643 /* NORWEGIAN syntax */
08644 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08645 {
08646    /* Introduce messages they have */
08647    int res;
08648 
08649    res = ast_play_and_wait(chan, "vm-youhave");
08650    if (res)
08651       return res;
08652 
08653    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08654       res = ast_play_and_wait(chan, "vm-no");
08655       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08656       return res;
08657    }
08658 
08659    if (vms->newmessages) {
08660       if ((vms->newmessages == 1)) {
08661          res = ast_play_and_wait(chan, "digits/1");
08662          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08663          res = res ? res : ast_play_and_wait(chan, "vm-message");
08664       } else {
08665          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08666          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08667          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08668       }
08669       if (!res && vms->oldmessages)
08670          res = ast_play_and_wait(chan, "vm-and");
08671    }
08672    if (!res && vms->oldmessages) {
08673       if (vms->oldmessages == 1) {
08674          res = ast_play_and_wait(chan, "digits/1");
08675          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08676          res = res ? res : ast_play_and_wait(chan, "vm-message");
08677       } else {
08678          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08679          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08680          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08681       }
08682    }
08683 
08684    return res;
08685 }
08686 
08687 /* GERMAN syntax */
08688 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08689 {
08690    /* Introduce messages they have */
08691    int res;
08692    res = ast_play_and_wait(chan, "vm-youhave");
08693    if (!res) {
08694       if (vms->newmessages) {
08695          if ((vms->newmessages == 1))
08696             res = ast_play_and_wait(chan, "digits/1F");
08697          else
08698             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08699          if (!res)
08700             res = ast_play_and_wait(chan, "vm-INBOX");
08701          if (vms->oldmessages && !res)
08702             res = ast_play_and_wait(chan, "vm-and");
08703          else if (!res) {
08704             if ((vms->newmessages == 1))
08705                res = ast_play_and_wait(chan, "vm-message");
08706             else
08707                res = ast_play_and_wait(chan, "vm-messages");
08708          }
08709             
08710       }
08711       if (!res && vms->oldmessages) {
08712          if (vms->oldmessages == 1)
08713             res = ast_play_and_wait(chan, "digits/1F");
08714          else
08715             res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08716          if (!res)
08717             res = ast_play_and_wait(chan, "vm-Old");
08718          if (!res) {
08719             if (vms->oldmessages == 1)
08720                res = ast_play_and_wait(chan, "vm-message");
08721             else
08722                res = ast_play_and_wait(chan, "vm-messages");
08723          }
08724       }
08725       if (!res) {
08726          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08727             res = ast_play_and_wait(chan, "vm-no");
08728             if (!res)
08729                res = ast_play_and_wait(chan, "vm-messages");
08730          }
08731       }
08732    }
08733    return res;
08734 }
08735 
08736 /* SPANISH syntax */
08737 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08738 {
08739    /* Introduce messages they have */
08740    int res;
08741    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08742       res = ast_play_and_wait(chan, "vm-youhaveno");
08743       if (!res)
08744          res = ast_play_and_wait(chan, "vm-messages");
08745    } else {
08746       res = ast_play_and_wait(chan, "vm-youhave");
08747    }
08748    if (!res) {
08749       if (vms->newmessages) {
08750          if (!res) {
08751             if ((vms->newmessages == 1)) {
08752                res = ast_play_and_wait(chan, "digits/1M");
08753                if (!res)
08754                   res = ast_play_and_wait(chan, "vm-message");
08755                if (!res)
08756                   res = ast_play_and_wait(chan, "vm-INBOXs");
08757             } else {
08758                res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08759                if (!res)
08760                   res = ast_play_and_wait(chan, "vm-messages");
08761                if (!res)
08762                   res = ast_play_and_wait(chan, "vm-INBOX");
08763             }
08764          }
08765          if (vms->oldmessages && !res)
08766             res = ast_play_and_wait(chan, "vm-and");
08767       }
08768       if (vms->oldmessages) {
08769          if (!res) {
08770             if (vms->oldmessages == 1) {
08771                res = ast_play_and_wait(chan, "digits/1M");
08772                if (!res)
08773                   res = ast_play_and_wait(chan, "vm-message");
08774                if (!res)
08775                   res = ast_play_and_wait(chan, "vm-Olds");
08776             } else {
08777                res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08778                if (!res)
08779                   res = ast_play_and_wait(chan, "vm-messages");
08780                if (!res)
08781                   res = ast_play_and_wait(chan, "vm-Old");
08782             }
08783          }
08784       }
08785    }
08786 return res;
08787 }
08788 
08789 /* BRAZILIAN PORTUGUESE syntax */
08790 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08791    /* Introduce messages they have */
08792    int res;
08793    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08794       res = ast_play_and_wait(chan, "vm-nomessages");
08795       return res;
08796    } else {
08797       res = ast_play_and_wait(chan, "vm-youhave");
08798    }
08799    if (vms->newmessages) {
08800       if (!res)
08801          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08802       if ((vms->newmessages == 1)) {
08803          if (!res)
08804             res = ast_play_and_wait(chan, "vm-message");
08805          if (!res)
08806             res = ast_play_and_wait(chan, "vm-INBOXs");
08807       } else {
08808          if (!res)
08809             res = ast_play_and_wait(chan, "vm-messages");
08810          if (!res)
08811             res = ast_play_and_wait(chan, "vm-INBOX");
08812       }
08813       if (vms->oldmessages && !res)
08814          res = ast_play_and_wait(chan, "vm-and");
08815    }
08816    if (vms->oldmessages) {
08817       if (!res)
08818          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08819       if (vms->oldmessages == 1) {
08820          if (!res)
08821             res = ast_play_and_wait(chan, "vm-message");
08822          if (!res)
08823             res = ast_play_and_wait(chan, "vm-Olds");
08824       } else {
08825          if (!res)
08826             res = ast_play_and_wait(chan, "vm-messages");
08827          if (!res)
08828             res = ast_play_and_wait(chan, "vm-Old");
08829       }
08830    }
08831    return res;
08832 }
08833 
08834 /* FRENCH syntax */
08835 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08836 {
08837    /* Introduce messages they have */
08838    int res;
08839    res = ast_play_and_wait(chan, "vm-youhave");
08840    if (!res) {
08841       if (vms->newmessages) {
08842          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08843          if (!res)
08844             res = ast_play_and_wait(chan, "vm-INBOX");
08845          if (vms->oldmessages && !res)
08846             res = ast_play_and_wait(chan, "vm-and");
08847          else if (!res) {
08848             if ((vms->newmessages == 1))
08849                res = ast_play_and_wait(chan, "vm-message");
08850             else
08851                res = ast_play_and_wait(chan, "vm-messages");
08852          }
08853             
08854       }
08855       if (!res && vms->oldmessages) {
08856          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08857          if (!res)
08858             res = ast_play_and_wait(chan, "vm-Old");
08859          if (!res) {
08860             if (vms->oldmessages == 1)
08861                res = ast_play_and_wait(chan, "vm-message");
08862             else
08863                res = ast_play_and_wait(chan, "vm-messages");
08864          }
08865       }
08866       if (!res) {
08867          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08868             res = ast_play_and_wait(chan, "vm-no");
08869             if (!res)
08870                res = ast_play_and_wait(chan, "vm-messages");
08871          }
08872       }
08873    }
08874    return res;
08875 }
08876 
08877 /* DUTCH syntax */
08878 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08879 {
08880    /* Introduce messages they have */
08881    int res;
08882    res = ast_play_and_wait(chan, "vm-youhave");
08883    if (!res) {
08884       if (vms->newmessages) {
08885          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
08886          if (!res) {
08887             if (vms->newmessages == 1)
08888                res = ast_play_and_wait(chan, "vm-INBOXs");
08889             else
08890                res = ast_play_and_wait(chan, "vm-INBOX");
08891          }
08892          if (vms->oldmessages && !res)
08893             res = ast_play_and_wait(chan, "vm-and");
08894          else if (!res) {
08895             if ((vms->newmessages == 1))
08896                res = ast_play_and_wait(chan, "vm-message");
08897             else
08898                res = ast_play_and_wait(chan, "vm-messages");
08899          }
08900             
08901       }
08902       if (!res && vms->oldmessages) {
08903          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
08904          if (!res) {
08905             if (vms->oldmessages == 1)
08906                res = ast_play_and_wait(chan, "vm-Olds");
08907             else
08908                res = ast_play_and_wait(chan, "vm-Old");
08909          }
08910          if (!res) {
08911             if (vms->oldmessages == 1)
08912                res = ast_play_and_wait(chan, "vm-message");
08913             else
08914                res = ast_play_and_wait(chan, "vm-messages");
08915          }
08916       }
08917       if (!res) {
08918          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08919             res = ast_play_and_wait(chan, "vm-no");
08920             if (!res)
08921                res = ast_play_and_wait(chan, "vm-messages");
08922          }
08923       }
08924    }
08925    return res;
08926 }
08927 
08928 /* PORTUGUESE syntax */
08929 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08930 {
08931    /* Introduce messages they have */
08932    int res;
08933    res = ast_play_and_wait(chan, "vm-youhave");
08934    if (!res) {
08935       if (vms->newmessages) {
08936          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08937          if (!res) {
08938             if ((vms->newmessages == 1)) {
08939                res = ast_play_and_wait(chan, "vm-message");
08940                if (!res)
08941                   res = ast_play_and_wait(chan, "vm-INBOXs");
08942             } else {
08943                res = ast_play_and_wait(chan, "vm-messages");
08944                if (!res)
08945                   res = ast_play_and_wait(chan, "vm-INBOX");
08946             }
08947          }
08948          if (vms->oldmessages && !res)
08949             res = ast_play_and_wait(chan, "vm-and");
08950       }
08951       if (!res && vms->oldmessages) {
08952          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08953          if (!res) {
08954             if (vms->oldmessages == 1) {
08955                res = ast_play_and_wait(chan, "vm-message");
08956                if (!res)
08957                   res = ast_play_and_wait(chan, "vm-Olds");
08958             } else {
08959                res = ast_play_and_wait(chan, "vm-messages");
08960                if (!res)
08961                   res = ast_play_and_wait(chan, "vm-Old");
08962             }
08963          }
08964       }
08965       if (!res) {
08966          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08967             res = ast_play_and_wait(chan, "vm-no");
08968             if (!res)
08969                res = ast_play_and_wait(chan, "vm-messages");
08970          }
08971       }
08972    }
08973    return res;
08974 }
08975 
08976 
08977 /* CZECH syntax */
08978 /* in czech there must be declension of word new and message
08979  * czech        : english        : czech      : english
08980  * --------------------------------------------------------
08981  * vm-youhave   : you have 
08982  * vm-novou     : one new        : vm-zpravu  : message
08983  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08984  * vm-novych    : 5-infinite new : vm-zprav   : messages
08985  * vm-starou   : one old
08986  * vm-stare     : 2-4 old 
08987  * vm-starych   : 5-infinite old
08988  * jednu        : one   - falling 4. 
08989  * vm-no        : no  ( no messages )
08990  */
08991 
08992 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08993 {
08994    int res;
08995    res = ast_play_and_wait(chan, "vm-youhave");
08996    if (!res) {
08997       if (vms->newmessages) {
08998          if (vms->newmessages == 1) {
08999             res = ast_play_and_wait(chan, "digits/jednu");
09000          } else {
09001             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09002          }
09003          if (!res) {
09004             if ((vms->newmessages == 1))
09005                res = ast_play_and_wait(chan, "vm-novou");
09006             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
09007                res = ast_play_and_wait(chan, "vm-nove");
09008             if (vms->newmessages > 4)
09009                res = ast_play_and_wait(chan, "vm-novych");
09010          }
09011          if (vms->oldmessages && !res)
09012             res = ast_play_and_wait(chan, "vm-and");
09013          else if (!res) {
09014             if ((vms->newmessages == 1))
09015                res = ast_play_and_wait(chan, "vm-zpravu");
09016             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
09017                res = ast_play_and_wait(chan, "vm-zpravy");
09018             if (vms->newmessages > 4)
09019                res = ast_play_and_wait(chan, "vm-zprav");
09020          }
09021       }
09022       if (!res && vms->oldmessages) {
09023          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09024          if (!res) {
09025             if ((vms->oldmessages == 1))
09026                res = ast_play_and_wait(chan, "vm-starou");
09027             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09028                res = ast_play_and_wait(chan, "vm-stare");
09029             if (vms->oldmessages > 4)
09030                res = ast_play_and_wait(chan, "vm-starych");
09031          }
09032          if (!res) {
09033             if ((vms->oldmessages == 1))
09034                res = ast_play_and_wait(chan, "vm-zpravu");
09035             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09036                res = ast_play_and_wait(chan, "vm-zpravy");
09037             if (vms->oldmessages > 4)
09038                res = ast_play_and_wait(chan, "vm-zprav");
09039          }
09040       }
09041       if (!res) {
09042          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09043             res = ast_play_and_wait(chan, "vm-no");
09044             if (!res)
09045                res = ast_play_and_wait(chan, "vm-zpravy");
09046          }
09047       }
09048    }
09049    return res;
09050 }
09051 
09052 /* CHINESE (Taiwan) syntax */
09053 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09054 {
09055    int res;
09056    /* Introduce messages they have */
09057    res = ast_play_and_wait(chan, "vm-you");
09058 
09059    if (!res && vms->newmessages) {
09060       res = ast_play_and_wait(chan, "vm-have");
09061       if (!res)
09062          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09063       if (!res)
09064          res = ast_play_and_wait(chan, "vm-tong");
09065       if (!res)
09066          res = ast_play_and_wait(chan, "vm-INBOX");
09067       if (vms->oldmessages && !res)
09068          res = ast_play_and_wait(chan, "vm-and");
09069       else if (!res) 
09070          res = ast_play_and_wait(chan, "vm-messages");
09071    }
09072    if (!res && vms->oldmessages) {
09073       res = ast_play_and_wait(chan, "vm-have");
09074       if (!res)
09075          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09076       if (!res)
09077          res = ast_play_and_wait(chan, "vm-tong");
09078       if (!res)
09079          res = ast_play_and_wait(chan, "vm-Old");
09080       if (!res)
09081          res = ast_play_and_wait(chan, "vm-messages");
09082    }
09083    if (!res && !vms->oldmessages && !vms->newmessages) {
09084       res = ast_play_and_wait(chan, "vm-haveno");
09085       if (!res)
09086          res = ast_play_and_wait(chan, "vm-messages");
09087    }
09088    return res;
09089 }
09090 
09091 /* Vietnamese syntax */
09092 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09093 {
09094    int res;
09095 
09096    /* Introduce messages they have */
09097    res = ast_play_and_wait(chan, "vm-youhave");
09098    if (!res) {
09099       if (vms->newmessages) {
09100          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09101          if (!res)
09102             res = ast_play_and_wait(chan, "vm-INBOX");
09103          if (vms->oldmessages && !res)
09104             res = ast_play_and_wait(chan, "vm-and");
09105       }
09106       if (!res && vms->oldmessages) {
09107          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09108          if (!res)
09109             res = ast_play_and_wait(chan, "vm-Old");        
09110       }
09111       if (!res) {
09112          if (!vms->oldmessages && !vms->newmessages) {
09113             res = ast_play_and_wait(chan, "vm-no");
09114             if (!res)
09115                res = ast_play_and_wait(chan, "vm-message");
09116          }
09117       }
09118    }
09119    return res;
09120 }
09121 
09122 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09123 {
09124    char prefile[256];
09125    
09126    /* Notify the user that the temp greeting is set and give them the option to remove it */
09127    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09128    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09129       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09130       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09131          ast_play_and_wait(chan, "vm-tempgreetactive");
09132       }
09133       DISPOSE(prefile, -1);
09134    }
09135 
09136    /* Play voicemail intro - syntax is different for different languages */
09137    if (0) {
09138       return 0;
09139    } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) {  /* CZECH syntax */
09140       return vm_intro_cs(chan, vms);
09141    } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) {  /* deprecated CZECH syntax */
09142       static int deprecation_warning = 0;
09143       if (deprecation_warning++ % 10 == 0) {
09144          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09145       }
09146       return vm_intro_cs(chan, vms);
09147    } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) {  /* GERMAN syntax */
09148       return vm_intro_de(chan, vms);
09149    } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) {  /* SPANISH syntax */
09150       return vm_intro_es(chan, vms);
09151    } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) {  /* FRENCH syntax */
09152       return vm_intro_fr(chan, vms);
09153    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {  /* GREEK syntax */
09154       return vm_intro_gr(chan, vms);
09155    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* HEBREW syntax */
09156       return vm_intro_he(chan, vms);
09157    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN syntax */
09158       return vm_intro_it(chan, vms);
09159    } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {  /* DUTCH syntax */
09160       return vm_intro_nl(chan, vms);
09161    } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {  /* NORWEGIAN syntax */
09162       return vm_intro_no(chan, vms);
09163    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {  /* POLISH syntax */
09164       return vm_intro_pl(chan, vms);
09165    } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09166       return vm_intro_pt_BR(chan, vms);
09167    } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) {  /* PORTUGUESE syntax */
09168       return vm_intro_pt(chan, vms);
09169    } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) {  /* RUSSIAN syntax */
09170       return vm_intro_multilang(chan, vms, "n");
09171    } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) {  /* SWEDISH syntax */
09172       return vm_intro_se(chan, vms);
09173    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {  /* UKRAINIAN syntax */
09174       return vm_intro_multilang(chan, vms, "n");
09175    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
09176       return vm_intro_vi(chan, vms);
09177    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
09178       return vm_intro_zh(chan, vms);
09179    } else {                                             /* Default to ENGLISH */
09180       return vm_intro_en(chan, vms);
09181    }
09182 }
09183 
09184 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09185 {
09186    int res = 0;
09187    /* Play instructions and wait for new command */
09188    while (!res) {
09189       if (vms->starting) {
09190          if (vms->lastmsg > -1) {
09191             if (skipadvanced)
09192                res = ast_play_and_wait(chan, "vm-onefor-full");
09193             else
09194                res = ast_play_and_wait(chan, "vm-onefor");
09195             if (!res)
09196                res = vm_play_folder_name(chan, vms->vmbox);
09197          }
09198          if (!res) {
09199             if (skipadvanced)
09200                res = ast_play_and_wait(chan, "vm-opts-full");
09201             else
09202                res = ast_play_and_wait(chan, "vm-opts");
09203          }
09204       } else {
09205          /* Added for additional help */
09206          if (skipadvanced) {
09207             res = ast_play_and_wait(chan, "vm-onefor-full");
09208             if (!res)
09209                res = vm_play_folder_name(chan, vms->vmbox);
09210             res = ast_play_and_wait(chan, "vm-opts-full");
09211          }
09212          /* Logic:
09213           * If the current message is not the first OR
09214           * if we're listening to the first new message and there are
09215           * also urgent messages, then prompt for navigation to the
09216           * previous message
09217           */
09218          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09219             res = ast_play_and_wait(chan, "vm-prev");
09220          }
09221          if (!res && !skipadvanced)
09222             res = ast_play_and_wait(chan, "vm-advopts");
09223          if (!res)
09224             res = ast_play_and_wait(chan, "vm-repeat");
09225          /* Logic:
09226           * If we're not listening to the last message OR
09227           * we're listening to the last urgent message and there are
09228           * also new non-urgent messages, then prompt for navigation
09229           * to the next message
09230           */
09231          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09232             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09233             res = ast_play_and_wait(chan, "vm-next");
09234          }
09235          if (!res) {
09236             if (!vms->deleted[vms->curmsg])
09237                res = ast_play_and_wait(chan, "vm-delete");
09238             else
09239                res = ast_play_and_wait(chan, "vm-undelete");
09240             if (!res)
09241                res = ast_play_and_wait(chan, "vm-toforward");
09242             if (!res)
09243                res = ast_play_and_wait(chan, "vm-savemessage");
09244          }
09245       }
09246       if (!res) {
09247          res = ast_play_and_wait(chan, "vm-helpexit");
09248       }
09249       if (!res)
09250          res = ast_waitfordigit(chan, 6000);
09251       if (!res) {
09252          vms->repeats++;
09253          if (vms->repeats > 2) {
09254             res = 't';
09255          }
09256       }
09257    }
09258    return res;
09259 }
09260 
09261 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09262 {
09263    int res = 0;
09264    /* Play instructions and wait for new command */
09265    while (!res) {
09266       if (vms->lastmsg > -1) {
09267          res = ast_play_and_wait(chan, "vm-listen");
09268          if (!res)
09269             res = vm_play_folder_name(chan, vms->vmbox);
09270          if (!res)
09271             res = ast_play_and_wait(chan, "press");
09272          if (!res)
09273             res = ast_play_and_wait(chan, "digits/1");
09274       }
09275       if (!res)
09276          res = ast_play_and_wait(chan, "vm-opts");
09277       if (!res) {
09278          vms->starting = 0;
09279          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09280       }
09281    }
09282    return res;
09283 }
09284 
09285 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09286 {
09287    if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
09288       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09289    } else {             /* Default to ENGLISH */
09290       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09291    }
09292 }
09293 
09294 
09295 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09296 {
09297    int cmd = 0;
09298    int duration = 0;
09299    int tries = 0;
09300    char newpassword[80] = "";
09301    char newpassword2[80] = "";
09302    char prefile[PATH_MAX] = "";
09303    unsigned char buf[256];
09304    int bytes = 0;
09305 
09306    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09307    if (ast_adsi_available(chan)) {
09308       bytes += adsi_logo(buf + bytes);
09309       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09310       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09311       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09312       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09313       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09314    }
09315 
09316    /* If forcename is set, have the user record their name */
09317    if (ast_test_flag(vmu, VM_FORCENAME)) {
09318       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09319       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09320          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09321          if (cmd < 0 || cmd == 't' || cmd == '#')
09322             return cmd;
09323       }
09324    }
09325 
09326    /* If forcegreetings is set, have the user record their greetings */
09327    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09328       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09329       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09330          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09331          if (cmd < 0 || cmd == 't' || cmd == '#')
09332             return cmd;
09333       }
09334 
09335       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09336       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09337          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09338          if (cmd < 0 || cmd == 't' || cmd == '#')
09339             return cmd;
09340       }
09341    }
09342 
09343    /*
09344     * Change the password last since new users will be able to skip over any steps this one comes before
09345     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09346     */
09347    for (;;) {
09348       newpassword[1] = '\0';
09349       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09350       if (cmd == '#')
09351          newpassword[0] = '\0';
09352       if (cmd < 0 || cmd == 't' || cmd == '#')
09353          return cmd;
09354       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09355       if (cmd < 0 || cmd == 't' || cmd == '#')
09356          return cmd;
09357       cmd = check_password(vmu, newpassword); /* perform password validation */
09358       if (cmd != 0) {
09359          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09360          cmd = ast_play_and_wait(chan, vm_invalid_password);
09361       } else {
09362          newpassword2[1] = '\0';
09363          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09364          if (cmd == '#')
09365             newpassword2[0] = '\0';
09366          if (cmd < 0 || cmd == 't' || cmd == '#')
09367             return cmd;
09368          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09369          if (cmd < 0 || cmd == 't' || cmd == '#')
09370             return cmd;
09371          if (!strcmp(newpassword, newpassword2))
09372             break;
09373          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09374          cmd = ast_play_and_wait(chan, vm_mismatch);
09375       }
09376       if (++tries == 3)
09377          return -1;
09378       if (cmd != 0) {
09379          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09380       }
09381    }
09382    if (pwdchange & PWDCHANGE_INTERNAL)
09383       vm_change_password(vmu, newpassword);
09384    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09385       vm_change_password_shell(vmu, newpassword);
09386 
09387    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09388    cmd = ast_play_and_wait(chan, vm_passchanged);
09389 
09390    return cmd;
09391 }
09392 
09393 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09394 {
09395    int cmd = 0;
09396    int retries = 0;
09397    int duration = 0;
09398    char newpassword[80] = "";
09399    char newpassword2[80] = "";
09400    char prefile[PATH_MAX] = "";
09401    unsigned char buf[256];
09402    int bytes = 0;
09403 
09404    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09405    if (ast_adsi_available(chan)) {
09406       bytes += adsi_logo(buf + bytes);
09407       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09408       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09409       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09410       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09411       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09412    }
09413    while ((cmd >= 0) && (cmd != 't')) {
09414       if (cmd)
09415          retries = 0;
09416       switch (cmd) {
09417       case '1': /* Record your unavailable message */
09418          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09419          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09420          break;
09421       case '2':  /* Record your busy message */
09422          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09423          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09424          break;
09425       case '3': /* Record greeting */
09426          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09427          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09428          break;
09429       case '4':  /* manage the temporary greeting */
09430          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09431          break;
09432       case '5': /* change password */
09433          if (vmu->password[0] == '-') {
09434             cmd = ast_play_and_wait(chan, "vm-no");
09435             break;
09436          }
09437          newpassword[1] = '\0';
09438          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09439          if (cmd == '#')
09440             newpassword[0] = '\0';
09441          else {
09442             if (cmd < 0)
09443                break;
09444             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09445                break;
09446             }
09447          }
09448          cmd = check_password(vmu, newpassword); /* perform password validation */
09449          if (cmd != 0) {
09450             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09451             cmd = ast_play_and_wait(chan, vm_invalid_password);
09452             if (!cmd) {
09453                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09454             }
09455             break;
09456          }
09457          newpassword2[1] = '\0';
09458          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09459          if (cmd == '#')
09460             newpassword2[0] = '\0';
09461          else {
09462             if (cmd < 0)
09463                break;
09464 
09465             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09466                break;
09467             }
09468          }
09469          if (strcmp(newpassword, newpassword2)) {
09470             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09471             cmd = ast_play_and_wait(chan, vm_mismatch);
09472             if (!cmd) {
09473                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09474             }
09475             break;
09476          }
09477 
09478          if (pwdchange & PWDCHANGE_INTERNAL) {
09479             vm_change_password(vmu, newpassword);
09480          }
09481          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09482             vm_change_password_shell(vmu, newpassword);
09483          }
09484 
09485          ast_debug(1, "User %s set password to %s of length %d\n",
09486             vms->username, newpassword, (int) strlen(newpassword));
09487          cmd = ast_play_and_wait(chan, vm_passchanged);
09488          break;
09489       case '*':
09490          cmd = 't';
09491          break;
09492       default:
09493          cmd = 0;
09494          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09495          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09496          if (ast_fileexists(prefile, NULL, NULL)) {
09497             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09498          }
09499          DISPOSE(prefile, -1);
09500          if (!cmd) {
09501             cmd = ast_play_and_wait(chan, "vm-options");
09502          }
09503          if (!cmd) {
09504             cmd = ast_waitfordigit(chan, 6000);
09505          }
09506          if (!cmd) {
09507             retries++;
09508          }
09509          if (retries > 3) {
09510             cmd = 't';
09511          }
09512          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09513       }
09514    }
09515    if (cmd == 't')
09516       cmd = 0;
09517    return cmd;
09518 }
09519 
09520 /*!
09521  * \brief The handler for 'record a temporary greeting'. 
09522  * \param chan
09523  * \param vmu
09524  * \param vms
09525  * \param fmtc
09526  * \param record_gain
09527  *
09528  * This is option 4 from the mailbox options menu.
09529  * This function manages the following promptings:
09530  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09531  * 2: remove (delete) the temporary greeting.
09532  * *: return to the main menu.
09533  *
09534  * \return zero on success, -1 on error.
09535  */
09536 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09537 {
09538    int cmd = 0;
09539    int retries = 0;
09540    int duration = 0;
09541    char prefile[PATH_MAX] = "";
09542    unsigned char buf[256];
09543    int bytes = 0;
09544 
09545    if (ast_adsi_available(chan)) {
09546       bytes += adsi_logo(buf + bytes);
09547       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09548       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09549       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09550       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09551       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09552    }
09553 
09554    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09555    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09556    while ((cmd >= 0) && (cmd != 't')) {
09557       if (cmd)
09558          retries = 0;
09559       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09560       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09561          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09562          cmd = 't';  
09563       } else {
09564          switch (cmd) {
09565          case '1':
09566             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09567             break;
09568          case '2':
09569             DELETE(prefile, -1, prefile, vmu);
09570             ast_play_and_wait(chan, "vm-tempremoved");
09571             cmd = 't';  
09572             break;
09573          case '*': 
09574             cmd = 't';
09575             break;
09576          default:
09577             cmd = ast_play_and_wait(chan,
09578                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09579                   "vm-tempgreeting2" : "vm-tempgreeting");
09580             if (!cmd) {
09581                cmd = ast_waitfordigit(chan, 6000);
09582             }
09583             if (!cmd) {
09584                retries++;
09585             }
09586             if (retries > 3) {
09587                cmd = 't';
09588             }
09589             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09590          }
09591       }
09592       DISPOSE(prefile, -1);
09593    }
09594    if (cmd == 't')
09595       cmd = 0;
09596    return cmd;
09597 }
09598 
09599 /*!
09600  * \brief Greek syntax for 'You have N messages' greeting.
09601  * \param chan
09602  * \param vms
09603  * \param vmu
09604  *
09605  * \return zero on success, -1 on error.
09606  */   
09607 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09608 {
09609    int cmd = 0;
09610 
09611    if (vms->lastmsg > -1) {
09612       cmd = play_message(chan, vmu, vms);
09613    } else {
09614       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09615       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09616          if (!cmd) {
09617             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09618             cmd = ast_play_and_wait(chan, vms->fn);
09619          }
09620          if (!cmd)
09621             cmd = ast_play_and_wait(chan, "vm-messages");
09622       } else {
09623          if (!cmd)
09624             cmd = ast_play_and_wait(chan, "vm-messages");
09625          if (!cmd) {
09626             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09627             cmd = ast_play_and_wait(chan, vms->fn);
09628          }
09629       }
09630    } 
09631    return cmd;
09632 }
09633 
09634 /* Hebrew Syntax */
09635 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09636 {
09637    int cmd = 0;
09638 
09639    if (vms->lastmsg > -1) {
09640       cmd = play_message(chan, vmu, vms);
09641    } else {
09642       if (!strcasecmp(vms->fn, "INBOX")) {
09643          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09644       } else {
09645          cmd = ast_play_and_wait(chan, "vm-nomessages");
09646       }
09647    }
09648    return cmd;
09649 }
09650 
09651 /*! 
09652  * \brief Default English syntax for 'You have N messages' greeting.
09653  * \param chan
09654  * \param vms
09655  * \param vmu
09656  *
09657  * \return zero on success, -1 on error.
09658  */
09659 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09660 {
09661    int cmd = 0;
09662 
09663    if (vms->lastmsg > -1) {
09664       cmd = play_message(chan, vmu, vms);
09665    } else {
09666       cmd = ast_play_and_wait(chan, "vm-youhave");
09667       if (!cmd) 
09668          cmd = ast_play_and_wait(chan, "vm-no");
09669       if (!cmd) {
09670          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09671          cmd = ast_play_and_wait(chan, vms->fn);
09672       }
09673       if (!cmd)
09674          cmd = ast_play_and_wait(chan, "vm-messages");
09675    }
09676    return cmd;
09677 }
09678 
09679 /*! 
09680  *\brief Italian syntax for 'You have N messages' greeting.
09681  * \param chan
09682  * \param vms
09683  * \param vmu
09684  *
09685  * \return zero on success, -1 on error.
09686  */
09687 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09688 {
09689    int cmd;
09690 
09691    if (vms->lastmsg > -1) {
09692       cmd = play_message(chan, vmu, vms);
09693    } else {
09694       cmd = ast_play_and_wait(chan, "vm-no");
09695       if (!cmd)
09696          cmd = ast_play_and_wait(chan, "vm-message");
09697       if (!cmd) {
09698          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09699          cmd = ast_play_and_wait(chan, vms->fn);
09700       }
09701    }
09702    return cmd;
09703 }
09704 
09705 /*! 
09706  * \brief Spanish syntax for 'You have N messages' greeting.
09707  * \param chan
09708  * \param vms
09709  * \param vmu
09710  *
09711  * \return zero on success, -1 on error.
09712  */
09713 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09714 {
09715    int cmd;
09716 
09717    if (vms->lastmsg > -1) {
09718       cmd = play_message(chan, vmu, vms);
09719    } else {
09720       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09721       if (!cmd)
09722          cmd = ast_play_and_wait(chan, "vm-messages");
09723       if (!cmd) {
09724          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09725          cmd = ast_play_and_wait(chan, vms->fn);
09726       }
09727    }
09728    return cmd;
09729 }
09730 
09731 /*! 
09732  * \brief Portuguese syntax for 'You have N messages' greeting.
09733  * \param chan
09734  * \param vms
09735  * \param vmu
09736  *
09737  * \return zero on success, -1 on error.
09738  */
09739 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09740 {
09741    int cmd;
09742 
09743    if (vms->lastmsg > -1) {
09744       cmd = play_message(chan, vmu, vms);
09745    } else {
09746       cmd = ast_play_and_wait(chan, "vm-no");
09747       if (!cmd) {
09748          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09749          cmd = ast_play_and_wait(chan, vms->fn);
09750       }
09751       if (!cmd)
09752          cmd = ast_play_and_wait(chan, "vm-messages");
09753    }
09754    return cmd;
09755 }
09756 
09757 /*! 
09758  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09759  * \param chan
09760  * \param vms
09761  * \param vmu
09762  *
09763  * \return zero on success, -1 on error.
09764  */
09765 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09766 {
09767    int cmd;
09768 
09769    if (vms->lastmsg > -1) {
09770       cmd = play_message(chan, vmu, vms);
09771    } else {
09772       cmd = ast_play_and_wait(chan, "vm-you");
09773       if (!cmd) 
09774          cmd = ast_play_and_wait(chan, "vm-haveno");
09775       if (!cmd)
09776          cmd = ast_play_and_wait(chan, "vm-messages");
09777       if (!cmd) {
09778          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09779          cmd = ast_play_and_wait(chan, vms->fn);
09780       }
09781    }
09782    return cmd;
09783 }
09784 
09785 /*! 
09786  * \brief Vietnamese syntax for 'You have N messages' greeting.
09787  * \param chan
09788  * \param vms
09789  * \param vmu
09790  *
09791  * \return zero on success, -1 on error.
09792  */
09793 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09794 {
09795    int cmd = 0;
09796 
09797    if (vms->lastmsg > -1) {
09798       cmd = play_message(chan, vmu, vms);
09799    } else {
09800       cmd = ast_play_and_wait(chan, "vm-no");
09801       if (!cmd) {
09802          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09803          cmd = ast_play_and_wait(chan, vms->fn);
09804       }
09805    }
09806    return cmd;
09807 }
09808 
09809 /*!
09810  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09811  * \param chan The channel for the current user. We read the language property from this.
09812  * \param vms passed into the language-specific vm_browse_messages function.
09813  * \param vmu passed into the language-specific vm_browse_messages function.
09814  * 
09815  * The method to be invoked is determined by the value of language code property in the user's channel.
09816  * The default (when unable to match) is to use english.
09817  *
09818  * \return zero on success, -1 on error.
09819  */
09820 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09821 {
09822    if (!strncasecmp(ast_channel_language(chan), "es", 2)) {         /* SPANISH */
09823       return vm_browse_messages_es(chan, vms, vmu);
09824    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {  /* GREEK */
09825       return vm_browse_messages_gr(chan, vms, vmu);
09826    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* HEBREW */
09827       return vm_browse_messages_he(chan, vms, vmu);
09828    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN */
09829       return vm_browse_messages_it(chan, vms, vmu);
09830    } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) {  /* PORTUGUESE */
09831       return vm_browse_messages_pt(chan, vms, vmu);
09832    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {  /* VIETNAMESE */
09833       return vm_browse_messages_vi(chan, vms, vmu);
09834    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) {  /* CHINESE (Taiwan) */
09835       return vm_browse_messages_zh(chan, vms, vmu);
09836    } else {                                             /* Default to English syntax */
09837       return vm_browse_messages_en(chan, vms, vmu);
09838    }
09839 }
09840 
09841 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09842          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09843          int skipuser, int max_logins, int silent)
09844 {
09845    int useadsi = 0, valid = 0, logretries = 0;
09846    char password[AST_MAX_EXTENSION]="", *passptr;
09847    struct ast_vm_user vmus, *vmu = NULL;
09848 
09849    /* If ADSI is supported, setup login screen */
09850    adsi_begin(chan, &useadsi);
09851    if (!skipuser && useadsi)
09852       adsi_login(chan);
09853    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09854    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", ast_channel_language(chan))) {
09855       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09856       return -1;
09857    }
09858 
09859    /* Authenticate them and get their mailbox/password */
09860 
09861    while (!valid && (logretries < max_logins)) {
09862       /* Prompt for, and read in the username */
09863       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09864          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09865          return -1;
09866       }
09867       if (ast_strlen_zero(mailbox)) {
09868          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09869             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09870          } else {
09871             ast_verb(3, "Username not entered\n"); 
09872             return -1;
09873          }
09874       } else if (mailbox[0] == '*') {
09875          /* user entered '*' */
09876          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09877          if (ast_exists_extension(chan, chan->context, "a", 1,
09878             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09879             return -1;
09880          }
09881          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09882          mailbox[0] = '\0';
09883       }
09884 
09885       if (useadsi)
09886          adsi_password(chan);
09887 
09888       if (!ast_strlen_zero(prefix)) {
09889          char fullusername[80] = "";
09890          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09891          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09892          ast_copy_string(mailbox, fullusername, mailbox_size);
09893       }
09894 
09895       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09896       vmu = find_user(&vmus, context, mailbox);
09897       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09898          /* saved password is blank, so don't bother asking */
09899          password[0] = '\0';
09900       } else {
09901          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09902          if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
09903             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09904             return -1;
09905          }
09906          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09907             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09908             return -1;
09909          } else if (password[0] == '*') {
09910             /* user entered '*' */
09911             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09912             if (ast_exists_extension(chan, chan->context, "a", 1,
09913                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09914                mailbox[0] = '*';
09915                return -1;
09916             }
09917             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09918             mailbox[0] = '\0';
09919             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09920             vmu = NULL;
09921          }
09922       }
09923 
09924       if (vmu) {
09925          passptr = vmu->password;
09926          if (passptr[0] == '-') passptr++;
09927       }
09928       if (vmu && !strcmp(passptr, password))
09929          valid++;
09930       else {
09931          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09932          if (!ast_strlen_zero(prefix))
09933             mailbox[0] = '\0';
09934       }
09935       logretries++;
09936       if (!valid) {
09937          if (skipuser || logretries >= max_logins) {
09938             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09939             if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
09940                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09941                return -1;
09942             }
09943          } else {
09944             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09945             if (useadsi)
09946                adsi_login(chan);
09947             if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
09948                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09949                return -1;
09950             }
09951          }
09952          if (ast_waitstream(chan, "")) /* Channel is hung up */
09953             return -1;
09954       }
09955    }
09956    if (!valid && (logretries >= max_logins)) {
09957       ast_stopstream(chan);
09958       ast_play_and_wait(chan, "vm-goodbye");
09959       return -1;
09960    }
09961    if (vmu && !skipuser) {
09962       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09963    }
09964    return 0;
09965 }
09966 
09967 static int vm_execmain(struct ast_channel *chan, const char *data)
09968 {
09969    /* XXX This is, admittedly, some pretty horrendous code.  For some
09970       reason it just seemed a lot easier to do with GOTO's.  I feel
09971       like I'm back in my GWBASIC days. XXX */
09972    int res = -1;
09973    int cmd = 0;
09974    int valid = 0;
09975    char prefixstr[80] ="";
09976    char ext_context[256]="";
09977    int box;
09978    int useadsi = 0;
09979    int skipuser = 0;
09980    struct vm_state vms;
09981    struct ast_vm_user *vmu = NULL, vmus;
09982    char *context = NULL;
09983    int silentexit = 0;
09984    struct ast_flags flags = { 0 };
09985    signed char record_gain = 0;
09986    int play_auto = 0;
09987    int play_folder = 0;
09988    int in_urgent = 0;
09989 #ifdef IMAP_STORAGE
09990    int deleted = 0;
09991 #endif
09992 
09993    /* Add the vm_state to the active list and keep it active */
09994    memset(&vms, 0, sizeof(vms));
09995 
09996    vms.lastmsg = -1;
09997 
09998    memset(&vmus, 0, sizeof(vmus));
09999 
10000    ast_test_suite_event_notify("START", "Message: vm_execmain started");
10001    if (chan->_state != AST_STATE_UP) {
10002       ast_debug(1, "Before ast_answer\n");
10003       ast_answer(chan);
10004    }
10005 
10006    if (!ast_strlen_zero(data)) {
10007       char *opts[OPT_ARG_ARRAY_SIZE];
10008       char *parse;
10009       AST_DECLARE_APP_ARGS(args,
10010          AST_APP_ARG(argv0);
10011          AST_APP_ARG(argv1);
10012       );
10013 
10014       parse = ast_strdupa(data);
10015 
10016       AST_STANDARD_APP_ARGS(args, parse);
10017 
10018       if (args.argc == 2) {
10019          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10020             return -1;
10021          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10022             int gain;
10023             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
10024                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10025                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10026                   return -1;
10027                } else {
10028                   record_gain = (signed char) gain;
10029                }
10030             } else {
10031                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
10032             }
10033          }
10034          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
10035             play_auto = 1;
10036             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
10037                /* See if it is a folder name first */
10038                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
10039                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
10040                      play_folder = -1;
10041                   }
10042                } else {
10043                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10044                }
10045             } else {
10046                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10047             }
10048             if (play_folder > 9 || play_folder < 0) {
10049                ast_log(AST_LOG_WARNING,
10050                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10051                   opts[OPT_ARG_PLAYFOLDER]);
10052                play_folder = 0;
10053             }
10054          }
10055       } else {
10056          /* old style options parsing */
10057          while (*(args.argv0)) {
10058             if (*(args.argv0) == 's')
10059                ast_set_flag(&flags, OPT_SILENT);
10060             else if (*(args.argv0) == 'p')
10061                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10062             else 
10063                break;
10064             (args.argv0)++;
10065          }
10066 
10067       }
10068 
10069       valid = ast_test_flag(&flags, OPT_SILENT);
10070 
10071       if ((context = strchr(args.argv0, '@')))
10072          *context++ = '\0';
10073 
10074       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10075          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10076       else
10077          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10078 
10079       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10080          skipuser++;
10081       else
10082          valid = 0;
10083    }
10084 
10085    if (!valid)
10086       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10087 
10088    ast_debug(1, "After vm_authenticate\n");
10089 
10090    if (vms.username[0] == '*') {
10091       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10092 
10093       /* user entered '*' */
10094       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10095          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10096          res = 0; /* prevent hangup */
10097          goto out;
10098       }
10099    }
10100 
10101    if (!res) {
10102       valid = 1;
10103       if (!skipuser)
10104          vmu = &vmus;
10105    } else {
10106       res = 0;
10107    }
10108 
10109    /* If ADSI is supported, setup login screen */
10110    adsi_begin(chan, &useadsi);
10111 
10112    ast_test_suite_assert(valid);
10113    if (!valid) {
10114       goto out;
10115    }
10116    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10117 
10118 #ifdef IMAP_STORAGE
10119    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10120    pthread_setspecific(ts_vmstate.key, &vms);
10121 
10122    vms.interactive = 1;
10123    vms.updated = 1;
10124    if (vmu)
10125       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10126    vmstate_insert(&vms);
10127    init_vm_state(&vms);
10128 #endif
10129 
10130    /* Set language from config to override channel language */
10131    if (!ast_strlen_zero(vmu->language))
10132       ast_channel_language_set(chan, vmu->language);
10133 
10134    /* Retrieve urgent, old and new message counts */
10135    ast_debug(1, "Before open_mailbox\n");
10136    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10137    if (res < 0)
10138       goto out;
10139    vms.oldmessages = vms.lastmsg + 1;
10140    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10141    /* check INBOX */
10142    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10143    if (res < 0)
10144       goto out;
10145    vms.newmessages = vms.lastmsg + 1;
10146    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10147    /* Start in Urgent */
10148    in_urgent = 1;
10149    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10150    if (res < 0)
10151       goto out;
10152    vms.urgentmessages = vms.lastmsg + 1;
10153    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10154 
10155    /* Select proper mailbox FIRST!! */
10156    if (play_auto) {
10157       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10158       if (vms.urgentmessages) {
10159          in_urgent = 1;
10160          res = open_mailbox(&vms, vmu, 11);
10161       } else {
10162          in_urgent = 0;
10163          res = open_mailbox(&vms, vmu, play_folder);
10164       }
10165       if (res < 0)
10166          goto out;
10167 
10168       /* If there are no new messages, inform the user and hangup */
10169       if (vms.lastmsg == -1) {
10170          in_urgent = 0;
10171          cmd = vm_browse_messages(chan, &vms, vmu);
10172          res = 0;
10173          goto out;
10174       }
10175    } else {
10176       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10177          /* If we only have old messages start here */
10178          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10179          in_urgent = 0;
10180          play_folder = 1;
10181          if (res < 0)
10182             goto out;
10183       } else if (!vms.urgentmessages && vms.newmessages) {
10184          /* If we have new messages but none are urgent */
10185          in_urgent = 0;
10186          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10187          if (res < 0)
10188             goto out;
10189       }
10190    }
10191 
10192    if (useadsi)
10193       adsi_status(chan, &vms);
10194    res = 0;
10195 
10196    /* Check to see if this is a new user */
10197    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10198       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10199       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10200          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10201       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10202       if ((cmd == 't') || (cmd == '#')) {
10203          /* Timeout */
10204          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10205          res = 0;
10206          goto out;
10207       } else if (cmd < 0) {
10208          /* Hangup */
10209          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10210          res = -1;
10211          goto out;
10212       }
10213    }
10214 #ifdef IMAP_STORAGE
10215       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10216       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10217          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10218          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10219       }
10220       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10221       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10222          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10223          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10224       }
10225 #endif
10226 
10227    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10228    if (play_auto) {
10229       cmd = '1';
10230    } else {
10231       cmd = vm_intro(chan, vmu, &vms);
10232    }
10233    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10234 
10235    vms.repeats = 0;
10236    vms.starting = 1;
10237    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10238       /* Run main menu */
10239       switch (cmd) {
10240       case '1': /* First message */
10241          vms.curmsg = 0;
10242          /* Fall through */
10243       case '5': /* Play current message */
10244          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10245          cmd = vm_browse_messages(chan, &vms, vmu);
10246          break;
10247       case '2': /* Change folders */
10248          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10249          if (useadsi)
10250             adsi_folders(chan, 0, "Change to folder...");
10251 
10252          cmd = get_folder2(chan, "vm-changeto", 0);
10253          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10254          if (cmd == '#') {
10255             cmd = 0;
10256          } else if (cmd > 0) {
10257             cmd = cmd - '0';
10258             res = close_mailbox(&vms, vmu);
10259             if (res == ERROR_LOCK_PATH)
10260                goto out;
10261             /* If folder is not urgent, set in_urgent to zero! */
10262             if (cmd != 11) in_urgent = 0;
10263             res = open_mailbox(&vms, vmu, cmd);
10264             if (res < 0)
10265                goto out;
10266             play_folder = cmd;
10267             cmd = 0;
10268          }
10269          if (useadsi)
10270             adsi_status2(chan, &vms);
10271 
10272          if (!cmd) {
10273             cmd = vm_play_folder_name(chan, vms.vmbox);
10274          }
10275 
10276          vms.starting = 1;
10277          break;
10278       case '3': /* Advanced options */
10279          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10280          cmd = 0;
10281          vms.repeats = 0;
10282          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10283             switch (cmd) {
10284             case '1': /* Reply */
10285                if (vms.lastmsg > -1 && !vms.starting) {
10286                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10287                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10288                      res = cmd;
10289                      goto out;
10290                   }
10291                } else {
10292                   cmd = ast_play_and_wait(chan, "vm-sorry");
10293                }
10294                cmd = 't';
10295                break;
10296             case '2': /* Callback */
10297                if (!vms.starting)
10298                   ast_verb(3, "Callback Requested\n");
10299                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10300                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10301                   if (cmd == 9) {
10302                      silentexit = 1;
10303                      goto out;
10304                   } else if (cmd == ERROR_LOCK_PATH) {
10305                      res = cmd;
10306                      goto out;
10307                   }
10308                } else {
10309                   cmd = ast_play_and_wait(chan, "vm-sorry");
10310                }
10311                cmd = 't';
10312                break;
10313             case '3': /* Envelope */
10314                if (vms.lastmsg > -1 && !vms.starting) {
10315                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10316                   if (cmd == ERROR_LOCK_PATH) {
10317                      res = cmd;
10318                      goto out;
10319                   }
10320                } else {
10321                   cmd = ast_play_and_wait(chan, "vm-sorry");
10322                }
10323                cmd = 't';
10324                break;
10325             case '4': /* Dialout */
10326                if (!ast_strlen_zero(vmu->dialout)) {
10327                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10328                   if (cmd == 9) {
10329                      silentexit = 1;
10330                      goto out;
10331                   }
10332                } else {
10333                   cmd = ast_play_and_wait(chan, "vm-sorry");
10334                }
10335                cmd = 't';
10336                break;
10337 
10338             case '5': /* Leave VoiceMail */
10339                if (ast_test_flag(vmu, VM_SVMAIL)) {
10340                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10341                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10342                      res = cmd;
10343                      goto out;
10344                   }
10345                } else {
10346                   cmd = ast_play_and_wait(chan, "vm-sorry");
10347                }
10348                cmd = 't';
10349                break;
10350 
10351             case '*': /* Return to main menu */
10352                cmd = 't';
10353                break;
10354 
10355             default:
10356                cmd = 0;
10357                if (!vms.starting) {
10358                   cmd = ast_play_and_wait(chan, "vm-toreply");
10359                }
10360                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10361                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10362                }
10363                if (!cmd && !vms.starting) {
10364                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10365                }
10366                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10367                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10368                }
10369                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10370                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10371                }
10372                if (!cmd) {
10373                   cmd = ast_play_and_wait(chan, "vm-starmain");
10374                }
10375                if (!cmd) {
10376                   cmd = ast_waitfordigit(chan, 6000);
10377                }
10378                if (!cmd) {
10379                   vms.repeats++;
10380                }
10381                if (vms.repeats > 3) {
10382                   cmd = 't';
10383                }
10384                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10385             }
10386          }
10387          if (cmd == 't') {
10388             cmd = 0;
10389             vms.repeats = 0;
10390          }
10391          break;
10392       case '4': /* Go to the previous message */
10393          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10394          if (vms.curmsg > 0) {
10395             vms.curmsg--;
10396             cmd = play_message(chan, vmu, &vms);
10397          } else {
10398             /* Check if we were listening to new
10399                messages.  If so, go to Urgent messages
10400                instead of saying "no more messages"
10401             */
10402             if (in_urgent == 0 && vms.urgentmessages > 0) {
10403                /* Check for Urgent messages */
10404                in_urgent = 1;
10405                res = close_mailbox(&vms, vmu);
10406                if (res == ERROR_LOCK_PATH)
10407                   goto out;
10408                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10409                if (res < 0)
10410                   goto out;
10411                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10412                vms.curmsg = vms.lastmsg;
10413                if (vms.lastmsg < 0) {
10414                   cmd = ast_play_and_wait(chan, "vm-nomore");
10415                }
10416             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10417                vms.curmsg = vms.lastmsg;
10418                cmd = play_message(chan, vmu, &vms);
10419             } else {
10420                cmd = ast_play_and_wait(chan, "vm-nomore");
10421             }
10422          }
10423          break;
10424       case '6': /* Go to the next message */
10425          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10426          if (vms.curmsg < vms.lastmsg) {
10427             vms.curmsg++;
10428             cmd = play_message(chan, vmu, &vms);
10429          } else {
10430             if (in_urgent && vms.newmessages > 0) {
10431                /* Check if we were listening to urgent
10432                 * messages.  If so, go to regular new messages
10433                 * instead of saying "no more messages"
10434                 */
10435                in_urgent = 0;
10436                res = close_mailbox(&vms, vmu);
10437                if (res == ERROR_LOCK_PATH)
10438                   goto out;
10439                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10440                if (res < 0)
10441                   goto out;
10442                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10443                vms.curmsg = -1;
10444                if (vms.lastmsg < 0) {
10445                   cmd = ast_play_and_wait(chan, "vm-nomore");
10446                }
10447             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10448                vms.curmsg = 0;
10449                cmd = play_message(chan, vmu, &vms);
10450             } else {
10451                cmd = ast_play_and_wait(chan, "vm-nomore");
10452             }
10453          }
10454          break;
10455       case '7': /* Delete the current message */
10456          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10457             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10458             if (useadsi)
10459                adsi_delete(chan, &vms);
10460             if (vms.deleted[vms.curmsg]) {
10461                if (play_folder == 0) {
10462                   if (in_urgent) {
10463                      vms.urgentmessages--;
10464                   } else {
10465                      vms.newmessages--;
10466                   }
10467                }
10468                else if (play_folder == 1)
10469                   vms.oldmessages--;
10470                cmd = ast_play_and_wait(chan, "vm-deleted");
10471             } else {
10472                if (play_folder == 0) {
10473                   if (in_urgent) {
10474                      vms.urgentmessages++;
10475                   } else {
10476                      vms.newmessages++;
10477                   }
10478                }
10479                else if (play_folder == 1)
10480                   vms.oldmessages++;
10481                cmd = ast_play_and_wait(chan, "vm-undeleted");
10482             }
10483             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10484                if (vms.curmsg < vms.lastmsg) {
10485                   vms.curmsg++;
10486                   cmd = play_message(chan, vmu, &vms);
10487                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10488                   vms.curmsg = 0;
10489                   cmd = play_message(chan, vmu, &vms);
10490                } else {
10491                   /* Check if we were listening to urgent
10492                      messages.  If so, go to regular new messages
10493                      instead of saying "no more messages"
10494                   */
10495                   if (in_urgent == 1) {
10496                      /* Check for new messages */
10497                      in_urgent = 0;
10498                      res = close_mailbox(&vms, vmu);
10499                      if (res == ERROR_LOCK_PATH)
10500                         goto out;
10501                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10502                      if (res < 0)
10503                         goto out;
10504                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10505                      vms.curmsg = -1;
10506                      if (vms.lastmsg < 0) {
10507                         cmd = ast_play_and_wait(chan, "vm-nomore");
10508                      }
10509                   } else {
10510                      cmd = ast_play_and_wait(chan, "vm-nomore");
10511                   }
10512                }
10513             }
10514          } else /* Delete not valid if we haven't selected a message */
10515             cmd = 0;
10516 #ifdef IMAP_STORAGE
10517          deleted = 1;
10518 #endif
10519          break;
10520 
10521       case '8': /* Forward the current message */
10522          if (vms.lastmsg > -1) {
10523             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10524             if (cmd == ERROR_LOCK_PATH) {
10525                res = cmd;
10526                goto out;
10527             }
10528          } else {
10529             /* Check if we were listening to urgent
10530                messages.  If so, go to regular new messages
10531                instead of saying "no more messages"
10532             */
10533             if (in_urgent == 1 && vms.newmessages > 0) {
10534                /* Check for new messages */
10535                in_urgent = 0;
10536                res = close_mailbox(&vms, vmu);
10537                if (res == ERROR_LOCK_PATH)
10538                   goto out;
10539                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10540                if (res < 0)
10541                   goto out;
10542                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10543                vms.curmsg = -1;
10544                if (vms.lastmsg < 0) {
10545                   cmd = ast_play_and_wait(chan, "vm-nomore");
10546                }
10547             } else {
10548                cmd = ast_play_and_wait(chan, "vm-nomore");
10549             }
10550          }
10551          break;
10552       case '9': /* Save message to folder */
10553          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10554          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10555             /* No message selected */
10556             cmd = 0;
10557             break;
10558          }
10559          if (useadsi)
10560             adsi_folders(chan, 1, "Save to folder...");
10561          cmd = get_folder2(chan, "vm-savefolder", 1);
10562          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10563          box = 0; /* Shut up compiler */
10564          if (cmd == '#') {
10565             cmd = 0;
10566             break;
10567          } else if (cmd > 0) {
10568             box = cmd = cmd - '0';
10569             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10570             if (cmd == ERROR_LOCK_PATH) {
10571                res = cmd;
10572                goto out;
10573 #ifndef IMAP_STORAGE
10574             } else if (!cmd) {
10575                vms.deleted[vms.curmsg] = 1;
10576 #endif
10577             } else {
10578                vms.deleted[vms.curmsg] = 0;
10579                vms.heard[vms.curmsg] = 0;
10580             }
10581          }
10582          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10583          if (useadsi)
10584             adsi_message(chan, &vms);
10585          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10586          if (!cmd) {
10587             cmd = ast_play_and_wait(chan, "vm-message");
10588             if (!cmd) 
10589                cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
10590             if (!cmd)
10591                cmd = ast_play_and_wait(chan, "vm-savedto");
10592             if (!cmd)
10593                cmd = vm_play_folder_name(chan, vms.fn);
10594          } else {
10595             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10596          }
10597          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10598             if (vms.curmsg < vms.lastmsg) {
10599                vms.curmsg++;
10600                cmd = play_message(chan, vmu, &vms);
10601             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10602                vms.curmsg = 0;
10603                cmd = play_message(chan, vmu, &vms);
10604             } else {
10605                /* Check if we were listening to urgent
10606                   messages.  If so, go to regular new messages
10607                   instead of saying "no more messages"
10608                */
10609                if (in_urgent == 1 && vms.newmessages > 0) {
10610                   /* Check for new messages */
10611                   in_urgent = 0;
10612                   res = close_mailbox(&vms, vmu);
10613                   if (res == ERROR_LOCK_PATH)
10614                      goto out;
10615                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10616                   if (res < 0)
10617                      goto out;
10618                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10619                   vms.curmsg = -1;
10620                   if (vms.lastmsg < 0) {
10621                      cmd = ast_play_and_wait(chan, "vm-nomore");
10622                   }
10623                } else {
10624                   cmd = ast_play_and_wait(chan, "vm-nomore");
10625                }
10626             }
10627          }
10628          break;
10629       case '*': /* Help */
10630          if (!vms.starting) {
10631             cmd = ast_play_and_wait(chan, "vm-onefor");
10632             if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
10633                cmd = ast_play_and_wait(chan, "vm-for");
10634             }
10635             if (!cmd)
10636                cmd = vm_play_folder_name(chan, vms.vmbox);
10637             if (!cmd)
10638                cmd = ast_play_and_wait(chan, "vm-opts");
10639             if (!cmd)
10640                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10641          } else
10642             cmd = 0;
10643          break;
10644       case '0': /* Mailbox options */
10645          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10646          if (useadsi)
10647             adsi_status(chan, &vms);
10648          break;
10649       default: /* Nothing */
10650          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10651          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10652          break;
10653       }
10654    }
10655    if ((cmd == 't') || (cmd == '#')) {
10656       /* Timeout */
10657       res = 0;
10658    } else {
10659       /* Hangup */
10660       res = -1;
10661    }
10662 
10663 out:
10664    if (res > -1) {
10665       ast_stopstream(chan);
10666       adsi_goodbye(chan);
10667       if (valid && res != OPERATOR_EXIT) {
10668          if (silentexit)
10669             res = ast_play_and_wait(chan, "vm-dialout");
10670          else 
10671             res = ast_play_and_wait(chan, "vm-goodbye");
10672       }
10673       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10674          res = 0;
10675       }
10676       if (useadsi)
10677          ast_adsi_unload_session(chan);
10678    }
10679    if (vmu)
10680       close_mailbox(&vms, vmu);
10681    if (valid) {
10682       int new = 0, old = 0, urgent = 0;
10683       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10684       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10685       /* Urgent flag not passwd to externnotify here */
10686       run_externnotify(vmu->context, vmu->mailbox, NULL);
10687       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10688       queue_mwi_event(ext_context, urgent, new, old);
10689    }
10690 #ifdef IMAP_STORAGE
10691    /* expunge message - use UID Expunge if supported on IMAP server*/
10692    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10693    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10694       ast_mutex_lock(&vms.lock);
10695 #ifdef HAVE_IMAP_TK2006
10696       if (LEVELUIDPLUS (vms.mailstream)) {
10697          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10698       } else
10699 #endif
10700          mail_expunge(vms.mailstream);
10701       ast_mutex_unlock(&vms.lock);
10702    }
10703    /*  before we delete the state, we should copy pertinent info
10704     *  back to the persistent model */
10705    if (vmu) {
10706       vmstate_delete(&vms);
10707    }
10708 #endif
10709    if (vmu)
10710       free_user(vmu);
10711 
10712 #ifdef IMAP_STORAGE
10713    pthread_setspecific(ts_vmstate.key, NULL);
10714 #endif
10715    return res;
10716 }
10717 
10718 static int vm_exec(struct ast_channel *chan, const char *data)
10719 {
10720    int res = 0;
10721    char *tmp;
10722    struct leave_vm_options leave_options;
10723    struct ast_flags flags = { 0 };
10724    char *opts[OPT_ARG_ARRAY_SIZE];
10725    AST_DECLARE_APP_ARGS(args,
10726       AST_APP_ARG(argv0);
10727       AST_APP_ARG(argv1);
10728    );
10729    
10730    memset(&leave_options, 0, sizeof(leave_options));
10731 
10732    if (chan->_state != AST_STATE_UP)
10733       ast_answer(chan);
10734 
10735    if (!ast_strlen_zero(data)) {
10736       tmp = ast_strdupa(data);
10737       AST_STANDARD_APP_ARGS(args, tmp);
10738       if (args.argc == 2) {
10739          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10740             return -1;
10741          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10742          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10743             int gain;
10744 
10745             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10746                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10747                return -1;
10748             } else {
10749                leave_options.record_gain = (signed char) gain;
10750             }
10751          }
10752          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10753             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10754                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10755          }
10756       }
10757    } else {
10758       char temp[256];
10759       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10760       if (res < 0)
10761          return res;
10762       if (ast_strlen_zero(temp))
10763          return 0;
10764       args.argv0 = ast_strdupa(temp);
10765    }
10766 
10767    res = leave_voicemail(chan, args.argv0, &leave_options);
10768    if (res == 't') {
10769       ast_play_and_wait(chan, "vm-goodbye");
10770       res = 0;
10771    }
10772 
10773    if (res == OPERATOR_EXIT) {
10774       res = 0;
10775    }
10776 
10777    if (res == ERROR_LOCK_PATH) {
10778       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10779       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10780       res = 0;
10781    }
10782 
10783    return res;
10784 }
10785 
10786 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10787 {
10788    struct ast_vm_user *vmu;
10789 
10790    if (!ast_strlen_zero(box) && box[0] == '*') {
10791       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10792             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10793             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10794             "\n\tand will be ignored.\n", box, context);
10795       return NULL;
10796    }
10797 
10798    AST_LIST_TRAVERSE(&users, vmu, list) {
10799       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10800          if (strcasecmp(vmu->context, context)) {
10801             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10802                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10803                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10804                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10805          }
10806          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10807          return NULL;
10808       }
10809       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10810          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10811          return NULL;
10812       }
10813    }
10814    
10815    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10816       return NULL;
10817    
10818    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10819    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10820 
10821    AST_LIST_INSERT_TAIL(&users, vmu, list);
10822    
10823    return vmu;
10824 }
10825 
10826 static int append_mailbox(const char *context, const char *box, const char *data)
10827 {
10828    /* Assumes lock is already held */
10829    char *tmp;
10830    char *stringp;
10831    char *s;
10832    struct ast_vm_user *vmu;
10833    char *mailbox_full;
10834    int new = 0, old = 0, urgent = 0;
10835    char secretfn[PATH_MAX] = "";
10836 
10837    tmp = ast_strdupa(data);
10838 
10839    if (!(vmu = find_or_create(context, box)))
10840       return -1;
10841 
10842    populate_defaults(vmu);
10843 
10844    stringp = tmp;
10845    if ((s = strsep(&stringp, ","))) {
10846       if (!ast_strlen_zero(s) && s[0] == '*') {
10847          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10848             "\n\tmust be reset in voicemail.conf.\n", box);
10849       }
10850       /* assign password regardless of validity to prevent NULL password from being assigned */
10851       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10852    }
10853    if (stringp && (s = strsep(&stringp, ","))) {
10854       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10855    }
10856    if (stringp && (s = strsep(&stringp, ","))) {
10857       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10858    }
10859    if (stringp && (s = strsep(&stringp, ","))) {
10860       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10861    }
10862    if (stringp && (s = strsep(&stringp, ","))) {
10863       apply_options(vmu, s);
10864    }
10865 
10866    switch (vmu->passwordlocation) {
10867    case OPT_PWLOC_SPOOLDIR:
10868       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10869       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10870    }
10871 
10872    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10873    strcpy(mailbox_full, box);
10874    strcat(mailbox_full, "@");
10875    strcat(mailbox_full, context);
10876 
10877    inboxcount2(mailbox_full, &urgent, &new, &old);
10878    queue_mwi_event(mailbox_full, urgent, new, old);
10879 
10880    return 0;
10881 }
10882 
10883 AST_TEST_DEFINE(test_voicemail_vmuser)
10884 {
10885    int res = 0;
10886    struct ast_vm_user *vmu;
10887    /* language parameter seems to only be used for display in manager action */
10888    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10889       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10890       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10891       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10892       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10893       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10894       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10895       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10896       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10897 #ifdef IMAP_STORAGE
10898    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10899       "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
10900 #endif
10901 
10902    switch (cmd) {
10903    case TEST_INIT:
10904       info->name = "vmuser";
10905       info->category = "/apps/app_voicemail/";
10906       info->summary = "Vmuser unit test";
10907       info->description =
10908          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10909       return AST_TEST_NOT_RUN;
10910    case TEST_EXECUTE:
10911       break;
10912    }
10913 
10914    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10915       return AST_TEST_NOT_RUN;
10916    }
10917    ast_set_flag(vmu, VM_ALLOCED);
10918    populate_defaults(vmu);
10919 
10920    apply_options(vmu, options_string);
10921 
10922    if (!ast_test_flag(vmu, VM_ATTACH)) {
10923       ast_test_status_update(test, "Parse failure for attach option\n");
10924       res = 1;
10925    }
10926    if (strcasecmp(vmu->attachfmt, "wav49")) {
10927       ast_test_status_update(test, "Parse failure for attachftm option\n");
10928       res = 1;
10929    }
10930    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10931       ast_test_status_update(test, "Parse failure for serveremail option\n");
10932       res = 1;
10933    }
10934    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10935       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10936       res = 1;
10937    }
10938    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10939       ast_test_status_update(test, "Parse failure for emailbody option\n");
10940       res = 1;
10941    }
10942    if (strcasecmp(vmu->zonetag, "central")) {
10943       ast_test_status_update(test, "Parse failure for tz option\n");
10944       res = 1;
10945    }
10946    if (!ast_test_flag(vmu, VM_DELETE)) {
10947       ast_test_status_update(test, "Parse failure for delete option\n");
10948       res = 1;
10949    }
10950    if (!ast_test_flag(vmu, VM_SAYCID)) {
10951       ast_test_status_update(test, "Parse failure for saycid option\n");
10952       res = 1;
10953    }
10954    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10955       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10956       res = 1;
10957    }
10958    if (!ast_test_flag(vmu, VM_REVIEW)) {
10959       ast_test_status_update(test, "Parse failure for review option\n");
10960       res = 1;
10961    }
10962    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10963       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10964       res = 1;
10965    }
10966    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10967       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10968       res = 1;
10969    }
10970    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10971       ast_test_status_update(test, "Parse failure for operator option\n");
10972       res = 1;
10973    }
10974    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10975       ast_test_status_update(test, "Parse failure for envelope option\n");
10976       res = 1;
10977    }
10978    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10979       ast_test_status_update(test, "Parse failure for moveheard option\n");
10980       res = 1;
10981    }
10982    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10983       ast_test_status_update(test, "Parse failure for sayduration option\n");
10984       res = 1;
10985    }
10986    if (vmu->saydurationm != 5) {
10987       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10988       res = 1;
10989    }
10990    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10991       ast_test_status_update(test, "Parse failure for forcename option\n");
10992       res = 1;
10993    }
10994    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10995       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10996       res = 1;
10997    }
10998    if (strcasecmp(vmu->callback, "somecontext")) {
10999       ast_test_status_update(test, "Parse failure for callbacks option\n");
11000       res = 1;
11001    }
11002    if (strcasecmp(vmu->dialout, "somecontext2")) {
11003       ast_test_status_update(test, "Parse failure for dialout option\n");
11004       res = 1;
11005    }
11006    if (strcasecmp(vmu->exit, "somecontext3")) {
11007       ast_test_status_update(test, "Parse failure for exitcontext option\n");
11008       res = 1;
11009    }
11010    if (vmu->minsecs != 10) {
11011       ast_test_status_update(test, "Parse failure for minsecs option\n");
11012       res = 1;
11013    }
11014    if (vmu->maxsecs != 100) {
11015       ast_test_status_update(test, "Parse failure for maxsecs option\n");
11016       res = 1;
11017    }
11018    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
11019       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
11020       res = 1;
11021    }
11022    if (vmu->maxdeletedmsg != 50) {
11023       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
11024       res = 1;
11025    }
11026    if (vmu->volgain != 1.3) {
11027       ast_test_status_update(test, "Parse failure for volgain option\n");
11028       res = 1;
11029    }
11030    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
11031       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
11032       res = 1;
11033    }
11034 #ifdef IMAP_STORAGE
11035    apply_options(vmu, option_string2);
11036 
11037    if (strcasecmp(vmu->imapuser, "imapuser")) {
11038       ast_test_status_update(test, "Parse failure for imapuser option\n");
11039       res = 1;
11040    }
11041    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11042       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11043       res = 1;
11044    }
11045    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11046       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11047       res = 1;
11048    }
11049    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11050       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11051       res = 1;
11052    }
11053    if (strcasecmp(vmu->imapserver, "imapserver")) {
11054       ast_test_status_update(test, "Parse failure for imapserver option\n");
11055       res = 1;
11056    }
11057    if (strcasecmp(vmu->imapport, "1234")) {
11058       ast_test_status_update(test, "Parse failure for imapport option\n");
11059       res = 1;
11060    }
11061    if (strcasecmp(vmu->imapflags, "flagged")) {
11062       ast_test_status_update(test, "Parse failure for imapflags option\n");
11063       res = 1;
11064    }
11065 #endif
11066 
11067    free_user(vmu);
11068    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11069 }
11070 
11071 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11072 {
11073    struct ast_vm_user svm;
11074    char *context, *box;
11075    AST_DECLARE_APP_ARGS(args,
11076       AST_APP_ARG(mbox);
11077       AST_APP_ARG(options);
11078    );
11079    static int dep_warning = 0;
11080 
11081    if (ast_strlen_zero(data)) {
11082       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11083       return -1;
11084    }
11085 
11086    if (!dep_warning) {
11087       dep_warning = 1;
11088       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${VM_INFO(%s,exists)} instead.\n", data);
11089    }
11090 
11091    box = ast_strdupa(data);
11092 
11093    AST_STANDARD_APP_ARGS(args, box);
11094 
11095    if (args.options) {
11096    }
11097 
11098    if ((context = strchr(args.mbox, '@'))) {
11099       *context = '\0';
11100       context++;
11101    }
11102 
11103    if (find_user(&svm, context, args.mbox)) {
11104       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11105    } else
11106       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11107 
11108    return 0;
11109 }
11110 
11111 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11112 {
11113    struct ast_vm_user svm;
11114    AST_DECLARE_APP_ARGS(arg,
11115       AST_APP_ARG(mbox);
11116       AST_APP_ARG(context);
11117    );
11118    static int dep_warning = 0;
11119 
11120    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11121 
11122    if (ast_strlen_zero(arg.mbox)) {
11123       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11124       return -1;
11125    }
11126 
11127    if (!dep_warning) {
11128       dep_warning = 1;
11129       ast_log(AST_LOG_WARNING, "MAILBOX_EXISTS is deprecated.  Please use ${VM_INFO(%s,exists)} instead.\n", args);
11130    }
11131 
11132    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11133    return 0;
11134 }
11135 
11136 static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11137 {
11138    struct ast_vm_user *vmu = NULL;
11139    char *tmp, *mailbox, *context, *parse;
11140    int res = 0;
11141 
11142    AST_DECLARE_APP_ARGS(arg,
11143       AST_APP_ARG(mailbox_context);
11144       AST_APP_ARG(attribute);
11145       AST_APP_ARG(folder);
11146    );
11147 
11148    buf[0] = '\0';
11149 
11150    if (ast_strlen_zero(args)) {
11151       ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
11152       return -1;
11153    }
11154 
11155    parse = ast_strdupa(args);
11156    AST_STANDARD_APP_ARGS(arg, parse);
11157 
11158    if (ast_strlen_zero(arg.mailbox_context) || ast_strlen_zero(arg.attribute)) {
11159       ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
11160       return -1;
11161    }
11162 
11163    tmp = ast_strdupa(arg.mailbox_context);
11164    mailbox = strsep(&tmp, "@");
11165    context = strsep(&tmp, "");
11166 
11167    if (ast_strlen_zero(context)) {
11168        context = "default";
11169    }
11170 
11171    vmu = find_user(NULL, context, mailbox);
11172 
11173    if (!strncasecmp(arg.attribute, "exists", 5)) {
11174       ast_copy_string(buf, vmu ? "1" : "0", len);
11175       return 0;
11176    }
11177 
11178    if (vmu) {
11179       if (!strncasecmp(arg.attribute, "password", 8)) {
11180          ast_copy_string(buf, vmu->password, len);
11181       } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
11182          ast_copy_string(buf, vmu->fullname, len);
11183       } else if (!strncasecmp(arg.attribute, "email", 5)) {
11184          ast_copy_string(buf, vmu->email, len);
11185       } else if (!strncasecmp(arg.attribute, "pager", 5)) {
11186          ast_copy_string(buf, vmu->pager, len);
11187       } else if (!strncasecmp(arg.attribute, "language", 8)) {
11188          ast_copy_string(buf, S_OR(vmu->language, ast_channel_language(chan)), len);
11189       } else if (!strncasecmp(arg.attribute, "locale", 6)) {
11190          ast_copy_string(buf, vmu->locale, len);
11191       } else if (!strncasecmp(arg.attribute, "tz", 2)) {
11192          ast_copy_string(buf, vmu->zonetag, len);
11193       } else if (!strncasecmp(arg.attribute, "count", 5)) {
11194          /* If mbxfolder is empty messagecount will default to INBOX */
11195          res = messagecount(context, mailbox, arg.folder);
11196          if (res < 0) {
11197             ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
11198             return -1;
11199          }
11200          snprintf(buf, len, "%d", res);
11201       } else {
11202          ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
11203          return -1;
11204       }
11205    }
11206 
11207    return 0;
11208 }
11209 
11210 static struct ast_custom_function mailbox_exists_acf = {
11211    .name = "MAILBOX_EXISTS",
11212    .read = acf_mailbox_exists,
11213 };
11214 
11215 static struct ast_custom_function vm_info_acf = {
11216    .name = "VM_INFO",
11217    .read = acf_vm_info,
11218 };
11219 
11220 static int vmauthenticate(struct ast_channel *chan, const char *data)
11221 {
11222    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11223    struct ast_vm_user vmus;
11224    char *options = NULL;
11225    int silent = 0, skipuser = 0;
11226    int res = -1;
11227    
11228    if (data) {
11229       s = ast_strdupa(data);
11230       user = strsep(&s, ",");
11231       options = strsep(&s, ",");
11232       if (user) {
11233          s = user;
11234          user = strsep(&s, "@");
11235          context = strsep(&s, "");
11236          if (!ast_strlen_zero(user))
11237             skipuser++;
11238          ast_copy_string(mailbox, user, sizeof(mailbox));
11239       }
11240    }
11241 
11242    if (options) {
11243       silent = (strchr(options, 's')) != NULL;
11244    }
11245 
11246    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11247       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11248       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11249       ast_play_and_wait(chan, "auth-thankyou");
11250       res = 0;
11251    } else if (mailbox[0] == '*') {
11252       /* user entered '*' */
11253       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11254          res = 0; /* prevent hangup */
11255       }
11256    }
11257 
11258    return res;
11259 }
11260 
11261 static char *show_users_realtime(int fd, const char *context)
11262 {
11263    struct ast_config *cfg;
11264    const char *cat = NULL;
11265 
11266    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11267       "context", context, SENTINEL))) {
11268       return CLI_FAILURE;
11269    }
11270 
11271    ast_cli(fd,
11272       "\n"
11273       "=============================================================\n"
11274       "=== Configured Voicemail Users ==============================\n"
11275       "=============================================================\n"
11276       "===\n");
11277 
11278    while ((cat = ast_category_browse(cfg, cat))) {
11279       struct ast_variable *var = NULL;
11280       ast_cli(fd,
11281          "=== Mailbox ...\n"
11282          "===\n");
11283       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11284          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11285       ast_cli(fd,
11286          "===\n"
11287          "=== ---------------------------------------------------------\n"
11288          "===\n");
11289    }
11290 
11291    ast_cli(fd,
11292       "=============================================================\n"
11293       "\n");
11294 
11295    ast_config_destroy(cfg);
11296 
11297    return CLI_SUCCESS;
11298 }
11299 
11300 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11301 {
11302    int which = 0;
11303    int wordlen;
11304    struct ast_vm_user *vmu;
11305    const char *context = "";
11306 
11307    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11308    if (pos > 4)
11309       return NULL;
11310    if (pos == 3)
11311       return (state == 0) ? ast_strdup("for") : NULL;
11312    wordlen = strlen(word);
11313    AST_LIST_TRAVERSE(&users, vmu, list) {
11314       if (!strncasecmp(word, vmu->context, wordlen)) {
11315          if (context && strcmp(context, vmu->context) && ++which > state)
11316             return ast_strdup(vmu->context);
11317          /* ignore repeated contexts ? */
11318          context = vmu->context;
11319       }
11320    }
11321    return NULL;
11322 }
11323 
11324 /*! \brief Show a list of voicemail users in the CLI */
11325 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11326 {
11327    struct ast_vm_user *vmu;
11328 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11329    const char *context = NULL;
11330    int users_counter = 0;
11331 
11332    switch (cmd) {
11333    case CLI_INIT:
11334       e->command = "voicemail show users";
11335       e->usage =
11336          "Usage: voicemail show users [for <context>]\n"
11337          "       Lists all mailboxes currently set up\n";
11338       return NULL;
11339    case CLI_GENERATE:
11340       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11341    }  
11342 
11343    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11344       return CLI_SHOWUSAGE;
11345    if (a->argc == 5) {
11346       if (strcmp(a->argv[3],"for"))
11347          return CLI_SHOWUSAGE;
11348       context = a->argv[4];
11349    }
11350 
11351    if (ast_check_realtime("voicemail")) {
11352       if (!context) {
11353          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11354          return CLI_SHOWUSAGE;
11355       }
11356       return show_users_realtime(a->fd, context);
11357    }
11358 
11359    AST_LIST_LOCK(&users);
11360    if (AST_LIST_EMPTY(&users)) {
11361       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11362       AST_LIST_UNLOCK(&users);
11363       return CLI_FAILURE;
11364    }
11365    if (a->argc == 3)
11366       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11367    else {
11368       int count = 0;
11369       AST_LIST_TRAVERSE(&users, vmu, list) {
11370          if (!strcmp(context, vmu->context))
11371             count++;
11372       }
11373       if (count) {
11374          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11375       } else {
11376          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11377          AST_LIST_UNLOCK(&users);
11378          return CLI_FAILURE;
11379       }
11380    }
11381    AST_LIST_TRAVERSE(&users, vmu, list) {
11382       int newmsgs = 0, oldmsgs = 0;
11383       char count[12], tmp[256] = "";
11384 
11385       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11386          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11387          inboxcount(tmp, &newmsgs, &oldmsgs);
11388          snprintf(count, sizeof(count), "%d", newmsgs);
11389          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11390          users_counter++;
11391       }
11392    }
11393    AST_LIST_UNLOCK(&users);
11394    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11395    return CLI_SUCCESS;
11396 }
11397 
11398 /*! \brief Show a list of voicemail zones in the CLI */
11399 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11400 {
11401    struct vm_zone *zone;
11402 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11403    char *res = CLI_SUCCESS;
11404 
11405    switch (cmd) {
11406    case CLI_INIT:
11407       e->command = "voicemail show zones";
11408       e->usage =
11409          "Usage: voicemail show zones\n"
11410          "       Lists zone message formats\n";
11411       return NULL;
11412    case CLI_GENERATE:
11413       return NULL;
11414    }
11415 
11416    if (a->argc != 3)
11417       return CLI_SHOWUSAGE;
11418 
11419    AST_LIST_LOCK(&zones);
11420    if (!AST_LIST_EMPTY(&zones)) {
11421       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11422       AST_LIST_TRAVERSE(&zones, zone, list) {
11423          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11424       }
11425    } else {
11426       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11427       res = CLI_FAILURE;
11428    }
11429    AST_LIST_UNLOCK(&zones);
11430 
11431    return res;
11432 }
11433 
11434 /*! \brief Reload voicemail configuration from the CLI */
11435 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11436 {
11437    switch (cmd) {
11438    case CLI_INIT:
11439       e->command = "voicemail reload";
11440       e->usage =
11441          "Usage: voicemail reload\n"
11442          "       Reload voicemail configuration\n";
11443       return NULL;
11444    case CLI_GENERATE:
11445       return NULL;
11446    }
11447 
11448    if (a->argc != 2)
11449       return CLI_SHOWUSAGE;
11450 
11451    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11452    load_config(1);
11453    
11454    return CLI_SUCCESS;
11455 }
11456 
11457 static struct ast_cli_entry cli_voicemail[] = {
11458    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11459    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11460    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11461 };
11462 
11463 #ifdef IMAP_STORAGE
11464    #define DATA_EXPORT_VM_USERS(USER)              \
11465       USER(ast_vm_user, context, AST_DATA_STRING)        \
11466       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11467       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11468       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11469       USER(ast_vm_user, email, AST_DATA_STRING)       \
11470       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11471       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11472       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11473       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11474       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11475       USER(ast_vm_user, language, AST_DATA_STRING)       \
11476       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11477       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11478       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11479       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11480       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11481       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11482       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11483       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11484       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11485       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11486       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11487       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11488       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11489       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11490       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11491 #else
11492    #define DATA_EXPORT_VM_USERS(USER)              \
11493       USER(ast_vm_user, context, AST_DATA_STRING)        \
11494       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11495       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11496       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11497       USER(ast_vm_user, email, AST_DATA_STRING)       \
11498       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11499       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11500       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11501       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11502       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11503       USER(ast_vm_user, language, AST_DATA_STRING)       \
11504       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11505       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11506       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11507       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11508       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11509       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11510       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11511       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11512       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11513       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11514       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11515       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11516 #endif
11517 
11518 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11519 
11520 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11521    ZONE(vm_zone, name, AST_DATA_STRING)      \
11522    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11523    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11524 
11525 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11526 
11527 /*!
11528  * \internal
11529  * \brief Add voicemail user to the data_root.
11530  * \param[in] search The search tree.
11531  * \param[in] data_root The main result node.
11532  * \param[in] user The voicemail user.
11533  */
11534 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11535     struct ast_data *data_root, struct ast_vm_user *user)
11536 {
11537    struct ast_data *data_user, *data_zone;
11538    struct ast_data *data_state;
11539    struct vm_zone *zone = NULL;
11540    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11541    char ext_context[256] = "";
11542 
11543    data_user = ast_data_add_node(data_root, "user");
11544    if (!data_user) {
11545       return -1;
11546    }
11547 
11548    ast_data_add_structure(ast_vm_user, data_user, user);
11549 
11550    AST_LIST_LOCK(&zones);
11551    AST_LIST_TRAVERSE(&zones, zone, list) {
11552       if (!strcmp(zone->name, user->zonetag)) {
11553          break;
11554       }
11555    }
11556    AST_LIST_UNLOCK(&zones);
11557 
11558    /* state */
11559    data_state = ast_data_add_node(data_user, "state");
11560    if (!data_state) {
11561       return -1;
11562    }
11563    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11564    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11565    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11566    ast_data_add_int(data_state, "newmsg", newmsg);
11567    ast_data_add_int(data_state, "oldmsg", oldmsg);
11568 
11569    if (zone) {
11570       data_zone = ast_data_add_node(data_user, "zone");
11571       ast_data_add_structure(vm_zone, data_zone, zone);
11572    }
11573 
11574    if (!ast_data_search_match(search, data_user)) {
11575       ast_data_remove_node(data_root, data_user);
11576    }
11577 
11578    return 0;
11579 }
11580 
11581 static int vm_users_data_provider_get(const struct ast_data_search *search,
11582    struct ast_data *data_root)
11583 {
11584    struct ast_vm_user *user;
11585 
11586    AST_LIST_LOCK(&users);
11587    AST_LIST_TRAVERSE(&users, user, list) {
11588       vm_users_data_provider_get_helper(search, data_root, user);
11589    }
11590    AST_LIST_UNLOCK(&users);
11591 
11592    return 0;
11593 }
11594 
11595 static const struct ast_data_handler vm_users_data_provider = {
11596    .version = AST_DATA_HANDLER_VERSION,
11597    .get = vm_users_data_provider_get
11598 };
11599 
11600 static const struct ast_data_entry vm_data_providers[] = {
11601    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11602 };
11603 
11604 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11605 {
11606    int new = 0, old = 0, urgent = 0;
11607 
11608    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11609 
11610    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11611       mwi_sub->old_urgent = urgent;
11612       mwi_sub->old_new = new;
11613       mwi_sub->old_old = old;
11614       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11615       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11616    }
11617 }
11618 
11619 static void poll_subscribed_mailboxes(void)
11620 {
11621    struct mwi_sub *mwi_sub;
11622 
11623    AST_RWLIST_RDLOCK(&mwi_subs);
11624    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11625       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11626          poll_subscribed_mailbox(mwi_sub);
11627       }
11628    }
11629    AST_RWLIST_UNLOCK(&mwi_subs);
11630 }
11631 
11632 static void *mb_poll_thread(void *data)
11633 {
11634    while (poll_thread_run) {
11635       struct timespec ts = { 0, };
11636       struct timeval wait;
11637 
11638       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11639       ts.tv_sec = wait.tv_sec;
11640       ts.tv_nsec = wait.tv_usec * 1000;
11641 
11642       ast_mutex_lock(&poll_lock);
11643       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11644       ast_mutex_unlock(&poll_lock);
11645 
11646       if (!poll_thread_run)
11647          break;
11648 
11649       poll_subscribed_mailboxes();
11650    }
11651 
11652    return NULL;
11653 }
11654 
11655 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11656 {
11657    ast_free(mwi_sub);
11658 }
11659 
11660 static int handle_unsubscribe(void *datap)
11661 {
11662    struct mwi_sub *mwi_sub;
11663    uint32_t *uniqueid = datap;
11664    
11665    AST_RWLIST_WRLOCK(&mwi_subs);
11666    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11667       if (mwi_sub->uniqueid == *uniqueid) {
11668          AST_LIST_REMOVE_CURRENT(entry);
11669          break;
11670       }
11671    }
11672    AST_RWLIST_TRAVERSE_SAFE_END
11673    AST_RWLIST_UNLOCK(&mwi_subs);
11674 
11675    if (mwi_sub)
11676       mwi_sub_destroy(mwi_sub);
11677 
11678    ast_free(uniqueid);  
11679    return 0;
11680 }
11681 
11682 static int handle_subscribe(void *datap)
11683 {
11684    unsigned int len;
11685    struct mwi_sub *mwi_sub;
11686    struct mwi_sub_task *p = datap;
11687 
11688    len = sizeof(*mwi_sub);
11689    if (!ast_strlen_zero(p->mailbox))
11690       len += strlen(p->mailbox);
11691 
11692    if (!ast_strlen_zero(p->context))
11693       len += strlen(p->context) + 1; /* Allow for seperator */
11694 
11695    if (!(mwi_sub = ast_calloc(1, len)))
11696       return -1;
11697 
11698    mwi_sub->uniqueid = p->uniqueid;
11699    if (!ast_strlen_zero(p->mailbox))
11700       strcpy(mwi_sub->mailbox, p->mailbox);
11701 
11702    if (!ast_strlen_zero(p->context)) {
11703       strcat(mwi_sub->mailbox, "@");
11704       strcat(mwi_sub->mailbox, p->context);
11705    }
11706 
11707    AST_RWLIST_WRLOCK(&mwi_subs);
11708    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11709    AST_RWLIST_UNLOCK(&mwi_subs);
11710    ast_free((void *) p->mailbox);
11711    ast_free((void *) p->context);
11712    ast_free(p);
11713    poll_subscribed_mailbox(mwi_sub);
11714    return 0;
11715 }
11716 
11717 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11718 {
11719    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11720    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11721       return;
11722 
11723    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11724       return;
11725 
11726    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11727    *uniqueid = u;
11728    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11729       ast_free(uniqueid);
11730    }
11731 }
11732 
11733 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11734 {
11735    struct mwi_sub_task *mwist;
11736    
11737    if (ast_event_get_type(event) != AST_EVENT_SUB)
11738       return;
11739 
11740    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11741       return;
11742 
11743    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11744       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11745       return;
11746    }
11747    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11748    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11749    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11750    
11751    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11752       ast_free(mwist);
11753    }
11754 }
11755 
11756 static void start_poll_thread(void)
11757 {
11758    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11759       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11760       AST_EVENT_IE_END);
11761 
11762    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11763       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11764       AST_EVENT_IE_END);
11765 
11766    if (mwi_sub_sub)
11767       ast_event_report_subs(mwi_sub_sub);
11768 
11769    poll_thread_run = 1;
11770 
11771    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11772 }
11773 
11774 static void stop_poll_thread(void)
11775 {
11776    poll_thread_run = 0;
11777 
11778    if (mwi_sub_sub) {
11779       ast_event_unsubscribe(mwi_sub_sub);
11780       mwi_sub_sub = NULL;
11781    }
11782 
11783    if (mwi_unsub_sub) {
11784       ast_event_unsubscribe(mwi_unsub_sub);
11785       mwi_unsub_sub = NULL;
11786    }
11787 
11788    ast_mutex_lock(&poll_lock);
11789    ast_cond_signal(&poll_cond);
11790    ast_mutex_unlock(&poll_lock);
11791 
11792    pthread_join(poll_thread, NULL);
11793 
11794    poll_thread = AST_PTHREADT_NULL;
11795 }
11796 
11797 /*! \brief Manager list voicemail users command */
11798 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11799 {
11800    struct ast_vm_user *vmu = NULL;
11801    const char *id = astman_get_header(m, "ActionID");
11802    char actionid[128] = "";
11803 
11804    if (!ast_strlen_zero(id))
11805       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11806 
11807    AST_LIST_LOCK(&users);
11808 
11809    if (AST_LIST_EMPTY(&users)) {
11810       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11811       AST_LIST_UNLOCK(&users);
11812       return RESULT_SUCCESS;
11813    }
11814    
11815    astman_send_ack(s, m, "Voicemail user list will follow");
11816    
11817    AST_LIST_TRAVERSE(&users, vmu, list) {
11818       char dirname[256];
11819 
11820 #ifdef IMAP_STORAGE
11821       int new, old;
11822       inboxcount(vmu->mailbox, &new, &old);
11823 #endif
11824       
11825       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11826       astman_append(s,
11827          "%s"
11828          "Event: VoicemailUserEntry\r\n"
11829          "VMContext: %s\r\n"
11830          "VoiceMailbox: %s\r\n"
11831          "Fullname: %s\r\n"
11832          "Email: %s\r\n"
11833          "Pager: %s\r\n"
11834          "ServerEmail: %s\r\n"
11835          "MailCommand: %s\r\n"
11836          "Language: %s\r\n"
11837          "TimeZone: %s\r\n"
11838          "Callback: %s\r\n"
11839          "Dialout: %s\r\n"
11840          "UniqueID: %s\r\n"
11841          "ExitContext: %s\r\n"
11842          "SayDurationMinimum: %d\r\n"
11843          "SayEnvelope: %s\r\n"
11844          "SayCID: %s\r\n"
11845          "AttachMessage: %s\r\n"
11846          "AttachmentFormat: %s\r\n"
11847          "DeleteMessage: %s\r\n"
11848          "VolumeGain: %.2f\r\n"
11849          "CanReview: %s\r\n"
11850          "CallOperator: %s\r\n"
11851          "MaxMessageCount: %d\r\n"
11852          "MaxMessageLength: %d\r\n"
11853          "NewMessageCount: %d\r\n"
11854 #ifdef IMAP_STORAGE
11855          "OldMessageCount: %d\r\n"
11856          "IMAPUser: %s\r\n"
11857          "IMAPServer: %s\r\n"
11858          "IMAPPort: %s\r\n"
11859          "IMAPFlags: %s\r\n"
11860 #endif
11861          "\r\n",
11862          actionid,
11863          vmu->context,
11864          vmu->mailbox,
11865          vmu->fullname,
11866          vmu->email,
11867          vmu->pager,
11868          vmu->serveremail,
11869          vmu->mailcmd,
11870          vmu->language,
11871          vmu->zonetag,
11872          vmu->callback,
11873          vmu->dialout,
11874          vmu->uniqueid,
11875          vmu->exit,
11876          vmu->saydurationm,
11877          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11878          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11879          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11880          vmu->attachfmt,
11881          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11882          vmu->volgain,
11883          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11884          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11885          vmu->maxmsg,
11886          vmu->maxsecs,
11887 #ifdef IMAP_STORAGE
11888          new, old,
11889          vmu->imapuser,
11890          vmu->imapserver,
11891          vmu->imapport,
11892          vmu->imapflags
11893 #else
11894          count_messages(vmu, dirname)
11895 #endif
11896          );
11897    }     
11898    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11899 
11900    AST_LIST_UNLOCK(&users);
11901 
11902    return RESULT_SUCCESS;
11903 }
11904 
11905 /*! \brief Free the users structure. */
11906 static void free_vm_users(void) 
11907 {
11908    struct ast_vm_user *current;
11909    AST_LIST_LOCK(&users);
11910    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11911       ast_set_flag(current, VM_ALLOCED);
11912       free_user(current);
11913    }
11914    AST_LIST_UNLOCK(&users);
11915 }
11916 
11917 /*! \brief Free the zones structure. */
11918 static void free_vm_zones(void)
11919 {
11920    struct vm_zone *zcur;
11921    AST_LIST_LOCK(&zones);
11922    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11923       free_zone(zcur);
11924    AST_LIST_UNLOCK(&zones);
11925 }
11926 
11927 static const char *substitute_escapes(const char *value)
11928 {
11929    char *current;
11930 
11931    /* Add 16 for fudge factor */
11932    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11933 
11934    ast_str_reset(str);
11935    
11936    /* Substitute strings \r, \n, and \t into the appropriate characters */
11937    for (current = (char *) value; *current; current++) {
11938       if (*current == '\\') {
11939          current++;
11940          if (!*current) {
11941             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11942             break;
11943          }
11944          switch (*current) {
11945          case '\\':
11946             ast_str_append(&str, 0, "\\");
11947             break;
11948          case 'r':
11949             ast_str_append(&str, 0, "\r");
11950             break;
11951          case 'n':
11952 #ifdef IMAP_STORAGE
11953             if (!str->used || str->str[str->used - 1] != '\r') {
11954                ast_str_append(&str, 0, "\r");
11955             }
11956 #endif
11957             ast_str_append(&str, 0, "\n");
11958             break;
11959          case 't':
11960             ast_str_append(&str, 0, "\t");
11961             break;
11962          default:
11963             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11964             break;
11965          }
11966       } else {
11967          ast_str_append(&str, 0, "%c", *current);
11968       }
11969    }
11970 
11971    return ast_str_buffer(str);
11972 }
11973 
11974 static int load_config(int reload)
11975 {
11976    struct ast_config *cfg, *ucfg;
11977    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11978    int res;
11979 
11980    ast_unload_realtime("voicemail");
11981    ast_unload_realtime("voicemail_data");
11982 
11983    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11984       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11985          return 0;
11986       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11987          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11988          ucfg = NULL;
11989       }
11990       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11991       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11992          ast_config_destroy(ucfg);
11993          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11994          return 0;
11995       }
11996    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11997       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11998       return 0;
11999    } else {
12000       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
12001       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
12002          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
12003          ucfg = NULL;
12004       }
12005    }
12006 
12007    res = actual_load_config(reload, cfg, ucfg);
12008 
12009    ast_config_destroy(cfg);
12010    ast_config_destroy(ucfg);
12011 
12012    return res;
12013 }
12014 
12015 #ifdef TEST_FRAMEWORK
12016 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
12017 {
12018    ast_unload_realtime("voicemail");
12019    ast_unload_realtime("voicemail_data");
12020    return actual_load_config(reload, cfg, ucfg);
12021 }
12022 #endif
12023 
12024 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
12025 {
12026    struct ast_vm_user *current;
12027    char *cat;
12028    struct ast_variable *var;
12029    const char *val;
12030    char *q, *stringp, *tmp;
12031    int x;
12032    int tmpadsi[4];
12033    char secretfn[PATH_MAX] = "";
12034 
12035 #ifdef IMAP_STORAGE
12036    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
12037 #endif
12038    /* set audio control prompts */
12039    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
12040    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
12041    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
12042    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
12043    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
12044 
12045    /* Free all the users structure */  
12046    free_vm_users();
12047 
12048    /* Free all the zones structure */
12049    free_vm_zones();
12050 
12051    AST_LIST_LOCK(&users);  
12052 
12053    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
12054    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
12055 
12056    if (cfg) {
12057       /* General settings */
12058 
12059       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
12060          val = "default";
12061       ast_copy_string(userscontext, val, sizeof(userscontext));
12062       /* Attach voice message to mail message ? */
12063       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
12064          val = "yes";
12065       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
12066 
12067       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
12068          val = "no";
12069       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
12070 
12071       volgain = 0.0;
12072       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
12073          sscanf(val, "%30lf", &volgain);
12074 
12075 #ifdef ODBC_STORAGE
12076       strcpy(odbc_database, "asterisk");
12077       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
12078          ast_copy_string(odbc_database, val, sizeof(odbc_database));
12079       }
12080       strcpy(odbc_table, "voicemessages");
12081       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
12082          ast_copy_string(odbc_table, val, sizeof(odbc_table));
12083       }
12084 #endif      
12085       /* Mail command */
12086       strcpy(mailcmd, SENDMAIL);
12087       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
12088          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
12089 
12090       maxsilence = 0;
12091       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
12092          maxsilence = atoi(val);
12093          if (maxsilence > 0)
12094             maxsilence *= 1000;
12095       }
12096       
12097       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
12098          maxmsg = MAXMSG;
12099       } else {
12100          maxmsg = atoi(val);
12101          if (maxmsg < 0) {
12102             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
12103             maxmsg = MAXMSG;
12104          } else if (maxmsg > MAXMSGLIMIT) {
12105             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12106             maxmsg = MAXMSGLIMIT;
12107          }
12108       }
12109 
12110       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
12111          maxdeletedmsg = 0;
12112       } else {
12113          if (sscanf(val, "%30d", &x) == 1)
12114             maxdeletedmsg = x;
12115          else if (ast_true(val))
12116             maxdeletedmsg = MAXMSG;
12117          else
12118             maxdeletedmsg = 0;
12119 
12120          if (maxdeletedmsg < 0) {
12121             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
12122             maxdeletedmsg = MAXMSG;
12123          } else if (maxdeletedmsg > MAXMSGLIMIT) {
12124             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12125             maxdeletedmsg = MAXMSGLIMIT;
12126          }
12127       }
12128 
12129       /* Load date format config for voicemail mail */
12130       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
12131          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
12132       }
12133 
12134       /* Load date format config for voicemail pager mail */
12135       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12136          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12137       }
12138 
12139       /* External password changing command */
12140       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12141          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12142          pwdchange = PWDCHANGE_EXTERNAL;
12143       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12144          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12145          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12146       }
12147 
12148       /* External password validation command */
12149       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12150          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12151          ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
12152       }
12153 
12154 #ifdef IMAP_STORAGE
12155       /* IMAP server address */
12156       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12157          ast_copy_string(imapserver, val, sizeof(imapserver));
12158       } else {
12159          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12160       }
12161       /* IMAP server port */
12162       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12163          ast_copy_string(imapport, val, sizeof(imapport));
12164       } else {
12165          ast_copy_string(imapport, "143", sizeof(imapport));
12166       }
12167       /* IMAP server flags */
12168       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12169          ast_copy_string(imapflags, val, sizeof(imapflags));
12170       }
12171       /* IMAP server master username */
12172       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12173          ast_copy_string(authuser, val, sizeof(authuser));
12174       }
12175       /* IMAP server master password */
12176       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12177          ast_copy_string(authpassword, val, sizeof(authpassword));
12178       }
12179       /* Expunge on exit */
12180       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12181          if (ast_false(val))
12182             expungeonhangup = 0;
12183          else
12184             expungeonhangup = 1;
12185       } else {
12186          expungeonhangup = 1;
12187       }
12188       /* IMAP voicemail folder */
12189       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12190          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12191       } else {
12192          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12193       }
12194       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12195          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12196       }
12197       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12198          imapgreetings = ast_true(val);
12199       } else {
12200          imapgreetings = 0;
12201       }
12202       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12203          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12204       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12205          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12206          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12207       } else {
12208          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12209       }
12210 
12211       /* There is some very unorthodox casting done here. This is due
12212        * to the way c-client handles the argument passed in. It expects a 
12213        * void pointer and casts the pointer directly to a long without
12214        * first dereferencing it. */
12215       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12216          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12217       } else {
12218          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12219       }
12220 
12221       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12222          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12223       } else {
12224          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12225       }
12226 
12227       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12228          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12229       } else {
12230          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12231       }
12232 
12233       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12234          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12235       } else {
12236          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12237       }
12238 
12239       /* Increment configuration version */
12240       imapversion++;
12241 #endif
12242       /* External voicemail notify application */
12243       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12244          ast_copy_string(externnotify, val, sizeof(externnotify));
12245          ast_debug(1, "found externnotify: %s\n", externnotify);
12246       } else {
12247          externnotify[0] = '\0';
12248       }
12249 
12250       /* SMDI voicemail notification */
12251       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12252          ast_debug(1, "Enabled SMDI voicemail notification\n");
12253          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12254             smdi_iface = ast_smdi_interface_find(val);
12255          } else {
12256             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12257             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12258          }
12259          if (!smdi_iface) {
12260             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12261          }
12262       }
12263 
12264       /* Silence treshold */
12265       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12266       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12267          silencethreshold = atoi(val);
12268 
12269       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
12270          val = ASTERISK_USERNAME;
12271       ast_copy_string(serveremail, val, sizeof(serveremail));
12272 
12273       vmmaxsecs = 0;
12274       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12275          if (sscanf(val, "%30d", &x) == 1) {
12276             vmmaxsecs = x;
12277          } else {
12278             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12279          }
12280       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12281          static int maxmessage_deprecate = 0;
12282          if (maxmessage_deprecate == 0) {
12283             maxmessage_deprecate = 1;
12284             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12285          }
12286          if (sscanf(val, "%30d", &x) == 1) {
12287             vmmaxsecs = x;
12288          } else {
12289             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12290          }
12291       }
12292 
12293       vmminsecs = 0;
12294       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12295          if (sscanf(val, "%30d", &x) == 1) {
12296             vmminsecs = x;
12297             if (maxsilence / 1000 >= vmminsecs) {
12298                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12299             }
12300          } else {
12301             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12302          }
12303       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12304          static int maxmessage_deprecate = 0;
12305          if (maxmessage_deprecate == 0) {
12306             maxmessage_deprecate = 1;
12307             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12308          }
12309          if (sscanf(val, "%30d", &x) == 1) {
12310             vmminsecs = x;
12311             if (maxsilence / 1000 >= vmminsecs) {
12312                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12313             }
12314          } else {
12315             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12316          }
12317       }
12318 
12319       val = ast_variable_retrieve(cfg, "general", "format");
12320       if (!val) {
12321          val = "wav";   
12322       } else {
12323          tmp = ast_strdupa(val);
12324          val = ast_format_str_reduce(tmp);
12325          if (!val) {
12326             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12327             val = "wav";
12328          }
12329       }
12330       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12331 
12332       skipms = 3000;
12333       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12334          if (sscanf(val, "%30d", &x) == 1) {
12335             maxgreet = x;
12336          } else {
12337             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12338          }
12339       }
12340 
12341       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12342          if (sscanf(val, "%30d", &x) == 1) {
12343             skipms = x;
12344          } else {
12345             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12346          }
12347       }
12348 
12349       maxlogins = 3;
12350       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12351          if (sscanf(val, "%30d", &x) == 1) {
12352             maxlogins = x;
12353          } else {
12354             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12355          }
12356       }
12357 
12358       minpassword = MINPASSWORD;
12359       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12360          if (sscanf(val, "%30d", &x) == 1) {
12361             minpassword = x;
12362          } else {
12363             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12364          }
12365       }
12366 
12367       /* Force new user to record name ? */
12368       if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
12369          val = "no";
12370       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12371 
12372       /* Force new user to record greetings ? */
12373       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
12374          val = "no";
12375       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12376 
12377       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12378          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12379          stringp = ast_strdupa(val);
12380          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12381             if (!ast_strlen_zero(stringp)) {
12382                q = strsep(&stringp, ",");
12383                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12384                   q++;
12385                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12386                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12387             } else {
12388                cidinternalcontexts[x][0] = '\0';
12389             }
12390          }
12391       }
12392       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12393          ast_debug(1, "VM Review Option disabled globally\n");
12394          val = "no";
12395       }
12396       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
12397 
12398       /* Temporary greeting reminder */
12399       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12400          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12401          val = "no";
12402       } else {
12403          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12404       }
12405       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12406       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12407          ast_debug(1, "VM next message wrap disabled globally\n");
12408          val = "no";
12409       }
12410       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
12411 
12412       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12413          ast_debug(1, "VM Operator break disabled globally\n");
12414          val = "no";
12415       }
12416       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
12417 
12418       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12419          ast_debug(1, "VM CID Info before msg disabled globally\n");
12420          val = "no";
12421       }
12422       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
12423 
12424       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12425          ast_debug(1, "Send Voicemail msg disabled globally\n");
12426          val = "no";
12427       }
12428       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12429 
12430       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12431          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12432          val = "yes";
12433       }
12434       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
12435 
12436       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12437          ast_debug(1, "Move Heard enabled globally\n");
12438          val = "yes";
12439       }
12440       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
12441 
12442       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12443          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12444          val = "no";
12445       }
12446       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
12447 
12448       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12449          ast_debug(1, "Duration info before msg enabled globally\n");
12450          val = "yes";
12451       }
12452       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
12453 
12454       saydurationminfo = 2;
12455       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12456          if (sscanf(val, "%30d", &x) == 1) {
12457             saydurationminfo = x;
12458          } else {
12459             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12460          }
12461       }
12462 
12463       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12464          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12465          val = "no";
12466       }
12467       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12468 
12469       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12470          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12471          ast_debug(1, "found dialout context: %s\n", dialcontext);
12472       } else {
12473          dialcontext[0] = '\0';
12474       }
12475 
12476       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12477          ast_copy_string(callcontext, val, sizeof(callcontext));
12478          ast_debug(1, "found callback context: %s\n", callcontext);
12479       } else {
12480          callcontext[0] = '\0';
12481       }
12482 
12483       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12484          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12485          ast_debug(1, "found operator context: %s\n", exitcontext);
12486       } else {
12487          exitcontext[0] = '\0';
12488       }
12489 
12490       /* load password sounds configuration */
12491       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12492          ast_copy_string(vm_password, val, sizeof(vm_password));
12493       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12494          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12495       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12496          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12497       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12498          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12499       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12500          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12501       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12502          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12503       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12504          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12505       }
12506       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12507          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12508       }
12509       /* load configurable audio prompts */
12510       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12511          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12512       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12513          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12514       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12515          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12516       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12517          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12518       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12519          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12520 
12521       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12522          val = "no";
12523       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12524 
12525       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12526          val = "voicemail.conf";
12527       }
12528       if (!(strcmp(val, "spooldir"))) {
12529          passwordlocation = OPT_PWLOC_SPOOLDIR;
12530       } else {
12531          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12532       }
12533 
12534       poll_freq = DEFAULT_POLL_FREQ;
12535       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12536          if (sscanf(val, "%30u", &poll_freq) != 1) {
12537             poll_freq = DEFAULT_POLL_FREQ;
12538             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12539          }
12540       }
12541 
12542       poll_mailboxes = 0;
12543       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12544          poll_mailboxes = ast_true(val);
12545 
12546       memset(fromstring, 0, sizeof(fromstring));
12547       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12548       strcpy(charset, "ISO-8859-1");
12549       if (emailbody) {
12550          ast_free(emailbody);
12551          emailbody = NULL;
12552       }
12553       if (emailsubject) {
12554          ast_free(emailsubject);
12555          emailsubject = NULL;
12556       }
12557       if (pagerbody) {
12558          ast_free(pagerbody);
12559          pagerbody = NULL;
12560       }
12561       if (pagersubject) {
12562          ast_free(pagersubject);
12563          pagersubject = NULL;
12564       }
12565       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12566          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12567       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12568          ast_copy_string(fromstring, val, sizeof(fromstring));
12569       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12570          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12571       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12572          ast_copy_string(charset, val, sizeof(charset));
12573       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12574          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12575          for (x = 0; x < 4; x++) {
12576             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12577          }
12578       }
12579       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12580          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12581          for (x = 0; x < 4; x++) {
12582             memcpy(&adsisec[x], &tmpadsi[x], 1);
12583          }
12584       }
12585       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12586          if (atoi(val)) {
12587             adsiver = atoi(val);
12588          }
12589       }
12590       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12591          ast_copy_string(zonetag, val, sizeof(zonetag));
12592       }
12593       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12594          ast_copy_string(locale, val, sizeof(locale));
12595       }
12596       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12597          emailsubject = ast_strdup(substitute_escapes(val));
12598       }
12599       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12600          emailbody = ast_strdup(substitute_escapes(val));
12601       }
12602       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12603          pagersubject = ast_strdup(substitute_escapes(val));
12604       }
12605       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12606          pagerbody = ast_strdup(substitute_escapes(val));
12607       }
12608 
12609       /* load mailboxes from users.conf */
12610       if (ucfg) { 
12611          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12612             if (!strcasecmp(cat, "general")) {
12613                continue;
12614             }
12615             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12616                continue;
12617             if ((current = find_or_create(userscontext, cat))) {
12618                populate_defaults(current);
12619                apply_options_full(current, ast_variable_browse(ucfg, cat));
12620                ast_copy_string(current->context, userscontext, sizeof(current->context));
12621                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12622                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12623                }
12624 
12625                switch (current->passwordlocation) {
12626                case OPT_PWLOC_SPOOLDIR:
12627                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12628                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12629                }
12630             }
12631          }
12632       }
12633 
12634       /* load mailboxes from voicemail.conf */
12635       cat = ast_category_browse(cfg, NULL);
12636       while (cat) {
12637          if (strcasecmp(cat, "general")) {
12638             var = ast_variable_browse(cfg, cat);
12639             if (strcasecmp(cat, "zonemessages")) {
12640                /* Process mailboxes in this context */
12641                while (var) {
12642                   append_mailbox(cat, var->name, var->value);
12643                   var = var->next;
12644                }
12645             } else {
12646                /* Timezones in this context */
12647                while (var) {
12648                   struct vm_zone *z;
12649                   if ((z = ast_malloc(sizeof(*z)))) {
12650                      char *msg_format, *tzone;
12651                      msg_format = ast_strdupa(var->value);
12652                      tzone = strsep(&msg_format, "|,");
12653                      if (msg_format) {
12654                         ast_copy_string(z->name, var->name, sizeof(z->name));
12655                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12656                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12657                         AST_LIST_LOCK(&zones);
12658                         AST_LIST_INSERT_HEAD(&zones, z, list);
12659                         AST_LIST_UNLOCK(&zones);
12660                      } else {
12661                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12662                         ast_free(z);
12663                      }
12664                   } else {
12665                      AST_LIST_UNLOCK(&users);
12666                      return -1;
12667                   }
12668                   var = var->next;
12669                }
12670             }
12671          }
12672          cat = ast_category_browse(cfg, cat);
12673       }
12674 
12675       AST_LIST_UNLOCK(&users);
12676 
12677       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12678          start_poll_thread();
12679       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12680          stop_poll_thread();;
12681 
12682       return 0;
12683    } else {
12684       AST_LIST_UNLOCK(&users);
12685       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12686       return 0;
12687    }
12688 }
12689 
12690 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12691 {
12692    int res = -1;
12693    char dir[PATH_MAX];
12694    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12695    ast_debug(2, "About to try retrieving name file %s\n", dir);
12696    RETRIEVE(dir, -1, mailbox, context);
12697    if (ast_fileexists(dir, NULL, NULL)) {
12698       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12699    }
12700    DISPOSE(dir, -1);
12701    return res;
12702 }
12703 
12704 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12705    struct ast_config *pwconf;
12706    struct ast_flags config_flags = { 0 };
12707 
12708    pwconf = ast_config_load(secretfn, config_flags);
12709    if (pwconf) {
12710       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12711       if (val) {
12712          ast_copy_string(password, val, passwordlen);
12713          return;
12714       }
12715    }
12716    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12717 }
12718 
12719 static int write_password_to_file(const char *secretfn, const char *password) {
12720    struct ast_config *conf;
12721    struct ast_category *cat;
12722    struct ast_variable *var;
12723 
12724    if (!(conf=ast_config_new())) {
12725       ast_log(LOG_ERROR, "Error creating new config structure\n");
12726       return -1;
12727    }
12728    if (!(cat=ast_category_new("general","",1))) {
12729       ast_log(LOG_ERROR, "Error creating new category structure\n");
12730       return -1;
12731    }
12732    if (!(var=ast_variable_new("password",password,""))) {
12733       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12734       return -1;
12735    }
12736    ast_category_append(conf,cat);
12737    ast_variable_append(cat,var);
12738    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12739       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12740       return -1;
12741    }
12742    return 0;
12743 }
12744 
12745 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12746 {
12747    char *context;
12748    char *args_copy;
12749    int res;
12750 
12751    if (ast_strlen_zero(data)) {
12752       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12753       return -1;
12754    }
12755 
12756    args_copy = ast_strdupa(data);
12757    if ((context = strchr(args_copy, '@'))) {
12758       *context++ = '\0';
12759    } else {
12760       context = "default";
12761    }
12762 
12763    if ((res = sayname(chan, args_copy, context)) < 0) {
12764       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12765       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12766       if (!res) {
12767          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, ast_channel_language(chan));
12768       }
12769    }
12770 
12771    return res;
12772 }
12773 
12774 #ifdef TEST_FRAMEWORK
12775 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12776 {
12777    return 0;
12778 }
12779 
12780 static struct ast_frame *fake_read(struct ast_channel *ast)
12781 {
12782    return &ast_null_frame;
12783 }
12784 
12785 AST_TEST_DEFINE(test_voicemail_vmsayname)
12786 {
12787    char dir[PATH_MAX];
12788    char dir2[PATH_MAX];
12789    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12790    static const char TEST_EXTENSION[] = "1234";
12791 
12792    struct ast_channel *test_channel1 = NULL;
12793    int res = -1;
12794 
12795    static const struct ast_channel_tech fake_tech = {
12796       .write = fake_write,
12797       .read = fake_read,
12798    };
12799 
12800    switch (cmd) {
12801    case TEST_INIT:
12802       info->name = "vmsayname_exec";
12803       info->category = "/apps/app_voicemail/";
12804       info->summary = "Vmsayname unit test";
12805       info->description =
12806          "This tests passing various parameters to vmsayname";
12807       return AST_TEST_NOT_RUN;
12808    case TEST_EXECUTE:
12809       break;
12810    }
12811 
12812    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12813         NULL, NULL, 0, 0, "TestChannel1"))) {
12814       goto exit_vmsayname_test;
12815    }
12816 
12817    /* normally this is done in the channel driver */
12818    ast_format_set(&test_channel1->writeformat, AST_FORMAT_GSM, 0);
12819    ast_format_cap_add(test_channel1->nativeformats, &test_channel1->writeformat);
12820    ast_format_set(&test_channel1->rawwriteformat, AST_FORMAT_GSM, 0);
12821    ast_format_set(&test_channel1->readformat, AST_FORMAT_GSM, 0);
12822    ast_format_set(&test_channel1->rawreadformat, AST_FORMAT_GSM, 0);
12823    test_channel1->tech = &fake_tech;
12824 
12825    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12826    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12827    if (!(res = vmsayname_exec(test_channel1, dir))) {
12828       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12829       if (ast_fileexists(dir, NULL, NULL)) {
12830          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12831          res = -1;
12832          goto exit_vmsayname_test;
12833       } else {
12834          /* no greeting already exists as expected, let's create one to fully test sayname */
12835          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12836             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12837             goto exit_vmsayname_test;
12838          }
12839          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12840          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12841          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12842          if ((res = symlink(dir, dir2))) {
12843             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12844             goto exit_vmsayname_test;
12845          }
12846          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12847          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12848          res = vmsayname_exec(test_channel1, dir);
12849 
12850          /* TODO: there may be a better way to do this */
12851          unlink(dir2);
12852          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12853          rmdir(dir2);
12854          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12855          rmdir(dir2);
12856       }
12857    }
12858 
12859 exit_vmsayname_test:
12860 
12861    if (test_channel1) {
12862       ast_hangup(test_channel1);
12863    }
12864 
12865    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12866 }
12867 
12868 AST_TEST_DEFINE(test_voicemail_msgcount)
12869 {
12870    int i, j, res = AST_TEST_PASS, syserr;
12871    struct ast_vm_user *vmu;
12872    struct vm_state vms;
12873 #ifdef IMAP_STORAGE
12874    struct ast_channel *chan = NULL;
12875 #endif
12876    struct {
12877       char dir[256];
12878       char file[256];
12879       char txtfile[256];
12880    } tmp[3];
12881    char syscmd[256];
12882    const char origweasels[] = "tt-weasels";
12883    const char testcontext[] = "test";
12884    const char testmailbox[] = "00000000";
12885    const char testspec[] = "00000000@test";
12886    FILE *txt;
12887    int new, old, urgent;
12888    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12889    const int folder2mbox[3] = { 1, 11, 0 };
12890    const int expected_results[3][12] = {
12891       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12892       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12893       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12894       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12895    };
12896 
12897    switch (cmd) {
12898    case TEST_INIT:
12899       info->name = "test_voicemail_msgcount";
12900       info->category = "/apps/app_voicemail/";
12901       info->summary = "Test Voicemail status checks";
12902       info->description =
12903          "Verify that message counts are correct when retrieved through the public API";
12904       return AST_TEST_NOT_RUN;
12905    case TEST_EXECUTE:
12906       break;
12907    }
12908 
12909    /* Make sure the original path was completely empty */
12910    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12911    if ((syserr = ast_safe_system(syscmd))) {
12912       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12913          syserr > 0 ? strerror(syserr) : "unable to fork()");
12914       return AST_TEST_FAIL;
12915    }
12916 
12917 #ifdef IMAP_STORAGE
12918    if (!(chan = ast_dummy_channel_alloc())) {
12919       ast_test_status_update(test, "Unable to create dummy channel\n");
12920       return AST_TEST_FAIL;
12921    }
12922 #endif
12923 
12924    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12925       !(vmu = find_or_create(testcontext, testmailbox))) {
12926       ast_test_status_update(test, "Cannot create vmu structure\n");
12927       ast_unreplace_sigchld();
12928 #ifdef IMAP_STORAGE
12929       chan = ast_channel_unref(chan);
12930 #endif
12931       return AST_TEST_FAIL;
12932    }
12933 
12934    populate_defaults(vmu);
12935    memset(&vms, 0, sizeof(vms));
12936 
12937    /* Create temporary voicemail */
12938    for (i = 0; i < 3; i++) {
12939       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12940       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12941       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12942 
12943       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12944          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12945             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12946          if ((syserr = ast_safe_system(syscmd))) {
12947             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12948                syserr > 0 ? strerror(syserr) : "unable to fork()");
12949             ast_unreplace_sigchld();
12950 #ifdef IMAP_STORAGE
12951             chan = ast_channel_unref(chan);
12952 #endif
12953             return AST_TEST_FAIL;
12954          }
12955       }
12956 
12957       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12958          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12959          fclose(txt);
12960       } else {
12961          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12962          res = AST_TEST_FAIL;
12963          break;
12964       }
12965       open_mailbox(&vms, vmu, folder2mbox[i]);
12966       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12967 
12968       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12969       for (j = 0; j < 3; j++) {
12970          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12971          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12972             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12973                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12974             res = AST_TEST_FAIL;
12975          }
12976       }
12977 
12978       new = old = urgent = 0;
12979       if (ast_app_inboxcount(testspec, &new, &old)) {
12980          ast_test_status_update(test, "inboxcount returned failure\n");
12981          res = AST_TEST_FAIL;
12982       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12983          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12984             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12985          res = AST_TEST_FAIL;
12986       }
12987 
12988       new = old = urgent = 0;
12989       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12990          ast_test_status_update(test, "inboxcount2 returned failure\n");
12991          res = AST_TEST_FAIL;
12992       } else if (old != expected_results[i][6 + 0] ||
12993             urgent != expected_results[i][6 + 1] ||
12994                new != expected_results[i][6 + 2]    ) {
12995          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12996             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12997          res = AST_TEST_FAIL;
12998       }
12999 
13000       new = old = urgent = 0;
13001       for (j = 0; j < 3; j++) {
13002          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
13003             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
13004                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
13005             res = AST_TEST_FAIL;
13006          }
13007       }
13008    }
13009 
13010    for (i = 0; i < 3; i++) {
13011       /* This is necessary if the voicemails are stored on an ODBC/IMAP
13012        * server, in which case, the rm below will not affect the
13013        * voicemails. */
13014       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
13015       DISPOSE(tmp[i].dir, 0);
13016    }
13017 
13018    if (vms.deleted) {
13019       ast_free(vms.deleted);
13020    }
13021    if (vms.heard) {
13022       ast_free(vms.heard);
13023    }
13024 
13025 #ifdef IMAP_STORAGE
13026    chan = ast_channel_unref(chan);
13027 #endif
13028 
13029    /* And remove test directory */
13030    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
13031    if ((syserr = ast_safe_system(syscmd))) {
13032       ast_test_status_update(test, "Unable to clear test directory: %s\n",
13033          syserr > 0 ? strerror(syserr) : "unable to fork()");
13034    }
13035 
13036    return res;
13037 }
13038 
13039 AST_TEST_DEFINE(test_voicemail_notify_endl)
13040 {
13041    int res = AST_TEST_PASS;
13042    char testcontext[] = "test";
13043    char testmailbox[] = "00000000";
13044    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
13045    char attach[256], attach2[256];
13046    char buf[256] = ""; /* No line should actually be longer than 80 */
13047    struct ast_channel *chan = NULL;
13048    struct ast_vm_user *vmu, vmus = {
13049       .flags = 0,
13050    };
13051    FILE *file;
13052    struct {
13053       char *name;
13054       enum { INT, FLAGVAL, STATIC, STRPTR } type;
13055       void *location;
13056       union {
13057          int intval;
13058          char *strval;
13059       } u;
13060    } test_items[] = {
13061       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
13062       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
13063       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
13064       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
13065       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
13066       { "attach2", STRPTR, attach2, .u.strval = "" },
13067       { "attach", STRPTR, attach, .u.strval = "" },
13068    };
13069    int which;
13070 
13071    switch (cmd) {
13072    case TEST_INIT:
13073       info->name = "test_voicemail_notify_endl";
13074       info->category = "/apps/app_voicemail/";
13075       info->summary = "Test Voicemail notification end-of-line";
13076       info->description =
13077          "Verify that notification emails use a consistent end-of-line character";
13078       return AST_TEST_NOT_RUN;
13079    case TEST_EXECUTE:
13080       break;
13081    }
13082 
13083    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
13084    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
13085 
13086    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
13087       !(vmu = find_or_create(testcontext, testmailbox))) {
13088       ast_test_status_update(test, "Cannot create vmu structure\n");
13089       return AST_TEST_NOT_RUN;
13090    }
13091 
13092    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
13093       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
13094       return AST_TEST_NOT_RUN;
13095    }
13096 
13097    populate_defaults(vmu);
13098    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
13099 #ifdef IMAP_STORAGE
13100    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
13101 #endif
13102 
13103    file = tmpfile();
13104    for (which = 0; which < ARRAY_LEN(test_items); which++) {
13105       /* Kill previous test, if any */
13106       rewind(file);
13107       if (ftruncate(fileno(file), 0)) {
13108          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
13109          res = AST_TEST_FAIL;
13110          break;
13111       }
13112 
13113       /* Make each change, in order, to the test mailbox */
13114       if (test_items[which].type == INT) {
13115          *((int *) test_items[which].location) = test_items[which].u.intval;
13116       } else if (test_items[which].type == FLAGVAL) {
13117          if (ast_test_flag(vmu, test_items[which].u.intval)) {
13118             ast_clear_flag(vmu, test_items[which].u.intval);
13119          } else {
13120             ast_set_flag(vmu, test_items[which].u.intval);
13121          }
13122       } else if (test_items[which].type == STATIC) {
13123          strcpy(test_items[which].location, test_items[which].u.strval);
13124       } else if (test_items[which].type == STRPTR) {
13125          test_items[which].location = test_items[which].u.strval;
13126       }
13127 
13128       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13129       rewind(file);
13130       while (fgets(buf, sizeof(buf), file)) {
13131          if (
13132 #ifdef IMAP_STORAGE
13133          buf[strlen(buf) - 2] != '\r'
13134 #else
13135          buf[strlen(buf) - 2] == '\r'
13136 #endif
13137          || buf[strlen(buf) - 1] != '\n') {
13138             res = AST_TEST_FAIL;
13139          }
13140       }
13141    }
13142    fclose(file);
13143    return res;
13144 }
13145 
13146 AST_TEST_DEFINE(test_voicemail_load_config)
13147 {
13148    int res = AST_TEST_PASS;
13149    struct ast_vm_user *vmu;
13150    struct ast_config *cfg;
13151    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13152    int fd;
13153    FILE *file;
13154    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13155 
13156    switch (cmd) {
13157    case TEST_INIT:
13158       info->name = "test_voicemail_load_config";
13159       info->category = "/apps/app_voicemail/";
13160       info->summary = "Test loading Voicemail config";
13161       info->description =
13162          "Verify that configuration is loaded consistently. "
13163          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13164          "some options were loaded after the mailboxes were instantiated, causing "
13165          "those options not to be set correctly.";
13166       return AST_TEST_NOT_RUN;
13167    case TEST_EXECUTE:
13168       break;
13169    }
13170 
13171    /* build a config file by hand... */
13172    if ((fd = mkstemp(config_filename)) < 0) {
13173       return AST_TEST_FAIL;
13174    }
13175    if (!(file = fdopen(fd, "w"))) {
13176       close(fd);
13177       unlink(config_filename);
13178       return AST_TEST_FAIL;
13179    }
13180    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13181    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13182    fputs("00000002 => 9999,Mrs. Test\n", file);
13183    fclose(file);
13184 
13185    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13186       res = AST_TEST_FAIL;
13187       goto cleanup;
13188    }
13189 
13190    load_config_from_memory(1, cfg, NULL);
13191    ast_config_destroy(cfg);
13192 
13193 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13194    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13195    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13196 
13197    AST_LIST_LOCK(&users);
13198    AST_LIST_TRAVERSE(&users, vmu, list) {
13199       if (!strcmp(vmu->mailbox, "00000001")) {
13200          if (0); /* trick to get CHECK to work */
13201          CHECK(vmu, callback, "othercontext")
13202          CHECK(vmu, locale, "nl_NL.UTF-8")
13203          CHECK(vmu, zonetag, "central")
13204       } else if (!strcmp(vmu->mailbox, "00000002")) {
13205          if (0); /* trick to get CHECK to work */
13206          CHECK(vmu, callback, "somecontext")
13207          CHECK(vmu, locale, "de_DE.UTF-8")
13208          CHECK(vmu, zonetag, "european")
13209       }
13210    }
13211    AST_LIST_UNLOCK(&users);
13212 
13213 #undef CHECK
13214 
13215    /* restore config */
13216    load_config(1); /* this might say "Failed to load configuration file." */
13217 
13218 cleanup:
13219    unlink(config_filename);
13220    return res;
13221 }
13222 
13223 AST_TEST_DEFINE(test_voicemail_vm_info)
13224 {
13225    struct ast_vm_user *vmu;
13226    struct ast_channel *chan = NULL;
13227    const char testcontext[] = "test";
13228    const char testmailbox[] = "00000000";
13229    const char vminfo_cmd[] = "VM_INFO";
13230    char vminfo_buf[256], vminfo_args[256];
13231    int res = AST_TEST_PASS;
13232    int test_ret = 0;
13233    int test_counter = 0;
13234 
13235    struct {
13236       char *vminfo_test_args;
13237       char *vminfo_expected;
13238       int vminfo_ret;
13239    } test_items[] = {
13240       { "", "", -1 },            /* Missing argument */
13241       { "00000000@test,badparam", "", -1 },  /* Wrong argument */
13242       { "00000000@test", "", -1 },     /* Missing argument */
13243       { "00000000@test,exists", "1", 0 },
13244       { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
13245       { "00000000@test,email", "vm-info-test@example.net", 0 },
13246       { "11111111@test,email", "", 0 },   /* Invalid mailbox */
13247       { "00000000@test,fullname", "Test Framework Mailbox", 0 },
13248       { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
13249       { "00000000@test,locale", "en_US", 0 },
13250       { "00000000@test,tz", "central", 0 },
13251       { "00000000@test,language", "en", 0 },
13252       { "00000000@test,password", "9876", 0 },
13253    };
13254 
13255    switch (cmd) {
13256       case TEST_INIT:
13257          info->name = "test_voicemail_vm_info";
13258          info->category = "/apps/app_voicemail/";
13259          info->summary = "VM_INFO unit test";
13260          info->description =
13261             "This tests passing various parameters to VM_INFO";
13262          return AST_TEST_NOT_RUN;
13263       case TEST_EXECUTE:
13264          break;
13265    }
13266 
13267    if (!(chan = ast_dummy_channel_alloc())) {
13268       ast_test_status_update(test, "Unable to create dummy channel\n");
13269       return AST_TEST_FAIL;
13270    }
13271 
13272    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
13273          !(vmu = find_or_create(testcontext, testmailbox))) {
13274       ast_test_status_update(test, "Cannot create vmu structure\n");
13275       chan = ast_channel_unref(chan);
13276       return AST_TEST_FAIL;
13277    }
13278 
13279    populate_defaults(vmu);
13280 
13281    ast_copy_string(vmu->email, "vm-info-test@example.net", sizeof(vmu->email));
13282    ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
13283    ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
13284    ast_copy_string(vmu->language, "en", sizeof(vmu->language));
13285    ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
13286    ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
13287    ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
13288 
13289    for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
13290       ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
13291       test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
13292       if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
13293          ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
13294          res = AST_TEST_FAIL;
13295       }
13296       if (!(test_ret == test_items[test_counter].vminfo_ret)) {
13297          ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
13298          res = AST_TEST_FAIL;
13299       }
13300    }
13301 
13302    chan = ast_channel_unref(chan);
13303    return res;
13304 }
13305 #endif /* defined(TEST_FRAMEWORK) */
13306 
13307 static int reload(void)
13308 {
13309    return load_config(1);
13310 }
13311 
13312 static int unload_module(void)
13313 {
13314    int res;
13315 
13316    res = ast_unregister_application(app);
13317    res |= ast_unregister_application(app2);
13318    res |= ast_unregister_application(app3);
13319    res |= ast_unregister_application(app4);
13320    res |= ast_unregister_application(sayname_app);
13321    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13322    res |= ast_custom_function_unregister(&vm_info_acf);
13323    res |= ast_manager_unregister("VoicemailUsersList");
13324    res |= ast_data_unregister(NULL);
13325 #ifdef TEST_FRAMEWORK
13326    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13327    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13328    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13329    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13330    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13331    res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
13332 #endif
13333    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13334    ast_uninstall_vm_functions();
13335    ao2_ref(inprocess_container, -1);
13336 
13337    if (poll_thread != AST_PTHREADT_NULL)
13338       stop_poll_thread();
13339 
13340    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13341    ast_unload_realtime("voicemail");
13342    ast_unload_realtime("voicemail_data");
13343 
13344    free_vm_users();
13345    free_vm_zones();
13346    return res;
13347 }
13348 
13349 static int load_module(void)
13350 {
13351    int res;
13352    my_umask = umask(0);
13353    umask(my_umask);
13354 
13355    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13356       return AST_MODULE_LOAD_DECLINE;
13357    }
13358 
13359    /* compute the location of the voicemail spool directory */
13360    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13361    
13362    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13363       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13364    }
13365 
13366    if ((res = load_config(0)))
13367       return res;
13368 
13369    res = ast_register_application_xml(app, vm_exec);
13370    res |= ast_register_application_xml(app2, vm_execmain);
13371    res |= ast_register_application_xml(app3, vm_box_exists);
13372    res |= ast_register_application_xml(app4, vmauthenticate);
13373    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13374    res |= ast_custom_function_register(&mailbox_exists_acf);
13375    res |= ast_custom_function_register(&vm_info_acf);
13376    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13377 #ifdef TEST_FRAMEWORK
13378    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13379    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13380    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13381    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13382    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13383    res |= AST_TEST_REGISTER(test_voicemail_vm_info);
13384 #endif
13385 
13386    if (res)
13387       return res;
13388 
13389    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13390    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13391 
13392    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13393    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13394    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13395 
13396    return res;
13397 }
13398 
13399 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13400 {
13401    int cmd = 0;
13402    char destination[80] = "";
13403    int retries = 0;
13404 
13405    if (!num) {
13406       ast_verb(3, "Destination number will be entered manually\n");
13407       while (retries < 3 && cmd != 't') {
13408          destination[1] = '\0';
13409          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13410          if (!cmd)
13411             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13412          if (!cmd)
13413             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13414          if (!cmd) {
13415             cmd = ast_waitfordigit(chan, 6000);
13416             if (cmd)
13417                destination[0] = cmd;
13418          }
13419          if (!cmd) {
13420             retries++;
13421          } else {
13422 
13423             if (cmd < 0)
13424                return 0;
13425             if (cmd == '*') {
13426                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13427                return 0;
13428             }
13429             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13430                retries++;
13431             else
13432                cmd = 't';
13433          }
13434          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13435       }
13436       if (retries >= 3) {
13437          return 0;
13438       }
13439       
13440    } else {
13441       ast_verb(3, "Destination number is CID number '%s'\n", num);
13442       ast_copy_string(destination, num, sizeof(destination));
13443    }
13444 
13445    if (!ast_strlen_zero(destination)) {
13446       if (destination[strlen(destination) -1 ] == '*')
13447          return 0; 
13448       ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13449       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13450       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13451       chan->priority = 0;
13452       return 9;
13453    }
13454    return 0;
13455 }
13456 
13457 /*!
13458  * \brief The advanced options within a message.
13459  * \param chan
13460  * \param vmu 
13461  * \param vms
13462  * \param msg
13463  * \param option
13464  * \param record_gain
13465  *
13466  * Provides handling for the play message envelope, call the person back, or reply to message. 
13467  *
13468  * \return zero on success, -1 on error.
13469  */
13470 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)
13471 {
13472    int res = 0;
13473    char filename[PATH_MAX];
13474    struct ast_config *msg_cfg = NULL;
13475    const char *origtime, *context;
13476    char *name, *num;
13477    int retries = 0;
13478    char *cid;
13479    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13480 
13481    vms->starting = 0; 
13482 
13483    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13484 
13485    /* Retrieve info from VM attribute file */
13486    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13487    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13488    msg_cfg = ast_config_load(filename, config_flags);
13489    DISPOSE(vms->curdir, vms->curmsg);
13490    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13491       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13492       return 0;
13493    }
13494 
13495    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13496       ast_config_destroy(msg_cfg);
13497       return 0;
13498    }
13499 
13500    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13501 
13502    context = ast_variable_retrieve(msg_cfg, "message", "context");
13503    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13504       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13505    switch (option) {
13506    case 3: /* Play message envelope */
13507       if (!res) {
13508          res = play_message_datetime(chan, vmu, origtime, filename);
13509       }
13510       if (!res) {
13511          res = play_message_callerid(chan, vms, cid, context, 0, 1);
13512       }
13513 
13514       res = 't';
13515       break;
13516 
13517    case 2:  /* Call back */
13518 
13519       if (ast_strlen_zero(cid))
13520          break;
13521 
13522       ast_callerid_parse(cid, &name, &num);
13523       while ((res > -1) && (res != 't')) {
13524          switch (res) {
13525          case '1':
13526             if (num) {
13527                /* Dial the CID number */
13528                res = dialout(chan, vmu, num, vmu->callback);
13529                if (res) {
13530                   ast_config_destroy(msg_cfg);
13531                   return 9;
13532                }
13533             } else {
13534                res = '2';
13535             }
13536             break;
13537 
13538          case '2':
13539             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13540             if (!ast_strlen_zero(vmu->dialout)) {
13541                res = dialout(chan, vmu, NULL, vmu->dialout);
13542                if (res) {
13543                   ast_config_destroy(msg_cfg);
13544                   return 9;
13545                }
13546             } else {
13547                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13548                res = ast_play_and_wait(chan, "vm-sorry");
13549             }
13550             ast_config_destroy(msg_cfg);
13551             return res;
13552          case '*':
13553             res = 't';
13554             break;
13555          case '3':
13556          case '4':
13557          case '5':
13558          case '6':
13559          case '7':
13560          case '8':
13561          case '9':
13562          case '0':
13563 
13564             res = ast_play_and_wait(chan, "vm-sorry");
13565             retries++;
13566             break;
13567          default:
13568             if (num) {
13569                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13570                res = ast_play_and_wait(chan, "vm-num-i-have");
13571                if (!res)
13572                   res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
13573                if (!res)
13574                   res = ast_play_and_wait(chan, "vm-tocallnum");
13575                /* Only prompt for a caller-specified number if there is a dialout context specified */
13576                if (!ast_strlen_zero(vmu->dialout)) {
13577                   if (!res)
13578                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13579                }
13580             } else {
13581                res = ast_play_and_wait(chan, "vm-nonumber");
13582                if (!ast_strlen_zero(vmu->dialout)) {
13583                   if (!res)
13584                      res = ast_play_and_wait(chan, "vm-toenternumber");
13585                }
13586             }
13587             if (!res) {
13588                res = ast_play_and_wait(chan, "vm-star-cancel");
13589             }
13590             if (!res) {
13591                res = ast_waitfordigit(chan, 6000);
13592             }
13593             if (!res) {
13594                retries++;
13595                if (retries > 3) {
13596                   res = 't';
13597                }
13598             }
13599             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13600             break; 
13601             
13602          }
13603          if (res == 't')
13604             res = 0;
13605          else if (res == '*')
13606             res = -1;
13607       }
13608       break;
13609       
13610    case 1:  /* Reply */
13611       /* Send reply directly to sender */
13612       if (ast_strlen_zero(cid))
13613          break;
13614 
13615       ast_callerid_parse(cid, &name, &num);
13616       if (!num) {
13617          ast_verb(3, "No CID number available, no reply sent\n");
13618          if (!res)
13619             res = ast_play_and_wait(chan, "vm-nonumber");
13620          ast_config_destroy(msg_cfg);
13621          return res;
13622       } else {
13623          struct ast_vm_user vmu2;
13624          if (find_user(&vmu2, vmu->context, num)) {
13625             struct leave_vm_options leave_options;
13626             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13627             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13628 
13629             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13630             
13631             memset(&leave_options, 0, sizeof(leave_options));
13632             leave_options.record_gain = record_gain;
13633             res = leave_voicemail(chan, mailbox, &leave_options);
13634             if (!res)
13635                res = 't';
13636             ast_config_destroy(msg_cfg);
13637             return res;
13638          } else {
13639             /* Sender has no mailbox, can't reply */
13640             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13641             ast_play_and_wait(chan, "vm-nobox");
13642             res = 't';
13643             ast_config_destroy(msg_cfg);
13644             return res;
13645          }
13646       } 
13647       res = 0;
13648 
13649       break;
13650    }
13651 
13652 #ifndef IMAP_STORAGE
13653    ast_config_destroy(msg_cfg);
13654 
13655    if (!res) {
13656       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13657       vms->heard[msg] = 1;
13658       res = wait_file(chan, vms, vms->fn);
13659    }
13660 #endif
13661    return res;
13662 }
13663 
13664 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13665          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13666          signed char record_gain, struct vm_state *vms, char *flag)
13667 {
13668    /* Record message & let caller review or re-record it, or set options if applicable */
13669    int res = 0;
13670    int cmd = 0;
13671    int max_attempts = 3;
13672    int attempts = 0;
13673    int recorded = 0;
13674    int msg_exists = 0;
13675    signed char zero_gain = 0;
13676    char tempfile[PATH_MAX];
13677    char *acceptdtmf = "#";
13678    char *canceldtmf = "";
13679    int canceleddtmf = 0;
13680 
13681    /* Note that urgent and private are for flagging messages as such in the future */
13682 
13683    /* barf if no pointer passed to store duration in */
13684    if (duration == NULL) {
13685       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13686       return -1;
13687    }
13688 
13689    if (!outsidecaller)
13690       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13691    else
13692       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13693 
13694    cmd = '3';  /* Want to start by recording */
13695 
13696    while ((cmd >= 0) && (cmd != 't')) {
13697       switch (cmd) {
13698       case '1':
13699          if (!msg_exists) {
13700             /* In this case, 1 is to record a message */
13701             cmd = '3';
13702             break;
13703          } else {
13704             /* Otherwise 1 is to save the existing message */
13705             ast_verb(3, "Saving message as is\n");
13706             if (!outsidecaller) 
13707                ast_filerename(tempfile, recordfile, NULL);
13708             ast_stream_and_wait(chan, "vm-msgsaved", "");
13709             if (!outsidecaller) {
13710                /* Saves to IMAP server only if imapgreeting=yes */
13711                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13712                DISPOSE(recordfile, -1);
13713             }
13714             cmd = 't';
13715             return res;
13716          }
13717       case '2':
13718          /* Review */
13719          ast_verb(3, "Reviewing the message\n");
13720          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13721          break;
13722       case '3':
13723          msg_exists = 0;
13724          /* Record */
13725          if (recorded == 1) 
13726             ast_verb(3, "Re-recording the message\n");
13727          else  
13728             ast_verb(3, "Recording the message\n");
13729          
13730          if (recorded && outsidecaller) {
13731             cmd = ast_play_and_wait(chan, INTRO);
13732             cmd = ast_play_and_wait(chan, "beep");
13733          }
13734          recorded = 1;
13735          /* 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 */
13736          if (record_gain)
13737             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13738          if (ast_test_flag(vmu, VM_OPERATOR))
13739             canceldtmf = "0";
13740          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13741          if (strchr(canceldtmf, cmd)) {
13742          /* need this flag here to distinguish between pressing '0' during message recording or after */
13743             canceleddtmf = 1;
13744          }
13745          if (record_gain)
13746             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13747          if (cmd == -1) {
13748             /* User has hung up, no options to give */
13749             if (!outsidecaller) {
13750                /* user was recording a greeting and they hung up, so let's delete the recording. */
13751                ast_filedelete(tempfile, NULL);
13752             }     
13753             return cmd;
13754          }
13755          if (cmd == '0') {
13756             break;
13757          } else if (cmd == '*') {
13758             break;
13759 #if 0
13760          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13761             /* Message is too short */
13762             ast_verb(3, "Message too short\n");
13763             cmd = ast_play_and_wait(chan, "vm-tooshort");
13764             cmd = ast_filedelete(tempfile, NULL);
13765             break;
13766          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13767             /* Message is all silence */
13768             ast_verb(3, "Nothing recorded\n");
13769             cmd = ast_filedelete(tempfile, NULL);
13770             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13771             if (!cmd)
13772                cmd = ast_play_and_wait(chan, "vm-speakup");
13773             break;
13774 #endif
13775          } else {
13776             /* If all is well, a message exists */
13777             msg_exists = 1;
13778             cmd = 0;
13779          }
13780          break;
13781       case '4':
13782          if (outsidecaller) {  /* only mark vm messages */
13783             /* Mark Urgent */
13784             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13785                ast_verb(3, "marking message as Urgent\n");
13786                res = ast_play_and_wait(chan, "vm-marked-urgent");
13787                strcpy(flag, "Urgent");
13788             } else if (flag) {
13789                ast_verb(3, "UNmarking message as Urgent\n");
13790                res = ast_play_and_wait(chan, "vm-urgent-removed");
13791                strcpy(flag, "");
13792             } else {
13793                ast_play_and_wait(chan, "vm-sorry");
13794             }
13795             cmd = 0;
13796          } else {
13797             cmd = ast_play_and_wait(chan, "vm-sorry");
13798          }
13799          break;
13800       case '5':
13801       case '6':
13802       case '7':
13803       case '8':
13804       case '9':
13805       case '*':
13806       case '#':
13807          cmd = ast_play_and_wait(chan, "vm-sorry");
13808          break;
13809 #if 0 
13810 /*  XXX Commented out for the moment because of the dangers of deleting
13811     a message while recording (can put the message numbers out of sync) */
13812       case '*':
13813          /* Cancel recording, delete message, offer to take another message*/
13814          cmd = ast_play_and_wait(chan, "vm-deleted");
13815          cmd = ast_filedelete(tempfile, NULL);
13816          if (outsidecaller) {
13817             res = vm_exec(chan, NULL);
13818             return res;
13819          }
13820          else
13821             return 1;
13822 #endif
13823       case '0':
13824          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13825             cmd = ast_play_and_wait(chan, "vm-sorry");
13826             break;
13827          }
13828          if (msg_exists || recorded) {
13829             cmd = ast_play_and_wait(chan, "vm-saveoper");
13830             if (!cmd)
13831                cmd = ast_waitfordigit(chan, 3000);
13832             if (cmd == '1') {
13833                ast_filerename(tempfile, recordfile, NULL);
13834                ast_play_and_wait(chan, "vm-msgsaved");
13835                cmd = '0';
13836             } else if (cmd == '4') {
13837                if (flag) {
13838                   ast_play_and_wait(chan, "vm-marked-urgent");
13839                   strcpy(flag, "Urgent");
13840                }
13841                ast_play_and_wait(chan, "vm-msgsaved");
13842                cmd = '0';
13843             } else {
13844                ast_play_and_wait(chan, "vm-deleted");
13845                DELETE(tempfile, -1, tempfile, vmu);
13846                cmd = '0';
13847             }
13848          }
13849          return cmd;
13850       default:
13851          /* If the caller is an ouside caller, and the review option is enabled,
13852             allow them to review the message, but let the owner of the box review
13853             their OGM's */
13854          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13855             return cmd;
13856          if (msg_exists) {
13857             cmd = ast_play_and_wait(chan, "vm-review");
13858             if (!cmd && outsidecaller) {
13859                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13860                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13861                } else if (flag) {
13862                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13863                }
13864             }
13865          } else {
13866             cmd = ast_play_and_wait(chan, "vm-torerecord");
13867             if (!cmd)
13868                cmd = ast_waitfordigit(chan, 600);
13869          }
13870          
13871          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13872             cmd = ast_play_and_wait(chan, "vm-reachoper");
13873             if (!cmd)
13874                cmd = ast_waitfordigit(chan, 600);
13875          }
13876 #if 0
13877          if (!cmd)
13878             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13879 #endif
13880          if (!cmd)
13881             cmd = ast_waitfordigit(chan, 6000);
13882          if (!cmd) {
13883             attempts++;
13884          }
13885          if (attempts > max_attempts) {
13886             cmd = 't';
13887          }
13888       }
13889    }
13890    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13891       /* Hang up or timeout, so delete the recording. */
13892       ast_filedelete(tempfile, NULL);
13893    }
13894 
13895    if (cmd != 't' && outsidecaller)
13896       ast_play_and_wait(chan, "vm-goodbye");
13897 
13898    return cmd;
13899 }
13900 
13901 /* This is a workaround so that menuselect displays a proper description
13902  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13903  */
13904 
13905 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13906       .load = load_module,
13907       .unload = unload_module,
13908       .reload = reload,
13909       .nonoptreq = "res_adsi,res_smdi",
13910       );

Generated on Sat Feb 11 06:33:04 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6